http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m deleted file mode 100644 index d73531c..0000000 --- a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m +++ /dev/null @@ -1,975 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "WXListComponent.h" -#import "WXCellComponent.h" -#import "WXHeaderComponent.h" -#import "WXComponent.h" -#import "WXComponent_internal.h" -#import "NSArray+Weex.h" -#import "WXAssert.h" -#import "WXMonitor.h" -#import "WXUtility.h" -#import "NSObject+WXSwizzle.h" -#import "WXSDKInstance_private.h" -#import "WXRefreshComponent.h" -#import "WXLoadingComponent.h" - -@interface WXTableView : UITableView - -@end - -@implementation WXTableView - -+ (BOOL)requiresConstraintBasedLayout -{ - return NO; -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch -{ - if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) { - return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch]; - } - else{ - return YES; - } -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - [self.wx_component layoutDidFinish]; -} - -- (void)setContentOffset:(CGPoint)contentOffset -{ - // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary. - // When UITableView is pulled down and finger releasesï¼contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary. - // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator. - // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f - // Have to reset contentOffset to zero manually here. - if (fabs(contentOffset.y) < 0.5) { - contentOffset.y = 0; - } - if (isnan(contentOffset.x)) { - contentOffset.x = 0; - } - if(isnan(contentOffset.y)) { - contentOffset.y = 0; - } - - [super setContentOffset:contentOffset]; -} - -@end - -// WXText is a non-public is not permitted -@interface WXSectionComponent : NSObject<NSCopying> - -@property (nonatomic, strong) WXHeaderComponent *header; -@property (nonatomic, strong) NSMutableArray<WXCellComponent *> *rows; - -@end - -@implementation WXSectionComponent - -- (instancetype)init -{ - if (self = [super init]) { - _rows = [NSMutableArray array]; - } - - return self; -} - -- (id)copyWithZone:(NSZone *)zone -{ - WXSectionComponent *newSection = [[[self class] allocWithZone:zone] init]; - newSection.header = _header; - newSection.rows = [_rows mutableCopyWithZone:zone]; - - return newSection; -} - -- (NSString *)description -{ - return [NSString stringWithFormat:@"%@\n%@", [_header description], [_rows description]]; -} -@end - -@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate> - -@property (nonatomic, assign) NSUInteger currentTopVisibleSection; - -@end - -@implementation WXListComponent -{ - __weak UITableView * _tableView; - - // Only accessed on component thread - NSMutableArray<WXSectionComponent *> *_sections; - // Only accessed on main thread - NSMutableArray<WXSectionComponent *> *_completedSections; - NSUInteger _previousLoadMoreRowNumber; - // insert & reload & batch - NSString *_updataType; - - BOOL _isUpdating; - NSMutableArray<void(^)(void)> *_updates; - NSTimeInterval _reloadInterval; -} - -- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance -{ - if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) { - _sections = [NSMutableArray array]; - _completedSections = [NSMutableArray array]; - _reloadInterval = attributes[@"reloadInterval"] ? [WXConvert CGFloat:attributes[@"reloadInterval"]]/1000 : 0; - _updataType = [WXConvert NSString:attributes[@"updataType"]]?:@"insert"; - [self fixFlicker]; - } - - return self; -} - -- (void)dealloc -{ - if (_tableView) { - _tableView.delegate = nil; - _tableView.dataSource = nil; - } -} - -- (UIView *)loadView -{ - return [[WXTableView alloc] init]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - _tableView = (UITableView *)self.view; - _tableView.allowsSelection = NO; - _tableView.allowsMultipleSelection = NO; - _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - _tableView.delegate = self; - _tableView.dataSource = self; - _tableView.userInteractionEnabled = YES; - - _tableView.estimatedRowHeight = 0; - _tableView.estimatedSectionFooterHeight = 0; - _tableView.estimatedSectionHeaderHeight = 0; -} - -- (void)viewWillUnload -{ - [super viewWillUnload]; - - _tableView.delegate = nil; - _tableView.dataSource = nil; -} - -- (void)updateAttributes:(NSDictionary *)attributes -{ - [super updateAttributes:attributes]; - - if (attributes[@"reloadInterval"]) { - _reloadInterval = [WXConvert CGFloat:attributes[@"reloadInterval"]] / 1000; - } - if (attributes[@"updataType"]) { - _updataType = [WXConvert NSString:attributes[@"updataType"]]; - } -} - -- (void)setContentSize:(CGSize)contentSize -{ - // Do Nothing -} - -- (void)_handleFirstScreenTime -{ - // Do Nothingï¼ firstScreenTime is set by cellDidRendered: -} - -- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated -{ - CGPoint contentOffset = _tableView.contentOffset; - CGFloat contentOffsetY = 0; - - WXComponent *cellComponent = component; - CGRect cellRect; - while (cellComponent) { - if ([cellComponent isKindOfClass:[WXCellComponent class]]) { - NSIndexPath *toIndexPath = [self indexPathForCell:(WXCellComponent*)cellComponent sections:_completedSections]; - cellRect = [_tableView rectForRowAtIndexPath:toIndexPath]; - break; - } - if ([cellComponent isKindOfClass:[WXHeaderComponent class]]) { - NSUInteger toIndex = [self indexForHeader:(WXHeaderComponent *)cellComponent sections:_completedSections]; - cellRect = [_tableView rectForSection:toIndex]; - break; - } - contentOffsetY += cellComponent.calculatedFrame.origin.y; - cellComponent = cellComponent.supercomponent; - } - - contentOffsetY += cellRect.origin.y; - contentOffsetY += offset * self.weexInstance.pixelScaleFactor; - - if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) { - contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height; - } else { - contentOffset.y = contentOffsetY; - } - - [_tableView setContentOffset:contentOffset animated:animated]; -} - - -#pragma mark - Inheritance - -- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index -{ - if ([subcomponent isKindOfClass:[WXCellComponent class]]) { - ((WXCellComponent *)subcomponent).delegate = self; - } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) { - ((WXHeaderComponent *)subcomponent).delegate = self; - } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]] - && ![subcomponent isKindOfClass:[WXLoadingComponent class]] - && subcomponent->_positionType != WXPositionTypeFixed) { - WXLogError(@"list only support cell/header/refresh/loading/fixed-component as child."); - return; - } - - [super _insertSubcomponent:subcomponent atIndex:index]; - - if (![subcomponent isKindOfClass:[WXHeaderComponent class]] - && ![subcomponent isKindOfClass:[WXCellComponent class]]) { - // Don't insert section if subcomponent is not header or cell - return; - } - - NSIndexPath *indexPath = [self indexPathForSubIndex:index]; - - if ([subcomponent isKindOfClass:[WXHeaderComponent class]] || _sections.count <= indexPath.section) { - // conditions to insert section: insert a header or insert first cell of table view - // this will be updated by recycler's update controller in the future - WXSectionComponent *insertSection = [WXSectionComponent new]; - BOOL keepScrollPosition = NO; - if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) { - WXHeaderComponent *header = (WXHeaderComponent*)subcomponent; - insertSection.header = header; - keepScrollPosition = header.keepScrollPosition; - } - - NSUInteger insertIndex = indexPath.section; - WXSectionComponent *reloadSection; - if (insertIndex > 0 && insertIndex <= _sections.count - && [subcomponent isKindOfClass:[WXHeaderComponent class]]) { - // insert a header in the middle, one section may divide into two - // so the original section need to be reloaded - NSIndexPath *indexPathBeforeHeader = [self indexPathForSubIndex:index - 1]; - if (_sections[insertIndex - 1].rows.count != 0 && indexPathBeforeHeader.row < _sections[insertIndex - 1].rows.count - 1) { - reloadSection = _sections[insertIndex - 1]; - NSArray *rowsToSeparate = reloadSection.rows; - insertSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(indexPathBeforeHeader.row + 1, rowsToSeparate.count - indexPathBeforeHeader.row - 1)] mutableCopy]; - reloadSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(0, indexPathBeforeHeader.row + 1)] mutableCopy]; - } - } - - [_sections insertObject:insertSection atIndex:insertIndex]; - WXSectionComponent *completedInsertSection = [insertSection copy]; - WXSectionComponent *completedReloadSection; - if (reloadSection) { - completedReloadSection = [reloadSection copy]; - } - - [self.weexInstance.componentManager _addUITask:^{ - WXLogDebug(@"Insert section:%ld", (unsigned long)insertIndex); - - [UIView performWithoutAnimation:^{ - - @try { - [_tableView beginUpdates]; - - [_completedSections insertObject:completedInsertSection atIndex:insertIndex]; - if (completedReloadSection) { - WXLogDebug(@"Reload section:%lu", (unsigned long)(insertIndex - 1)); - _completedSections[insertIndex - 1] = completedReloadSection; - } - - [self _insertTableViewSectionAtIndex:insertIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone]; - - if (completedReloadSection) { - [_tableView reloadSections:[NSIndexSet indexSetWithIndex:insertIndex - 1] withRowAnimation:UITableViewRowAnimationNone]; - } - - [_tableView endUpdates]; - } @catch (NSException *exception) { - WXLogError(@"list insert component occurs exception %@", exception); - } @finally { - // nothing - } - - }]; - - }]; - - } -} - -- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index -{ - //Here will not insert cell or header's view again - if (![subcomponent isKindOfClass:[WXCellComponent class]] - && ![subcomponent isKindOfClass:[WXHeaderComponent class]]) { - [super insertSubview:subcomponent atIndex:index]; - } -} - -#pragma mark - WXHeaderRenderDelegate - -- (float)headerWidthForLayout:(WXHeaderComponent *)cell -{ - return self.scrollerCSSNode->style.dimensions[CSS_WIDTH]; -} - -- (void)headerDidLayout:(WXHeaderComponent *)header -{ - [self.weexInstance.componentManager _addUITask:^{ - // trigger section header update - [UIView performWithoutAnimation:^{ - [_tableView beginUpdates]; - - NSUInteger reloadIndex = [self indexForHeader:header sections:_completedSections]; - [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone]; - - [_tableView endUpdates]; - }]; - }]; -} - -- (void)headerDidRemove:(WXHeaderComponent *)header -{ - NSUInteger headerIndex = [self indexForHeader:header sections:_sections]; - // this will be updated by recycler's update controller in the future - WXSectionComponent *headerSection = _sections[headerIndex]; - WXSectionComponent *reloadSection; - NSUInteger reloadIndex = -1; - BOOL isDeleteSection = NO; - if (headerIndex == 0 && headerSection.rows.count > 0) { - // delete a header in the first section and the section still has cells - // reload the first section - reloadIndex = 0; - reloadSection = _sections[reloadIndex]; - _sections[reloadIndex].header = nil; - } else if (headerIndex > 0 && headerSection.rows.count > 0) { - // delete a header in the middle, two sections merge into one - // so one section need to be deleted and the other should be relo - isDeleteSection = YES; - reloadIndex = headerIndex - 1; - reloadSection = _sections[reloadIndex]; - reloadSection.rows = [[reloadSection.rows arrayByAddingObjectsFromArray:headerSection.rows] mutableCopy]; - [_sections removeObjectAtIndex:headerIndex]; - } else { - // delete a header with no cell in that section - // just delete the section - isDeleteSection = YES; - [_sections removeObjectAtIndex:headerIndex]; - } - - WXSectionComponent *completedReloadSection; - if (reloadSection) { - completedReloadSection = [reloadSection copy]; - } - BOOL keepScrollPosition = header.keepScrollPosition; - - [self.weexInstance.componentManager _addUITask:^{ - if (isDeleteSection) { - WXLogDebug(@"delete section:%lu", (unsigned long)headerIndex); - [_completedSections removeObjectAtIndex:headerIndex]; - } - - if (reloadIndex == 0 && !isDeleteSection) { - _completedSections[reloadIndex].header = nil; - } - - if (completedReloadSection) { - WXLogDebug(@"Reload section:%lu", (unsigned long)reloadIndex); - _completedSections[reloadIndex] = completedReloadSection; - } - - [UIView performWithoutAnimation:^{ - [_tableView beginUpdates]; - if (isDeleteSection) { - [self _deleteTableViewSectionAtIndex:headerIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone]; - } - - if (completedReloadSection) { - [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone]; - } - - [_tableView endUpdates]; - }]; - - }]; -} - -#pragma mark - WXCellRenderDelegate - -- (float)containerWidthForLayout:(WXCellComponent *)cell -{ - return self.scrollerCSSNode->style.dimensions[CSS_WIDTH]; -} - -- (void)cellDidRemove:(WXCellComponent *)cell -{ - WXAssertComponentThread(); - - NSIndexPath *indexPath = [self indexPathForCell:cell sections:_sections]; - [self removeCellForIndexPath:indexPath withSections:_sections]; - - [self.weexInstance.componentManager _addUITask:^{ - [self removeCellForIndexPath:indexPath withSections:_completedSections]; - - WXLogDebug(@"Delete cell:%@ at indexPath:%@", cell.ref, indexPath); - if (cell.deleteAnimation == UITableViewRowAnimationNone) { - [UIView performWithoutAnimation:^{ - [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone]; - }]; - } else { - [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.deleteAnimation]; - } - }]; -} - -- (void)cellDidLayout:(WXCellComponent *)cell -{ - WXAssertComponentThread() ; - - NSUInteger index = [self.subcomponents indexOfObject:cell]; - NSIndexPath *indexPath = [self indexPathForSubIndex:index]; - - NSInteger sectionNum = indexPath.section; - if (sectionNum >= [_sections count] || sectionNum < 0) { - // try to protect sectionNum out of range. - return; - } - NSInteger row = indexPath.row; - NSMutableArray *sections = _sections; - WXSectionComponent *section = sections[sectionNum]; - WXAssert(section, @"no section found for section number:%ld", sectionNum); - NSMutableArray *completedSections; - BOOL isReload = [section.rows containsObject:cell]; - if (!isReload && row > [section.rows count]) { - // protect crash when row out of bounds - return ; - } - if (!isReload) { - [section.rows insertObject:cell atIndex:row]; - // deep copy - completedSections = [[NSMutableArray alloc] initWithArray:sections copyItems:YES];; - } - - [self.weexInstance.componentManager _addUITask:^{ - if (!isReload) { - WXLogDebug(@"Insert cell:%@ at indexPath:%@", cell.ref, indexPath); - _completedSections = completedSections; - if (cell.insertAnimation == UITableViewRowAnimationNone) { - [UIView performWithoutAnimation:^{ - [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone]; - }]; - } else { - [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.insertAnimation]; - } - } else { - WXLogInfo(@"Reload cell:%@ at indexPath:%@", cell.ref, indexPath); - [UIView performWithoutAnimation:^{ - [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; - [self handleAppear]; - }]; - } - }]; -} - -- (void)cellDidRendered:(WXCellComponent *)cell -{ - WXAssertMainThread(); - - if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) { - // improve performance - return; - } - - NSIndexPath *indexPath = [self indexPathForCell:cell sections:_completedSections]; - if (!indexPath || indexPath.section >= [_tableView numberOfSections] || - indexPath.row < 0 || indexPath.row >= [_tableView numberOfRowsInSection:indexPath.section]) { - WXLogWarning(@"Rendered cell:%@ out of range, sections:%@", cell, _completedSections); - return; - } - - CGRect cellRect = [_tableView rectForRowAtIndexPath:indexPath]; - if (cellRect.origin.y + cellRect.size.height >= _tableView.frame.size.height) { - WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance); - } - - if (self.weexInstance.onRenderProgress) { - CGRect renderRect = [_tableView convertRect:cellRect toView:self.weexInstance.rootView]; - self.weexInstance.onRenderProgress(renderRect); - } -} - -- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index -{ - WXAssertComponentThread(); - - NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections]; - NSIndexPath *toIndexPath = [self indexPathForSubIndex:index]; - if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) { - WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]); - return; - } - [self removeCellForIndexPath:fromIndexPath withSections:_sections]; - [self insertCell:cell forIndexPath:toIndexPath withSections:_sections]; - - [self.weexInstance.componentManager _addUITask:^{ - if (_reloadInterval > 0) { - // use [UITableView reloadData] to do batch updates, will move to recycler's update controller - __weak typeof(self) weakSelf = self; - if (!_updates) { - _updates = [NSMutableArray array]; - } - [_updates addObject:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf removeCellForIndexPath:fromIndexPath withSections:strongSelf->_completedSections]; - [strongSelf insertCell:cell forIndexPath:toIndexPath withSections:strongSelf->_completedSections]; - }]; - - [self checkReloadData]; - } else { - [self removeCellForIndexPath:fromIndexPath withSections:_completedSections]; - [self insertCell:cell forIndexPath:toIndexPath withSections:_completedSections]; - [UIView performWithoutAnimation:^{ - @try { - [_tableView beginUpdates]; - [_tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; - [self handleAppear]; - [_tableView endUpdates]; - }@catch(NSException * exception){ - WXLogDebug(@"move cell exception: %@", [exception description]); - }@finally { - // do nothing - } - }]; - } - }]; -} - -- (void)checkReloadData -{ - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reloadInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - if (_isUpdating || _updates.count == 0) { - return ; - } - - _isUpdating = YES; - NSArray *updates = [_updates copy]; - [_updates removeAllObjects]; - for (void(^update)(void) in updates) { - update(); - } - [_tableView reloadData]; - _isUpdating = NO; - - [self checkReloadData]; - }); -} - -- (void)addStickyComponent:(WXComponent *)sticky -{ - -} - -- (void)removeStickyComponent:(WXComponent *)sticky -{ - -} -#pragma mark - TableView delegate - -- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath -{ - -} - -- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath); - NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows]; - if (![visibleIndexPaths containsObject:indexPath]) { - if (cell.contentView.subviews.count > 0) { - UIView *wxCellView = [cell.contentView.subviews firstObject]; - // Must invoke synchronously otherwise it will remove the view just added. - WXCellComponent *cellComponent = (WXCellComponent *)wxCellView.wx_component; - if (cellComponent.isRecycle) { - [wxCellView.wx_component _unloadViewWithReusing:YES]; - } - } - } -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - WXCellComponent *cell = [self cellForIndexPath:indexPath]; - return cell.calculatedFrame.size.height; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section -{ - WXHeaderComponent *header = ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).header; - if (header) { - return header.calculatedFrame.size.height; - } else { - return 0.0; - } -} - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView -{ - [super scrollViewDidScroll:scrollView]; - if ([[_tableView indexPathsForVisibleRows] count] > 0) { - NSIndexPath *topCellPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0]; - if (self.currentTopVisibleSection != topCellPath.section) { - if (self.currentTopVisibleSection) { - WXSectionComponent *removeSection = [_sections wx_safeObjectAtIndex:self.currentTopVisibleSection]; - if (removeSection.header && [removeSection.header.events containsObject:@"unsticky"]) { - [removeSection.header fireEvent:@"unsticky" params:nil]; - } - } - self.currentTopVisibleSection = topCellPath.section; - WXSectionComponent *showSection = [_sections wx_safeObjectAtIndex:topCellPath.section]; - if (showSection.header && [showSection.header.events containsObject:@"sticky"]) { - [showSection.header fireEvent:@"sticky" params:nil]; - } - } - } -} - -- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section -{ - WXHeaderComponent *header = ((WXSectionComponent *)_completedSections[section]).header; - WXLogDebug(@"header view for section %ld:%@", (long)section, header.view); - return header.view; -} - -#pragma mark - TableView Data Source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return _completedSections.count; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).rows.count; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"Getting cell at indexPath:%@", indexPath); - static NSString *reuseIdentifier = @"WXTableViewCell"; - - UITableViewCell *cellView = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; - if (!cellView) { - cellView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; - cellView.backgroundColor = [UIColor clearColor]; - } - - WXCellComponent *cell = [self cellForIndexPath:indexPath]; - - if (cell.zIndex) { - cellView.layer.zPosition = [WXConvert CGFloat:cell.zIndex]; - } - - if (!cell) { - return cellView; - } - - if (cell.view.superview == cellView.contentView) { - return cellView; - } - - for (UIView *view in cellView.contentView.subviews) { - [view removeFromSuperview]; - } - - [cellView.contentView addSubview:cell.view]; - - [cellView setAccessibilityIdentifier:cell.view.accessibilityIdentifier]; - - WXLogDebug(@"Created cell:%@ view:%@ cellView:%@ at indexPath:%@", cell.ref, cell.view, cellView, indexPath); - return cellView; -} - -#pragma mark - Load More Event - -- (void)setLoadmoreretry:(NSUInteger)loadmoreretry -{ - if (loadmoreretry != self.loadmoreretry) { - _previousLoadMoreRowNumber = 0; - } - - [super setLoadmoreretry:loadmoreretry]; -} - -- (void)loadMore -{ - [super loadMore]; - - _previousLoadMoreRowNumber = [self totalNumberOfRows]; -} - -- (BOOL)isNeedLoadMore -{ - BOOL superNeedLoadMore = [super isNeedLoadMore]; - return superNeedLoadMore && _previousLoadMoreRowNumber != [self totalNumberOfRows]; -} - -- (NSUInteger)totalNumberOfRows -{ - NSUInteger rowNumber = 0; - NSUInteger sectionCount = [_tableView numberOfSections]; - for (int section = 0; section < sectionCount; section ++) { - rowNumber += [_tableView numberOfRowsInSection:section]; - } - - return rowNumber; -} - -- (void)resetLoadmore{ - [super resetLoadmore]; - _previousLoadMoreRowNumber=0; -} - -#pragma mark Private - -- (WXCellComponent *)cellForIndexPath:(NSIndexPath *)indexPath -{ - WXSectionComponent *section = [_completedSections wx_safeObjectAtIndex:indexPath.section]; - if (!section) { - WXLogError(@"No section found for num:%ld, completed sections:%ld", (long)indexPath.section, (unsigned long)_completedSections.count); - return nil; - } - - WXCellComponent *cell = [section.rows wx_safeObjectAtIndex:indexPath.row]; - if (!cell) { - WXLogError(@"No cell found for num:%ld, completed rows:%ld", (long)indexPath.row, (unsigned long)section.rows.count); - return nil; - } - - return cell; -} - -- (void)insertCell:(WXCellComponent *)cell forIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections -{ - WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section]; - if (indexPath.row > [section.rows count] || indexPath.row < 0) { - WXLogError(@"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections); - return; - } - WXAssert(section, @"inserting cell at indexPath:%@ section has not been inserted to list before, sections:%@", indexPath, sections); - WXAssert(indexPath.row <= section.rows.count, @"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections); - [section.rows insertObject:cell atIndex:indexPath.row]; -} - -- (void)removeCellForIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections -{ - WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section]; - if (0 == [section.rows count]) { - return; - } - WXAssert(section, @"Removing cell at indexPath:%@ has not been inserted to cell list before, sections:%@", indexPath, sections); - WXAssert(indexPath.row < section.rows.count, @"Removing cell at indexPath:%@ outof range, sections:%@", indexPath, sections); - [section.rows removeObjectAtIndex:indexPath.row]; -} - -- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell sections:(NSMutableArray<WXSectionComponent *> *)sections -{ - __block NSIndexPath *indexPath; - [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull sectionStop) { - [section.rows enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull row, NSUInteger rowIndex, BOOL * _Nonnull stop) { - if (row == cell) { - indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; - *stop = YES; - *sectionStop = YES; - } - }]; - }]; - - return indexPath; -} - -- (NSUInteger)indexForHeader:(WXHeaderComponent *)header sections:(NSMutableArray<WXSectionComponent *> *)sections -{ - __block NSUInteger index; - [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull stop) { - if (section.header == header) { - index = sectionIndex; - *stop = YES; - } - }]; - - return index; -} - -- (NSIndexPath *)indexPathForSubIndex:(NSUInteger)index -{ - NSInteger section = 0; - NSInteger row = -1; - WXComponent *firstComponent; - for (int i = 0; i <= index; i++) { - WXComponent* component = [self.subcomponents wx_safeObjectAtIndex:i]; - if (!component) { - continue; - } - if (([component isKindOfClass:[WXHeaderComponent class]] - || [component isKindOfClass:[WXCellComponent class]]) - && !firstComponent) { - firstComponent = component; - } - - if (component != firstComponent && [component isKindOfClass:[WXHeaderComponent class]]) { - section ++; - row = -1; - } - - if ([component isKindOfClass:[WXCellComponent class]]) { - row ++; - } - } - - return [NSIndexPath indexPathForRow:row inSection:section]; -} - -- (void)_performUpdates:(void(^)(void))updates withKeepScrollPosition:(BOOL)keepScrollPosition adjustmentBlock:(CGFloat(^)(NSIndexPath *topVisibleCell))adjustmentBlock -{ - CGFloat adjustment = 0; - - // keep the scroll position when inserting or deleting sections/rows by adjusting the content offset - if (keepScrollPosition) { - NSIndexPath *top = _tableView.indexPathsForVisibleRows.firstObject; - adjustment = adjustmentBlock(top); - } - - updates(); - - if (keepScrollPosition) { - CGPoint afterContentOffset = _tableView.contentOffset; - CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + adjustment); - _tableView.contentOffset = newContentOffset; - } - - [self handleAppear]; -} - -- (void)_insertTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation -{ - [self _performUpdates:^{ - [_tableView insertSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation]; - } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { - if (section <= top.section) { - return [self tableView:_tableView heightForHeaderInSection:section]; - } else { - return 0.0; - } - }]; -} - -- (void)_deleteTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation -{ - [self _performUpdates:^{ - [_tableView deleteSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation]; - } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { - if (section <= top.section) { - return [self tableView:_tableView heightForHeaderInSection:section]; - } else { - return 0.0; - } - }]; -} - -- (void)_insertTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation -{ - [self _performUpdates:^{ - if ([_updataType isEqual: @"reload"]) { - [_tableView reloadData]; - } else { - [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation]; - } - } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { - if (([indexPath compare:top] <= 0) || [_updataType isEqual: @"reload"]) { - return [self tableView:_tableView heightForRowAtIndexPath:indexPath]; - } else { - return 0.0; - } - }]; -} - -- (void)_deleteTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation -{ - if (!indexPath) { - return ; - } - [self _performUpdates:^{ - [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation]; - } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { - if ([indexPath compare:top] <= 0) { - return [self tableView:_tableView heightForRowAtIndexPath:indexPath]; - } else { - return 0.0; - } - }]; -} - -- (void)fixFlicker -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // FIXME:(ภâ¢Ì_â¢Ì)à¸â»ââ» Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells. - // So if you pull down list while list is rendering, the list will be flickering. - // Demo: - // Have to hook _adjustContentOffsetIfNecessary here. - // Any other more elegant way? - NSString *a = @"ntOffsetIfNe"; - NSString *b = @"adjustConte"; - - NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a]; - [[self class] weex_swizzle:[WXTableView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)]; - }); -} - -- (void)fixedFlickerSelector -{ - // DO NOT delete this method. -} - - -@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm new file mode 100644 index 0000000..bd4eb64 --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm @@ -0,0 +1,997 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "WXListComponent.h" +#import "WXCellComponent.h" +#import "WXHeaderComponent.h" +#import "WXComponent.h" +#import "WXComponent_internal.h" +#import "NSArray+Weex.h" +#import "WXAssert.h" +#import "WXMonitor.h" +#import "WXUtility.h" +#import "NSObject+WXSwizzle.h" +#import "WXSDKInstance_private.h" +#import "WXRefreshComponent.h" +#import "WXLoadingComponent.h" +#import "WXScrollerComponent+Layout.h" + +@interface WXTableView : UITableView + +@end + +@implementation WXTableView + ++ (BOOL)requiresConstraintBasedLayout +{ + return NO; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch +{ + if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) { + return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch]; + } + else{ + return YES; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + [self.wx_component layoutDidFinish]; +} + +- (void)setContentOffset:(CGPoint)contentOffset +{ + // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary. + // When UITableView is pulled down and finger releasesï¼contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary. + // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator. + // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f + // Have to reset contentOffset to zero manually here. + if (fabs(contentOffset.y) < 0.5) { + contentOffset.y = 0; + } + if (isnan(contentOffset.x)) { + contentOffset.x = 0; + } + if(isnan(contentOffset.y)) { + contentOffset.y = 0; + } + + [super setContentOffset:contentOffset]; +} + +@end + +// WXText is a non-public is not permitted +@interface WXSectionComponent : NSObject<NSCopying> + +@property (nonatomic, strong) WXHeaderComponent *header; +@property (nonatomic, strong) NSMutableArray<WXCellComponent *> *rows; + +@end + +@implementation WXSectionComponent + +- (instancetype)init +{ + if (self = [super init]) { + _rows = [NSMutableArray array]; + } + + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + WXSectionComponent *newSection = [[[self class] allocWithZone:zone] init]; + newSection.header = _header; + newSection.rows = [_rows mutableCopyWithZone:zone]; + + return newSection; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"%@\n%@", [_header description], [_rows description]]; +} +@end + +@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate> + +@property (nonatomic, assign) NSUInteger currentTopVisibleSection; + +@end + +@implementation WXListComponent +{ + __weak UITableView * _tableView; + + // Only accessed on component thread + NSMutableArray<WXSectionComponent *> *_sections; + // Only accessed on main thread + NSMutableArray<WXSectionComponent *> *_completedSections; + NSUInteger _previousLoadMoreRowNumber; + // insert & reload & batch + NSString *_updataType; + + BOOL _isUpdating; + NSMutableArray<void(^)(void)> *_updates; + NSTimeInterval _reloadInterval; +} + +- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance +{ + if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) { + _sections = [NSMutableArray array]; + _completedSections = [NSMutableArray array]; + _reloadInterval = attributes[@"reloadInterval"] ? [WXConvert CGFloat:attributes[@"reloadInterval"]]/1000 : 0; + _updataType = [WXConvert NSString:attributes[@"updataType"]]?:@"insert"; + [self fixFlicker]; + } + + return self; +} + +- (void)dealloc +{ + if (_tableView) { + _tableView.delegate = nil; + _tableView.dataSource = nil; + } +} + +- (UIView *)loadView +{ + return [[WXTableView alloc] init]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + _tableView = (UITableView *)self.view; + _tableView.allowsSelection = NO; + _tableView.allowsMultipleSelection = NO; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + _tableView.delegate = self; + _tableView.dataSource = self; + _tableView.userInteractionEnabled = YES; + + _tableView.estimatedRowHeight = 0; + _tableView.estimatedSectionFooterHeight = 0; + _tableView.estimatedSectionHeaderHeight = 0; +} + +- (void)viewWillUnload +{ + [super viewWillUnload]; + + _tableView.delegate = nil; + _tableView.dataSource = nil; +} + +- (void)updateAttributes:(NSDictionary *)attributes +{ + [super updateAttributes:attributes]; + + if (attributes[@"reloadInterval"]) { + _reloadInterval = [WXConvert CGFloat:attributes[@"reloadInterval"]] / 1000; + } + if (attributes[@"updataType"]) { + _updataType = [WXConvert NSString:attributes[@"updataType"]]; + } +} + +- (void)setContentSize:(CGSize)contentSize +{ + // Do Nothing +} + +- (void)_handleFirstScreenTime +{ + // Do Nothingï¼ firstScreenTime is set by cellDidRendered: +} + +- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated +{ + CGPoint contentOffset = _tableView.contentOffset; + CGFloat contentOffsetY = 0; + + WXComponent *cellComponent = component; + CGRect cellRect; + while (cellComponent) { + if ([cellComponent isKindOfClass:[WXCellComponent class]]) { + NSIndexPath *toIndexPath = [self indexPathForCell:(WXCellComponent*)cellComponent sections:_completedSections]; + cellRect = [_tableView rectForRowAtIndexPath:toIndexPath]; + break; + } + if ([cellComponent isKindOfClass:[WXHeaderComponent class]]) { + NSUInteger toIndex = [self indexForHeader:(WXHeaderComponent *)cellComponent sections:_completedSections]; + cellRect = [_tableView rectForSection:toIndex]; + break; + } + contentOffsetY += cellComponent.calculatedFrame.origin.y; + cellComponent = cellComponent.supercomponent; + } + + contentOffsetY += cellRect.origin.y; + contentOffsetY += offset * self.weexInstance.pixelScaleFactor; + + if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) { + contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height; + } else { + contentOffset.y = contentOffsetY; + } + + [_tableView setContentOffset:contentOffset animated:animated]; +} + + +#pragma mark - Inheritance + +- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index +{ + if ([subcomponent isKindOfClass:[WXCellComponent class]]) { + ((WXCellComponent *)subcomponent).delegate = self; + } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) { + ((WXHeaderComponent *)subcomponent).delegate = self; + } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]] + && ![subcomponent isKindOfClass:[WXLoadingComponent class]] + && subcomponent->_positionType != WXPositionTypeFixed) { + WXLogError(@"list only support cell/header/refresh/loading/fixed-component as child."); + return; + } + + [super _insertSubcomponent:subcomponent atIndex:index]; + + if (![subcomponent isKindOfClass:[WXHeaderComponent class]] + && ![subcomponent isKindOfClass:[WXCellComponent class]]) { + // Don't insert section if subcomponent is not header or cell + return; + } + + NSIndexPath *indexPath = [self indexPathForSubIndex:index]; + + if ([subcomponent isKindOfClass:[WXHeaderComponent class]] || _sections.count <= indexPath.section) { + // conditions to insert section: insert a header or insert first cell of table view + // this will be updated by recycler's update controller in the future + WXSectionComponent *insertSection = [WXSectionComponent new]; + BOOL keepScrollPosition = NO; + if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) { + WXHeaderComponent *header = (WXHeaderComponent*)subcomponent; + insertSection.header = header; + keepScrollPosition = header.keepScrollPosition; + } + + NSUInteger insertIndex = indexPath.section; + WXSectionComponent *reloadSection; + if (insertIndex > 0 && insertIndex <= _sections.count + && [subcomponent isKindOfClass:[WXHeaderComponent class]]) { + // insert a header in the middle, one section may divide into two + // so the original section need to be reloaded + NSIndexPath *indexPathBeforeHeader = [self indexPathForSubIndex:index - 1]; + if (_sections[insertIndex - 1].rows.count != 0 && indexPathBeforeHeader.row < _sections[insertIndex - 1].rows.count - 1) { + reloadSection = _sections[insertIndex - 1]; + NSArray *rowsToSeparate = reloadSection.rows; + insertSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(indexPathBeforeHeader.row + 1, rowsToSeparate.count - indexPathBeforeHeader.row - 1)] mutableCopy]; + reloadSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(0, indexPathBeforeHeader.row + 1)] mutableCopy]; + } + } + + [_sections insertObject:insertSection atIndex:insertIndex]; + WXSectionComponent *completedInsertSection = [insertSection copy]; + WXSectionComponent *completedReloadSection; + if (reloadSection) { + completedReloadSection = [reloadSection copy]; + } + + [self.weexInstance.componentManager _addUITask:^{ + WXLogDebug(@"Insert section:%ld", (unsigned long)insertIndex); + + [UIView performWithoutAnimation:^{ + + @try { + [_tableView beginUpdates]; + + [_completedSections insertObject:completedInsertSection atIndex:insertIndex]; + if (completedReloadSection) { + WXLogDebug(@"Reload section:%lu", (unsigned long)(insertIndex - 1)); + _completedSections[insertIndex - 1] = completedReloadSection; + } + + [self _insertTableViewSectionAtIndex:insertIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone]; + + if (completedReloadSection) { + [_tableView reloadSections:[NSIndexSet indexSetWithIndex:insertIndex - 1] withRowAnimation:UITableViewRowAnimationNone]; + } + + [_tableView endUpdates]; + } @catch (NSException *exception) { + WXLogError(@"list insert component occurs exception %@", exception); + } @finally { + // nothing + } + + }]; + + }]; + + } +} + +- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index +{ + //Here will not insert cell or header's view again + if (![subcomponent isKindOfClass:[WXCellComponent class]] + && ![subcomponent isKindOfClass:[WXHeaderComponent class]]) { + [super insertSubview:subcomponent atIndex:index]; + } +} + +#pragma mark - WXHeaderRenderDelegate + +- (float)headerWidthForLayout:(WXHeaderComponent *)cell +{ +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]){ + return self.scrollerCSSNode->style.dimensions[CSS_WIDTH]; + } +//#else + else + { + return self.flexScrollerCSSNode->getStyleWidth(); + } +//#endif +} + +- (void)headerDidLayout:(WXHeaderComponent *)header +{ + [self.weexInstance.componentManager _addUITask:^{ + // trigger section header update + [UIView performWithoutAnimation:^{ + [_tableView beginUpdates]; + + NSUInteger reloadIndex = [self indexForHeader:header sections:_completedSections]; + [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone]; + + [_tableView endUpdates]; + }]; + }]; +} + +- (void)headerDidRemove:(WXHeaderComponent *)header +{ + NSUInteger headerIndex = [self indexForHeader:header sections:_sections]; + // this will be updated by recycler's update controller in the future + WXSectionComponent *headerSection = _sections[headerIndex]; + WXSectionComponent *reloadSection; + NSUInteger reloadIndex = -1; + BOOL isDeleteSection = NO; + if (headerIndex == 0 && headerSection.rows.count > 0) { + // delete a header in the first section and the section still has cells + // reload the first section + reloadIndex = 0; + reloadSection = _sections[reloadIndex]; + _sections[reloadIndex].header = nil; + } else if (headerIndex > 0 && headerSection.rows.count > 0) { + // delete a header in the middle, two sections merge into one + // so one section need to be deleted and the other should be relo + isDeleteSection = YES; + reloadIndex = headerIndex - 1; + reloadSection = _sections[reloadIndex]; + reloadSection.rows = [[reloadSection.rows arrayByAddingObjectsFromArray:headerSection.rows] mutableCopy]; + [_sections removeObjectAtIndex:headerIndex]; + } else { + // delete a header with no cell in that section + // just delete the section + isDeleteSection = YES; + [_sections removeObjectAtIndex:headerIndex]; + } + + WXSectionComponent *completedReloadSection; + if (reloadSection) { + completedReloadSection = [reloadSection copy]; + } + BOOL keepScrollPosition = header.keepScrollPosition; + + [self.weexInstance.componentManager _addUITask:^{ + if (isDeleteSection) { + WXLogDebug(@"delete section:%lu", (unsigned long)headerIndex); + [_completedSections removeObjectAtIndex:headerIndex]; + } + + if (reloadIndex == 0 && !isDeleteSection) { + _completedSections[reloadIndex].header = nil; + } + + if (completedReloadSection) { + WXLogDebug(@"Reload section:%lu", (unsigned long)reloadIndex); + _completedSections[reloadIndex] = completedReloadSection; + } + + [UIView performWithoutAnimation:^{ + [_tableView beginUpdates]; + if (isDeleteSection) { + [self _deleteTableViewSectionAtIndex:headerIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone]; + } + + if (completedReloadSection) { + [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone]; + } + + [_tableView endUpdates]; + }]; + + }]; +} + +#pragma mark - WXCellRenderDelegate + +- (float)containerWidthForLayout:(WXCellComponent *)cell +{ +//#ifndef USE_FLEX + if (![WXComponent isUseFlex]) { + return self.scrollerCSSNode->style.dimensions[CSS_WIDTH]; + } +//#else + else{ + return self.flexScrollerCSSNode->getStyleWidth(); + } +//#endif +} + +- (void)cellDidRemove:(WXCellComponent *)cell +{ + WXAssertComponentThread(); + + NSIndexPath *indexPath = [self indexPathForCell:cell sections:_sections]; + if(!indexPath){ + //protect when cell not exist in sections + return; + } + [self removeCellForIndexPath:indexPath withSections:_sections]; + + [self.weexInstance.componentManager _addUITask:^{ + [self removeCellForIndexPath:indexPath withSections:_completedSections]; + + WXLogDebug(@"Delete cell:%@ at indexPath:%@", cell.ref, indexPath); + if (cell.deleteAnimation == UITableViewRowAnimationNone) { + [UIView performWithoutAnimation:^{ + [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone]; + }]; + } else { + [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.deleteAnimation]; + } + }]; +} + +- (void)cellDidLayout:(WXCellComponent *)cell +{ + WXAssertComponentThread() ; + + NSUInteger index = [self.subcomponents indexOfObject:cell]; + NSIndexPath *indexPath = [self indexPathForSubIndex:index]; + + NSInteger sectionNum = indexPath.section; + if (sectionNum >= [_sections count] || sectionNum < 0) { + // try to protect sectionNum out of range. + return; + } + NSInteger row = indexPath.row; + NSMutableArray *sections = _sections; + WXSectionComponent *section = sections[sectionNum]; + WXAssert(section, @"no section found for section number:%ld", sectionNum); + NSMutableArray *completedSections; + BOOL isReload = [section.rows containsObject:cell]; + if (!isReload && row > [section.rows count]) { + // protect crash when row out of bounds + return ; + } + if (!isReload) { + [section.rows insertObject:cell atIndex:row]; + // deep copy + completedSections = [[NSMutableArray alloc] initWithArray:sections copyItems:YES];; + } + + [self.weexInstance.componentManager _addUITask:^{ + if (!isReload) { + WXLogDebug(@"Insert cell:%@ at indexPath:%@", cell.ref, indexPath); + _completedSections = completedSections; + if (cell.insertAnimation == UITableViewRowAnimationNone) { + [UIView performWithoutAnimation:^{ + [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone]; + }]; + } else { + [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.insertAnimation]; + } + } else { + WXLogInfo(@"Reload cell:%@ at indexPath:%@", cell.ref, indexPath); + [UIView performWithoutAnimation:^{ + [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; + [self handleAppear]; + }]; + } + }]; +} + +- (void)cellDidRendered:(WXCellComponent *)cell +{ + WXAssertMainThread(); + + if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) { + // improve performance + return; + } + + NSIndexPath *indexPath = [self indexPathForCell:cell sections:_completedSections]; + if (!indexPath || indexPath.section >= [_tableView numberOfSections] || + indexPath.row < 0 || indexPath.row >= [_tableView numberOfRowsInSection:indexPath.section]) { + WXLogWarning(@"Rendered cell:%@ out of range, sections:%@", cell, _completedSections); + return; + } + + CGRect cellRect = [_tableView rectForRowAtIndexPath:indexPath]; + if (cellRect.origin.y + cellRect.size.height >= _tableView.frame.size.height) { + WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance); + } + + if (self.weexInstance.onRenderProgress) { + CGRect renderRect = [_tableView convertRect:cellRect toView:self.weexInstance.rootView]; + self.weexInstance.onRenderProgress(renderRect); + } +} + +- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index +{ + WXAssertComponentThread(); + + NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections]; + NSIndexPath *toIndexPath = [self indexPathForSubIndex:index]; + if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) { + WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]); + return; + } + [self removeCellForIndexPath:fromIndexPath withSections:_sections]; + [self insertCell:cell forIndexPath:toIndexPath withSections:_sections]; + + [self.weexInstance.componentManager _addUITask:^{ + if (_reloadInterval > 0) { + // use [UITableView reloadData] to do batch updates, will move to recycler's update controller + __weak typeof(self) weakSelf = self; + if (!_updates) { + _updates = [NSMutableArray array]; + } + [_updates addObject:^{ + __strong typeof(weakSelf) strongSelf = weakSelf; + [strongSelf removeCellForIndexPath:fromIndexPath withSections:strongSelf->_completedSections]; + [strongSelf insertCell:cell forIndexPath:toIndexPath withSections:strongSelf->_completedSections]; + }]; + + [self checkReloadData]; + } else { + [self removeCellForIndexPath:fromIndexPath withSections:_completedSections]; + [self insertCell:cell forIndexPath:toIndexPath withSections:_completedSections]; + [UIView performWithoutAnimation:^{ + @try { + [_tableView beginUpdates]; + [_tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; + [self handleAppear]; + [_tableView endUpdates]; + }@catch(NSException * exception){ + WXLogDebug(@"move cell exception: %@", [exception description]); + }@finally { + // do nothing + } + }]; + } + }]; +} + +- (void)checkReloadData +{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reloadInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (_isUpdating || _updates.count == 0) { + return ; + } + + _isUpdating = YES; + NSArray *updates = [_updates copy]; + [_updates removeAllObjects]; + for (void(^update)(void) in updates) { + update(); + } + [_tableView reloadData]; + _isUpdating = NO; + + [self checkReloadData]; + }); +} + +- (void)addStickyComponent:(WXComponent *)sticky +{ + +} + +- (void)removeStickyComponent:(WXComponent *)sticky +{ + +} +#pragma mark - TableView delegate + +- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath +{ + +} + +- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath +{ + WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath); + NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows]; + if (![visibleIndexPaths containsObject:indexPath]) { + if (cell.contentView.subviews.count > 0) { + UIView *wxCellView = [cell.contentView.subviews firstObject]; + // Must invoke synchronously otherwise it will remove the view just added. + WXCellComponent *cellComponent = (WXCellComponent *)wxCellView.wx_component; + if (cellComponent.isRecycle) { + [wxCellView.wx_component _unloadViewWithReusing:YES]; + } + } + } +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + WXCellComponent *cell = [self cellForIndexPath:indexPath]; + return cell.calculatedFrame.size.height; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section +{ + WXHeaderComponent *header = ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).header; + if (header) { + return header.calculatedFrame.size.height; + } else { + return 0.0; + } +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + [super scrollViewDidScroll:scrollView]; + if ([[_tableView indexPathsForVisibleRows] count] > 0) { + NSIndexPath *topCellPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0]; + if (self.currentTopVisibleSection != topCellPath.section) { + if (self.currentTopVisibleSection) { + WXSectionComponent *removeSection = [_sections wx_safeObjectAtIndex:self.currentTopVisibleSection]; + if (removeSection.header && [removeSection.header.events containsObject:@"unsticky"]) { + [removeSection.header fireEvent:@"unsticky" params:nil]; + } + } + self.currentTopVisibleSection = topCellPath.section; + WXSectionComponent *showSection = [_sections wx_safeObjectAtIndex:topCellPath.section]; + if (showSection.header && [showSection.header.events containsObject:@"sticky"]) { + [showSection.header fireEvent:@"sticky" params:nil]; + } + } + } +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section +{ + WXHeaderComponent *header = ((WXSectionComponent *)_completedSections[section]).header; + WXLogDebug(@"header view for section %ld:%@", (long)section, header.view); + return header.view; +} + +#pragma mark - TableView Data Source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return _completedSections.count; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).rows.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + WXLogDebug(@"Getting cell at indexPath:%@", indexPath); + static NSString *reuseIdentifier = @"WXTableViewCell"; + + UITableViewCell *cellView = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; + if (!cellView) { + cellView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; + cellView.backgroundColor = [UIColor clearColor]; + } + + WXCellComponent *cell = [self cellForIndexPath:indexPath]; + + if (cell.zIndex) { + cellView.layer.zPosition = [WXConvert CGFloat:cell.zIndex]; + } + + if (!cell) { + return cellView; + } + + if (cell.view.superview == cellView.contentView) { + return cellView; + } + + for (UIView *view in cellView.contentView.subviews) { + [view removeFromSuperview]; + } + + [cellView.contentView addSubview:cell.view]; + + [cellView setAccessibilityIdentifier:cell.view.accessibilityIdentifier]; + + WXLogDebug(@"Created cell:%@ view:%@ cellView:%@ at indexPath:%@", cell.ref, cell.view, cellView, indexPath); + return cellView; +} + +#pragma mark - Load More Event + +- (void)setLoadmoreretry:(NSUInteger)loadmoreretry +{ + if (loadmoreretry != self.loadmoreretry) { + _previousLoadMoreRowNumber = 0; + } + + [super setLoadmoreretry:loadmoreretry]; +} + +- (void)loadMore +{ + [super loadMore]; + + _previousLoadMoreRowNumber = [self totalNumberOfRows]; +} + +- (BOOL)isNeedLoadMore +{ + BOOL superNeedLoadMore = [super isNeedLoadMore]; + return superNeedLoadMore && _previousLoadMoreRowNumber != [self totalNumberOfRows]; +} + +- (NSUInteger)totalNumberOfRows +{ + NSUInteger rowNumber = 0; + NSUInteger sectionCount = [_tableView numberOfSections]; + for (int section = 0; section < sectionCount; section ++) { + rowNumber += [_tableView numberOfRowsInSection:section]; + } + + return rowNumber; +} + +- (void)resetLoadmore{ + [super resetLoadmore]; + _previousLoadMoreRowNumber=0; +} + +#pragma mark Private + +- (WXCellComponent *)cellForIndexPath:(NSIndexPath *)indexPath +{ + WXSectionComponent *section = [_completedSections wx_safeObjectAtIndex:indexPath.section]; + if (!section) { + WXLogError(@"No section found for num:%ld, completed sections:%ld", (long)indexPath.section, (unsigned long)_completedSections.count); + return nil; + } + + WXCellComponent *cell = [section.rows wx_safeObjectAtIndex:indexPath.row]; + if (!cell) { + WXLogError(@"No cell found for num:%ld, completed rows:%ld", (long)indexPath.row, (unsigned long)section.rows.count); + return nil; + } + + return cell; +} + +- (void)insertCell:(WXCellComponent *)cell forIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections +{ + WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section]; + if (indexPath.row > [section.rows count] || indexPath.row < 0) { + WXLogError(@"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections); + return; + } + WXAssert(section, @"inserting cell at indexPath:%@ section has not been inserted to list before, sections:%@", indexPath, sections); + WXAssert(indexPath.row <= section.rows.count, @"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections); + [section.rows insertObject:cell atIndex:indexPath.row]; +} + +- (void)removeCellForIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections +{ + WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section]; + if (0 == [section.rows count]) { + return; + } + WXAssert(section, @"Removing cell at indexPath:%@ has not been inserted to cell list before, sections:%@", indexPath, sections); + WXAssert(indexPath.row < section.rows.count, @"Removing cell at indexPath:%@ outof range, sections:%@", indexPath, sections); + [section.rows removeObjectAtIndex:indexPath.row]; +} + +- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell sections:(NSMutableArray<WXSectionComponent *> *)sections +{ + __block NSIndexPath *indexPath; + [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull sectionStop) { + [section.rows enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull row, NSUInteger rowIndex, BOOL * _Nonnull stop) { + if (row == cell) { + indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + *stop = YES; + *sectionStop = YES; + } + }]; + }]; + + return indexPath; +} + +- (NSUInteger)indexForHeader:(WXHeaderComponent *)header sections:(NSMutableArray<WXSectionComponent *> *)sections +{ + __block NSUInteger index; + [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull stop) { + if (section.header == header) { + index = sectionIndex; + *stop = YES; + } + }]; + + return index; +} + +- (NSIndexPath *)indexPathForSubIndex:(NSUInteger)index +{ + NSInteger section = 0; + NSInteger row = -1; + WXComponent *firstComponent; + for (int i = 0; i <= index; i++) { + WXComponent* component = [self.subcomponents wx_safeObjectAtIndex:i]; + if (!component) { + continue; + } + if (([component isKindOfClass:[WXHeaderComponent class]] + || [component isKindOfClass:[WXCellComponent class]]) + && !firstComponent) { + firstComponent = component; + } + + if (component != firstComponent && [component isKindOfClass:[WXHeaderComponent class]]) { + section ++; + row = -1; + } + + if ([component isKindOfClass:[WXCellComponent class]]) { + row ++; + } + } + + return [NSIndexPath indexPathForRow:row inSection:section]; +} + +- (void)_performUpdates:(void(^)(void))updates withKeepScrollPosition:(BOOL)keepScrollPosition adjustmentBlock:(CGFloat(^)(NSIndexPath *topVisibleCell))adjustmentBlock +{ + CGFloat adjustment = 0; + + // keep the scroll position when inserting or deleting sections/rows by adjusting the content offset + if (keepScrollPosition) { + NSIndexPath *top = _tableView.indexPathsForVisibleRows.firstObject; + adjustment = adjustmentBlock(top); + } + + updates(); + + if (keepScrollPosition) { + CGPoint afterContentOffset = _tableView.contentOffset; + CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + adjustment); + _tableView.contentOffset = newContentOffset; + } + + [self handleAppear]; +} + +- (void)_insertTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation +{ + [self _performUpdates:^{ + [_tableView insertSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation]; + } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { + if (section <= top.section) { + return [self tableView:_tableView heightForHeaderInSection:section]; + } else { + return 0.0; + } + }]; +} + +- (void)_deleteTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation +{ + [self _performUpdates:^{ + [_tableView deleteSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation]; + } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { + if (section <= top.section) { + return [self tableView:_tableView heightForHeaderInSection:section]; + } else { + return 0.0; + } + }]; +} + +- (void)_insertTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation +{ + [self _performUpdates:^{ + if ([_updataType isEqual: @"reload"]) { + [_tableView reloadData]; + } else { + [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation]; + } + } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { + if (([indexPath compare:top] <= 0) || [_updataType isEqual: @"reload"]) { + return [self tableView:_tableView heightForRowAtIndexPath:indexPath]; + } else { + return 0.0; + } + }]; +} + +- (void)_deleteTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation +{ + if (!indexPath) { + return ; + } + [self _performUpdates:^{ + [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation]; + } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) { + if ([indexPath compare:top] <= 0) { + return [self tableView:_tableView heightForRowAtIndexPath:indexPath]; + } else { + return 0.0; + } + }]; +} + +- (void)fixFlicker +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // FIXME:(ภâ¢Ì_â¢Ì)à¸â»ââ» Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells. + // So if you pull down list while list is rendering, the list will be flickering. + // Demo: + // Have to hook _adjustContentOffsetIfNecessary here. + // Any other more elegant way? + NSString *a = @"ntOffsetIfNe"; + NSString *b = @"adjustConte"; + + NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a]; + [[self class] weex_swizzle:[WXTableView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)]; + }); +} + +- (void)fixedFlickerSelector +{ + // DO NOT delete this method. +} + + +@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m deleted file mode 100644 index d57fa9a..0000000 --- a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "WXLoadingComponent.h" -#import "WXScrollerComponent.h" -#import "WXLoadingIndicator.h" -#import "WXComponent_internal.h" -#import "WXLog.h" - -@interface WXLoadingComponent() - -@property (nonatomic) BOOL initFinished; -@property (nonatomic) BOOL loadingEvent; -@property (nonatomic) BOOL displayState; - -@property (nonatomic, weak) WXLoadingIndicator *indicator; - -@end - -@implementation WXLoadingComponent - -- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance -{ - self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]; - if (self) { - if (attributes[@"display"]) { - if ([attributes[@"display"] isEqualToString:@"show"]) { - _displayState = YES; - } else if ([attributes[@"display"] isEqualToString:@"hide"]) { - _displayState = NO; - } else { - WXLogError(@""); - } - } - self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE; - } - return self; -} - -- (void)viewWillUnload -{ - _displayState = NO; - _loadingEvent = NO; - _initFinished = NO; -} - --(void)updateAttributes:(NSDictionary *)attributes -{ - if (attributes[@"display"]) { - if ([attributes[@"display"] isEqualToString:@"show"]) { - _displayState = YES; - } else if ([attributes[@"display"] isEqualToString:@"hide"]) { - _displayState = NO; - } else { - WXLogError(@""); - } - [self setDisplay]; - } -} - -- (void)viewDidLoad -{ - _initFinished = YES; - - if (!_displayState) { - [_indicator.view setHidden:YES]; - } -} - -- (void)addEvent:(NSString *)eventName -{ - if ([eventName isEqualToString:@"loading"]) { - _loadingEvent = YES; - } -} - -- (void)removeEvent:(NSString *)eventName -{ - if ([eventName isEqualToString:@"loading"]) { - _loadingEvent = NO; - } -} - -- (void)loading -{ - if (!_loadingEvent || _displayState) - return; - - [self fireEvent:@"loading" params:nil]; -} - -- (void)setDisplay -{ - id<WXScrollerProtocol> scrollerProtocol = [self ancestorScroller]; - if (scrollerProtocol == nil || !_initFinished) - return; - WXComponent *scroller = (WXComponent*)scrollerProtocol; - CGPoint contentOffset = [scrollerProtocol contentOffset]; - if (_displayState) { - contentOffset.y = [scrollerProtocol contentSize].height - scroller.calculatedFrame.size.height + self.calculatedFrame.size.height; - [_indicator start]; - } else { - contentOffset.y = contentOffset.y - self.calculatedFrame.size.height; - [_indicator stop]; - } - [scrollerProtocol setContentOffset:contentOffset animated:YES]; -} - -- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index -{ - if (subcomponent) { - [super _insertSubcomponent:subcomponent atIndex:index]; - if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) { - _indicator = (WXLoadingIndicator*)subcomponent; - } - } -} - -- (BOOL)displayState -{ - return _displayState; -} - -- (void)resizeFrame -{ - CGRect rect = self.calculatedFrame; - - id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller; - if (scrollerProtocol) { - rect.origin.y = [scrollerProtocol contentSize].height; - } - - [self.view setFrame:rect]; -} - -@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm new file mode 100644 index 0000000..bb5024f --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "WXLoadingComponent.h" +#import "WXScrollerComponent.h" +#import "WXLoadingIndicator.h" +#import "WXComponent_internal.h" +#import "WXLog.h" +#import "WXComponent+Layout.h" + +@interface WXLoadingComponent() + +@property (nonatomic) BOOL initFinished; +@property (nonatomic) BOOL loadingEvent; +@property (nonatomic) BOOL displayState; + +@property (nonatomic, weak) WXLoadingIndicator *indicator; + +@end + +@implementation WXLoadingComponent + +- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance +{ + self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]; + if (self) { + if (attributes[@"display"]) { + if ([attributes[@"display"] isEqualToString:@"show"]) { + _displayState = YES; + } else if ([attributes[@"display"] isEqualToString:@"hide"]) { + _displayState = NO; + } else { + WXLogError(@""); + } + } +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE; + } +//#else + else + { + self.flexCssNode->setStylePositionType(WeexCore::kAbsolute); + } + +//#endif + } + return self; +} + +- (void)viewWillUnload +{ + _displayState = NO; + _loadingEvent = NO; + _initFinished = NO; +} + +-(void)updateAttributes:(NSDictionary *)attributes +{ + if (attributes[@"display"]) { + if ([attributes[@"display"] isEqualToString:@"show"]) { + _displayState = YES; + } else if ([attributes[@"display"] isEqualToString:@"hide"]) { + _displayState = NO; + } else { + WXLogError(@""); + } + [self setDisplay]; + } +} + +- (void)viewDidLoad +{ + _initFinished = YES; + + if (!_displayState) { + [_indicator.view setHidden:YES]; + } +} + +- (void)addEvent:(NSString *)eventName +{ + if ([eventName isEqualToString:@"loading"]) { + _loadingEvent = YES; + } +} + +- (void)removeEvent:(NSString *)eventName +{ + if ([eventName isEqualToString:@"loading"]) { + _loadingEvent = NO; + } +} + +- (void)loading +{ + if (!_loadingEvent || _displayState) + return; + + [self fireEvent:@"loading" params:nil]; +} + +- (void)setDisplay +{ + id<WXScrollerProtocol> scrollerProtocol = [self ancestorScroller]; + if (scrollerProtocol == nil || !_initFinished) + return; + WXComponent *scroller = (WXComponent*)scrollerProtocol; + CGPoint contentOffset = [scrollerProtocol contentOffset]; + if (_displayState) { + contentOffset.y = [scrollerProtocol contentSize].height - scroller.calculatedFrame.size.height + self.calculatedFrame.size.height; + [_indicator start]; + } else { + contentOffset.y = contentOffset.y - self.calculatedFrame.size.height; + [_indicator stop]; + } + [scrollerProtocol setContentOffset:contentOffset animated:YES]; +} + +- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index +{ + if (subcomponent) { + [super _insertSubcomponent:subcomponent atIndex:index]; + if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) { + _indicator = (WXLoadingIndicator*)subcomponent; + } + } +} + +- (BOOL)displayState +{ + return _displayState; +} + +- (void)resizeFrame +{ + CGRect rect = self.calculatedFrame; + + id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller; + if (scrollerProtocol) { + rect.origin.y = [scrollerProtocol contentSize].height; + } + + [self.view setFrame:rect]; +} + +@end