http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m index b76ac90..e50e15a 100644 --- a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m +++ b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m @@ -136,6 +136,9 @@ _Pragma("clang diagnostic pop") \ } [WXTracingManager startTracingWithInstanceId:instanceId ref:elementData[@"ref"] className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"addElement" options:nil]; WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: addElement : %@",elementData[@"type"]); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -159,6 +162,9 @@ _Pragma("clang diagnostic pop") \ } [WXTracingManager startTracingWithInstanceId:instanceId ref:bodyData[@"ref"] className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: createBody %@ ref:%@",bodyData[@"type"],bodyData[@"ref"]); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -183,6 +189,9 @@ _Pragma("clang diagnostic pop") \ } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"removeElement" options:nil]; WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: removeElement ref:%@",ref); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -206,6 +215,9 @@ _Pragma("clang diagnostic pop") \ } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"moveElement" options:nil]; WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: moveElement ,ref:%@ to ref:%@",ref,parentRef); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -228,6 +240,9 @@ _Pragma("clang diagnostic pop") \ [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: updateAttrs ref:%@,attr:%@",ref,attrsData); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -251,6 +266,10 @@ _Pragma("clang diagnostic pop") \ } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"updateStyles" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ + +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: updateStyles ref:%@,styles:%@",ref,stylesData); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -273,6 +292,10 @@ _Pragma("clang diagnostic pop") \ } WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: addEvent ref:%@",ref); +#endif + WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -295,6 +318,9 @@ _Pragma("clang diagnostic pop") \ } WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action :removeEvent ref:%@",ref); +#endif WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -317,6 +343,10 @@ _Pragma("clang diagnostic pop") \ } [WXTracingManager startTracingWithInstanceId:instanceId ref:nil className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"createFinish" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: createFinish :%@",instanceId); +#endif + WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; @@ -339,6 +369,9 @@ _Pragma("clang diagnostic pop") \ WXLogInfo(@"instance not found for callNativeModule:%@.%@, maybe already destroyed", moduleName, methodName); return nil; } +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: callNativeModule : %@ . %@",moduleName,methodName); +#endif NSMutableDictionary * newOptions = [options mutableCopy]; NSMutableArray * newArguments = [arguments mutableCopy]; @@ -367,6 +400,11 @@ _Pragma("clang diagnostic pop") \ }]; [_jsBridge registerCallNativeComponent:^void(NSString *instanceId, NSString *componentRef, NSString *methodName, NSArray *args, NSDictionary *options) { + +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action: callNativeComponent ref:%@",componentRef); +#endif + WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; WXComponentMethod *method = [[WXComponentMethod alloc] initWithComponentRef:componentRef methodName:methodName arguments:args instance:instance]; [method invoke];
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m deleted file mode 100644 index a6adec0..0000000 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m +++ /dev/null @@ -1,94 +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 "WXCellSlotComponent.h" -#import "WXComponent_internal.h" -#import "WXComponentManager.h" -#import "WXSDKInstance_private.h" -#import "WXConvert.h" -#import "WXAssert.h" -#import "WXScrollerComponent.h" - -@implementation WXCellSlotComponent - -- (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) { - // TODO: isRecycle / insertAnimation / deleteAnimation / keepScrollPosition - if (attributes[@"default"]) { - _templateCaseType = @"default"; - } - _templateCaseType = attributes[@"case"] ? [WXConvert NSString:attributes[@"case"]] : WXDefaultRecycleTemplateType; - _lazyCreateView = YES; - _isNeedJoinLayoutSystem = NO; - } - - return self; -} - -- (void)updateAttributes:(NSDictionary *)attributes -{ - -} - -- (void)updateCellData:(NSDictionary *)data -{ - WXAssertComponentThread(); - - [self updateBindingData:data]; - [self triggerLayout]; -} - -- (void)_didInserted -{ - [self triggerLayout]; -} - -- (void)triggerLayout -{ - WXAssertComponentThread(); - - //TODO: _isUseContainerWidth? - if (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH])) { - self.cssNode->style.dimensions[CSS_WIDTH] = ((WXScrollerComponent *)(self.supercomponent)).scrollerCSSNode->style.dimensions[CSS_WIDTH]; - } - - if ([self needsLayout]) { - layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT); - if ([WXLog logLevel] >= WXLogLevelDebug) { - print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN); - } - } - - NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set]; - [self _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents]; - for (WXComponent *dirtyComponent in dirtyComponents) { - [self.weexInstance.componentManager _addUITask:^{ - [dirtyComponent _layoutDidFinish]; - }]; - } -} - -@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm new file mode 100644 index 0000000..b1b757a --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm @@ -0,0 +1,117 @@ +/* + * 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 "WXCellSlotComponent.h" +#import "WXComponent_internal.h" +#import "WXComponentManager.h" +#import "WXSDKInstance_private.h" +#import "WXConvert.h" +#import "WXAssert.h" +#import "WXScrollerComponent+Layout.h" + +@implementation WXCellSlotComponent + +- (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) { + // TODO: isRecycle / insertAnimation / deleteAnimation / keepScrollPosition + if (attributes[@"default"]) { + _templateCaseType = @"default"; + } + _templateCaseType = attributes[@"case"] ? [WXConvert NSString:attributes[@"case"]] :const_cast<NSString *>(WXDefaultRecycleTemplateType) ; + _lazyCreateView = YES; + _isNeedJoinLayoutSystem = NO; + } + + return self; +} + +- (void)updateAttributes:(NSDictionary *)attributes +{ + +} + +- (void)updateCellData:(NSDictionary *)data +{ + WXAssertComponentThread(); + + [self updateBindingData:data]; + [self triggerLayout]; +} + +- (void)_didInserted +{ + [self triggerLayout]; +} + +- (void)triggerLayout +{ + WXAssertComponentThread(); + +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + //TODO: _isUseContainerWidth? + if (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH])) { + self.cssNode->style.dimensions[CSS_WIDTH] = ((WXScrollerComponent *)(self.supercomponent)).scrollerCSSNode->style.dimensions[CSS_WIDTH]; + } + + if ([self needsLayout]) { + layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT); + if ([WXLog logLevel] >= WXLogLevelDebug) { + print_css_node(self.cssNode, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN)); + } + } + } + +//#else + else + { + if (flexIsUndefined(self.flexCssNode->getStyleWidth())) { + self.flexCssNode->setStyleWidth(((WXScrollerComponent *)(self.supercomponent)).flexScrollerCSSNode->getStyleWidth(),NO); + } + + if ([self needsLayout]) { + std::pair<float, float> renderPageSize; + renderPageSize.first = self.weexInstance.frame.size.width; + renderPageSize.second = self.weexInstance.frame.size.height; + self.flexCssNode->calculateLayout(renderPageSize); + if ([WXLog logLevel] >= WXLogLevelDebug) { + + } + } + } +//#endif + + NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set]; + [self _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents]; + for (WXComponent *dirtyComponent in dirtyComponents) { + [self.weexInstance.componentManager _addUITask:^{ + [dirtyComponent _layoutDidFinish]; + }]; + } +} + +@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m deleted file mode 100644 index 8f71797..0000000 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m +++ /dev/null @@ -1,618 +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 "WXLog.h" -#import "WXUtility.h" -#import "WXComponent_internal.h" -#import "WXComponentManager.h" -#import "WXSDKInstance_private.h" - -#import "WXCellSlotComponent.h" -#import "WXRecycleListLayout.h" -#import "WXRecycleListComponent.h" -#import "WXRecycleListDataManager.h" -#import "WXRecycleListTemplateManager.h" -#import "WXRecycleListUpdateManager.h" -#import "WXBridgeManager.h" -#import "WXSDKManager.h" -#import "WXComponent+DataBinding.h" - -@interface WXRecycleListComponentView:UICollectionView -@end - -@implementation WXRecycleListComponentView -- (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; - } -} - -@end - -@interface WXRecycleListComponent () <WXRecycleListLayoutDelegate, WXRecycleListUpdateDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource> - -@end - -@implementation WXRecycleListComponent -{ - NSString *_templateSwitchKey; - NSString *_aliasKey; - NSString *_indexKey; - __weak UICollectionView *_collectionView; - - NSMutableDictionary *_sizeCache; - NSMutableDictionary *_stickyCache; - - NSUInteger _previousLoadMoreCellNumber; -} - -WX_EXPORT_METHOD(@selector(appendData:)) -WX_EXPORT_METHOD(@selector(appendRange:)) -WX_EXPORT_METHOD(@selector(insertData:data:)) -WX_EXPORT_METHOD(@selector(updateData:data:)) -WX_EXPORT_METHOD(@selector(removeData:count:)) -WX_EXPORT_METHOD(@selector(moveData:toIndex:)) -WX_EXPORT_METHOD(@selector(scrollTo:options:)) -WX_EXPORT_METHOD(@selector(insertRange:range:)) -WX_EXPORT_METHOD(@selector(setListData:)) - -- (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]) { - _dataManager = attributes[@"listData"]? [[WXRecycleListDataManager alloc] initWithData:attributes[@"listData"]] : [WXRecycleListDataManager new]; - _templateManager = [WXRecycleListTemplateManager new]; - _updateManager = [WXRecycleListUpdateManager new]; - _updateManager.delegate = self; - _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]]; - _aliasKey = [WXConvert NSString:attributes[@"alias"]]; - _indexKey = [WXConvert NSString:attributes[@"index"]]; - _sizeCache = [NSMutableDictionary dictionary]; - _stickyCache = [NSMutableDictionary dictionary]; - } - - return self; -} - -#pragma mark - WXComponent Methods - -- (UIView *)loadView -{ - WXRecycleListLayout *layout = [self recycleListLayout]; - return [[WXRecycleListComponentView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - _collectionView = (UICollectionView *)self.view; - _collectionView.allowsSelection = NO; - _collectionView.allowsMultipleSelection = NO; - _collectionView.dataSource = self; - _collectionView.delegate = self; - - _templateManager.collectionView = _collectionView; - _updateManager.collectionView = _collectionView; -} - -- (void)viewWillUnload -{ - [super viewWillUnload]; - - _collectionView.dataSource = nil; - _collectionView.delegate = nil; -} - -- (void)updateAttributes:(NSDictionary *)attributes -{ - [super updateAttributes:attributes]; - - if (attributes[@"listData"]) { - NSArray *listData = attributes[@"listData"]; - [self _updateListData:listData withCompletion:nil animation:NO]; - } - if (attributes[@"switch"]) { - _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]]; - } - if (attributes[@"alias"]) { - _aliasKey = [WXConvert NSString:attributes[@"alias"]]; - } - if (attributes[@"index"]) { - _indexKey = [WXConvert NSString:attributes[@"index"]]; - } - if (attributes[@"scrollDirection"]) { - WXScrollDirection newScrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical; - [self _updateScrollDirection:newScrollDirection]; - } -} - -- (CGPoint)absolutePositionForComponent:(WXComponent *)component -{ - CGPoint position = CGPointZero; - UIView *view = component->_view; - while (view) { - if ([view isKindOfClass:[UICollectionViewCell class]]) { - NSIndexPath *indexPath = [_collectionView indexPathForCell:(UICollectionViewCell *)view]; - if (!indexPath) { - return CGPointMake(NAN, NAN); - } - UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:indexPath]; - CGPoint cellOrigin = attributes.frame.origin; - position = CGPointMake(position.x + cellOrigin.x, - position.y + cellOrigin.y); - break; - } - position = CGPointMake(position.x + view.frame.origin.x, - position.y + view.frame.origin.y); - view = view.superview; - } - - return position; -} - -- (void)setContentSize:(CGSize)contentSize -{ - // Do Nothing -} - -- (void)adjustSticky -{ - // Do Nothing, sticky is adjusted by layout -} - -#pragma mark - Load More Event - -- (void)loadMore -{ - [super loadMore]; - - _previousLoadMoreCellNumber = [_collectionView numberOfItemsInSection:0]; -} - -- (BOOL)isNeedLoadMore -{ - BOOL superNeedLoadMore = [super isNeedLoadMore]; - return superNeedLoadMore && _previousLoadMoreCellNumber != [_collectionView numberOfItemsInSection:0]; -} - -- (void)resetLoadmore -{ - [super resetLoadmore]; - _previousLoadMoreCellNumber = 0; -} - -#pragma mark - Exported Component Methods - -- (void)appendData:(id)appendingData -{ - if (!appendingData){ - return; - } - NSMutableArray * newListData = [[_dataManager data] mutableCopy]; - [newListData addObject:appendingData]; -} - -- (void)appendRange:(NSArray*)data -{ - if (![data isKindOfClass:[NSArray class]]) { - WXLogError(@"wrong format of appending data:%@", data); - return; - } - - NSArray *oldData = [_dataManager data]; - [_updateManager updateWithAppendingData:data oldData:oldData completion:nil animation:NO]; -} - -- (void)setListData:(NSArray*)data -{ - if ([data count]) { - [_dataManager updateData:data]; - } -} -- (void)insertData:(NSUInteger)index data:(id)data -{ - // TODO: bring the update logic to UpdateManager - // TODO: update cell because index has changed - NSMutableArray *newListData = [[_dataManager data] mutableCopy]; - if (index <= newListData.count) { - [newListData insertObject:data atIndex:index]; - [_dataManager updateData:newListData]; - - NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; - - [UIView performWithoutAnimation:^{ - [self->_collectionView insertItemsAtIndexPaths:@[indexPath]]; - }]; - } -} - -- (void)updateComponentData:(NSString*)componentDataId componentData:(NSDictionary*)componentData callback:(NSString*)callbackId -{ - NSMutableDictionary * virtualComponentData = [[_dataManager virtualComponentDataWithId:componentDataId] mutableCopy]; - NSIndexPath * indexPath = virtualComponentData[@"indexPath"]; - if (!indexPath) { - return; - } - virtualComponentData = virtualComponentData?:[NSMutableDictionary new]; - [virtualComponentData addEntriesFromDictionary:componentData]; - [_dataManager updateVirtualComponentData:componentDataId data:[virtualComponentData copy]]; - virtualComponentData[@"@phase"] = @"update"; - virtualComponentData[@"callbackId"] = callbackId; - [self _updateDataForCellSlotAtIndexPath:indexPath data:virtualComponentData]; -} - -- (void)_updateDataForCellSlotAtIndexPath:(NSIndexPath*)indexPath data:(NSDictionary*)data -{ - if(!indexPath || !data) { - return; - } - WXPerformBlockOnMainThread(^{ - UICollectionViewCell * cellView = [self->_collectionView cellForItemAtIndexPath:indexPath]; - WXCellSlotComponent * cellSlotComponent = (WXCellSlotComponent*)cellView.wx_component; - if (cellSlotComponent) { - [self _updateBindingData:data forCell:cellSlotComponent atIndexPath:indexPath]; - } - // callback when update virtual component data success. - NSString * callbackId = data[@"callbackId"]; - if (callbackId) { - [[WXSDKManager bridgeMgr] callBack:self.weexInstance.instanceId funcId:callbackId params:@{@"result":@"success"}]; - } - }); -} - -- (void)updateData:(NSUInteger)index data:(id)data -{ - NSMutableArray * newListData = [[_dataManager data] mutableCopy]; - if (!data && index > [newListData count]) { - return; - } - NSIndexPath * indexPath = [NSIndexPath indexPathForRow:index inSection:0]; - NSDictionary * virtualComponentData = [_dataManager virtualComponentDataWithIndexPath:indexPath]; - if ([virtualComponentData[WXBindingOnceIdentify] boolValue]) { - return; - } - - // TODO: bring the update logic to UpdateManager - newListData[index] = data; - [_dataManager updateData:newListData]; - NSString* virtualComponentId = [_dataManager virtualComponentIdWithIndexPath:indexPath]; - [_dataManager updateVirtualComponentData:virtualComponentId data:data]; - NSMutableDictionary * newData = nil; - if (![data isKindOfClass:[NSDictionary class]]) { - newData = [NSMutableDictionary new]; - [newData setObject:@"data" forKey:data]; - data = newData; - } - newData = [data mutableCopy]; - newData[@"@phase"] = @"update"; - [self _updateDataForCellSlotAtIndexPath:indexPath data:[newData copy]]; -} - -- (void)insertRange:(NSInteger)index range:(NSArray*)data -{ - if (![data count]) { - WXLogDebug(@"ignore invalid insertRange"); - return; - } - - NSMutableArray * newListData = [[_dataManager data] mutableCopy]; - NSRange range = NSMakeRange(index,[data count]); - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range]; - [newListData insertObjects:data atIndexes:indexSet]; - [_dataManager updateData:newListData]; - [_collectionView reloadData]; -} - -- (void)removeData:(NSInteger)index count:(NSInteger)count -{ - // TODO: bring the update logic to UpdateManager - - NSMutableArray *newListData = [[_dataManager data] mutableCopy]; - if (index > [newListData count] || index + count - 1 > [newListData count]) { - - return; - } - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, count)]; - [newListData removeObjectsAtIndexes:indexSet]; - __block NSMutableArray<NSIndexPath*>* indexPaths = [NSMutableArray new]; - [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { - NSIndexPath* indexPath = [NSIndexPath indexPathForRow:idx inSection:0]; - [indexPaths addObject:indexPath]; - }]; - - [_dataManager updateData:newListData]; - [_dataManager deleteVirtualComponentAtIndexPaths:indexPaths]; - [UIView performWithoutAnimation:^{ - [self->_collectionView deleteItemsAtIndexPaths:indexPaths]; - [self->_collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; - }]; -} - -- (void)moveData:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex -{ - // TODO: bring the update logic to UpdateManager - NSMutableArray *newListData = [[_dataManager data] mutableCopy]; - id data = newListData[fromIndex]; - [newListData removeObjectAtIndex:fromIndex]; - [newListData insertObject:data atIndex:toIndex]; - [_dataManager updateData:newListData]; - - NSIndexPath *fromIndexPath = [NSIndexPath indexPathForItem:fromIndex inSection:0]; - NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:toIndex inSection:0]; - [UIView performWithoutAnimation:^{ - [self->_collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; - }]; -} - -- (void)scrollTo:(NSUInteger)index options:(NSDictionary *)options -{ - NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:index inSection:0]; - BOOL animated = options[@"animated"] ? [WXConvert BOOL:options[@"animated"]] : NO; - [_collectionView scrollToItemAtIndexPath:toIndexPath atScrollPosition:UICollectionViewScrollPositionTop animated:animated]; -} - -#pragma mark - WXComponent Internal Methods - -- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index -{ - [super _insertSubcomponent:subcomponent atIndex:index]; - - if ([subcomponent isKindOfClass:[WXCellSlotComponent class]]) { - WXCellSlotComponent *cell = (WXCellSlotComponent*)subcomponent; - [self.weexInstance.componentManager _addUITask:^{ - [_templateManager addTemplate:cell]; - }]; - - //TODO: update collection view if adding template - } -} - -#pragma mark - Private - -- (void)_updateBindingData:(id)data forCell:(WXCellSlotComponent *)cellComponent atIndexPath:(NSIndexPath *)indexPath -{ - id originalData = data; - if (![originalData isKindOfClass:[NSDictionary class]]) { - if (_aliasKey) { - NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; - [dictionary setObject:data forKey:_aliasKey]; - data = dictionary; - } else { - return; - } - } - - if (!data[@"indexPath"] || !data[@"recycleListComponentRef"]) { - NSMutableDictionary * dataNew = [data mutableCopy]; - dataNew[@"recycleListComponentRef"] = self.ref; - dataNew[@"indexPath"] = indexPath; - data = dataNew; - } - - if ([originalData isKindOfClass:[NSDictionary class]] && _aliasKey &&!data[@"phase"]) { - data = @{_aliasKey:data,@"aliasKey":_aliasKey}; - } - - if (_indexKey) { - NSMutableDictionary *dataNew = [data mutableCopy]; - dataNew[_indexKey] = @(indexPath.item); - data = dataNew; - } - -#ifdef DEBUG - NSDate *startTime = [NSDate date]; -#endif - - WXPerformBlockSyncOnComponentThread(^{ - [cellComponent updateCellData:[data copy]]; - }); -#ifdef DEBUG - double duration = -[startTime timeIntervalSinceNow] * 1000; - WXLogDebug(@"cell:%li update data time:%f", (long)indexPath.item, duration); -#endif - - NSValue *cachedSize = _sizeCache[indexPath]; - if (!cachedSize || !CGSizeEqualToSize([cachedSize CGSizeValue] , cellComponent.calculatedFrame.size)) { - _sizeCache[indexPath] = [NSValue valueWithCGSize:cellComponent.calculatedFrame.size]; - [_collectionView.collectionViewLayout invalidateLayout]; - } - NSNumber *cachedSticky = _stickyCache[indexPath]; - BOOL isSticky = cellComponent->_positionType == WXPositionTypeSticky; - if (!cachedSticky || [cachedSticky boolValue] != isSticky) { - _stickyCache[indexPath] = @(isSticky); - } -} - -- (void)_updateListData:(NSArray *)newData - withCompletion:(WXRecycleListUpdateCompletion)completion - animation:(BOOL)animation -{ - if (![newData isKindOfClass:[NSArray class]]) { - WXLogError(@"wrong format of list data:%@", newData); - completion(NO); - return; - } - - NSArray *oldData = [_dataManager data]; - [_updateManager updateWithNewData:newData oldData:oldData completion:completion animation:animation]; -} - -- (void)_updateScrollDirection:(WXScrollDirection)newScrollDirection -{ - WXRecycleListLayout *layout = [self recycleListLayout]; - _collectionView.collectionViewLayout = layout; -} - -- (WXRecycleListLayout *)recycleListLayout -{ - WXRecycleListLayout *layout = [WXRecycleListLayout new]; - layout.delegate = self; - // to show cells that original width / height is zero, otherwise cellForItemAtIndexPath will not be called - layout.minimumLineSpacing = 0.01; - layout.minimumInteritemSpacing = 0.01; - if (WXScrollDirectionHorizontal == self.scrollDirection) { - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; - } - return layout; -} - -#pragma mark - UICollectionViewDataSource - -- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView -{ - return 1; -} - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section -{ - return [_dataManager numberOfItems]; -} - -- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath -{ - // 1. get the data relating to the cell - id data = [_dataManager dataAtIndex:indexPath.row]; - - // 2. get the template type specified by data - NSString * templateType = [self templateType:indexPath]; - _templateManager.collectionView = collectionView; - if (!templateType) { - WXLogError(@"Each data should have a value for %@ to indicate template type", _templateSwitchKey); - return nil; - } - - // 3. dequeue a cell component by template type - UICollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:templateType forIndexPath:indexPath]; - WXCellSlotComponent *cellComponent = (WXCellSlotComponent *)cellView.wx_component; - if (!cellComponent) { - cellComponent = [_templateManager dequeueCellSlotWithType:templateType forIndexPath:indexPath]; - cellView.wx_component = cellComponent; - WXPerformBlockOnComponentThread(^{ - //TODO: How can we avoid this? - [super _insertSubcomponent:cellComponent atIndex:self.subcomponents.count]; - }); - } - - // 4. binding the data to the cell component - [self _updateBindingData:data forCell:cellComponent atIndexPath:indexPath]; - - // 5. Add cell component's view to content view. - UIView *contentView = cellComponent.view; - if (contentView.superview == cellView.contentView) { - return cellView; - } - - for (UIView *view in cellView.contentView.subviews) { - [view removeFromSuperview]; - } - [cellView.contentView addSubview:contentView]; - [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier]; - - WXLogDebug(@"Return cell view:%@, indexPath:%@", cellView, indexPath); - - [self handleAppear]; - - return cellView; -} - -- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - return nil; -} - -#pragma mark - UICollectionViewDelegate - -- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath); -} - -- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath); -} - -#pragma mark - UICollectionViewDelegateFlowLayout - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - NSValue *size = _sizeCache[indexPath]; - if (size) { - return [size CGSizeValue]; - } else { - - WXCellSlotComponent *cell = [_templateManager templateWithType:[self templateType:indexPath]]; - CGSize size = cell.calculatedFrame.size; - _sizeCache[indexPath] = [NSValue valueWithCGSize:size]; - return CGSizeMake(_collectionView.frame.size.width, size.height); - } -} - -#pragma mark - WXRecycleListLayoutDelegate - -- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForIndexPath:(NSIndexPath *)indexPath -{ - NSNumber *cachedSticky = _stickyCache[indexPath]; - if (cachedSticky) { - return [cachedSticky boolValue]; - } else { - return NO; - } -} - -#pragma mark - WXRecycleListUpdateDelegate - -- (void)updateManager:(WXRecycleListUpdateManager *)manager willUpdateData:(id)newData -{ - [_dataManager updateData:newData]; -} - -- (void)updateManager:(WXRecycleListUpdateManager *)manager didUpdateData:(id)newData withSuccess:(BOOL)finished -{ - -} - -- (NSString*)templateType:(NSIndexPath*)indexPath -{ - NSDictionary *data = [_dataManager dataAtIndex:indexPath.row]; - // default is first template. - NSString *templateType = [_templateManager topTemplate].templateCaseType; - if (!data || ![data isKindOfClass:[NSDictionary class]]) { - return templateType; - } - - if (_templateSwitchKey &&data[_templateSwitchKey]){ - templateType = data[_templateSwitchKey]; - } else if (data[WXDefaultRecycleTemplateType]){ - // read the default type. - templateType = data[WXDefaultRecycleTemplateType]; - } - return templateType; -} - -@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm new file mode 100644 index 0000000..551acd9 --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm @@ -0,0 +1,619 @@ +/* + * 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 "WXLog.h" +#import "WXUtility.h" +#import "WXComponent_internal.h" +#import "WXComponentManager.h" +#import "WXSDKInstance_private.h" + +#import "WXCellSlotComponent.h" +#import "WXRecycleListLayout.h" +#import "WXRecycleListComponent.h" +#import "WXRecycleListDataManager.h" +#import "WXRecycleListTemplateManager.h" +#import "WXRecycleListUpdateManager.h" +#import "WXBridgeManager.h" +#import "WXSDKManager.h" +#import "WXComponent+DataBinding.h" +#import "WXComponent+Layout.h" + +@interface WXRecycleListComponentView:UICollectionView +@end + +@implementation WXRecycleListComponentView +- (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; + } +} + +@end + +@interface WXRecycleListComponent () <WXRecycleListLayoutDelegate, WXRecycleListUpdateDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource> + +@end + +@implementation WXRecycleListComponent +{ + NSString *_templateSwitchKey; + NSString *_aliasKey; + NSString *_indexKey; + __weak UICollectionView *_collectionView; + + NSMutableDictionary *_sizeCache; + NSMutableDictionary *_stickyCache; + + NSUInteger _previousLoadMoreCellNumber; +} + +WX_EXPORT_METHOD(@selector(appendData:)) +WX_EXPORT_METHOD(@selector(appendRange:)) +WX_EXPORT_METHOD(@selector(insertData:data:)) +WX_EXPORT_METHOD(@selector(updateData:data:)) +WX_EXPORT_METHOD(@selector(removeData:count:)) +WX_EXPORT_METHOD(@selector(moveData:toIndex:)) +WX_EXPORT_METHOD(@selector(scrollTo:options:)) +WX_EXPORT_METHOD(@selector(insertRange:range:)) +WX_EXPORT_METHOD(@selector(setListData:)) + +- (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]) { + _dataManager = attributes[@"listData"]? [[WXRecycleListDataManager alloc] initWithData:attributes[@"listData"]] : [WXRecycleListDataManager new]; + _templateManager = [WXRecycleListTemplateManager new]; + _updateManager = [WXRecycleListUpdateManager new]; + _updateManager.delegate = self; + _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]]; + _aliasKey = [WXConvert NSString:attributes[@"alias"]]; + _indexKey = [WXConvert NSString:attributes[@"index"]]; + _sizeCache = [NSMutableDictionary dictionary]; + _stickyCache = [NSMutableDictionary dictionary]; + } + + return self; +} + +#pragma mark - WXComponent Methods + +- (UIView *)loadView +{ + WXRecycleListLayout *layout = [self recycleListLayout]; + return [[WXRecycleListComponentView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + _collectionView = (UICollectionView *)self.view; + _collectionView.allowsSelection = NO; + _collectionView.allowsMultipleSelection = NO; + _collectionView.dataSource = self; + _collectionView.delegate = self; + + _templateManager.collectionView = _collectionView; + _updateManager.collectionView = _collectionView; +} + +- (void)viewWillUnload +{ + [super viewWillUnload]; + + _collectionView.dataSource = nil; + _collectionView.delegate = nil; +} + +- (void)updateAttributes:(NSDictionary *)attributes +{ + [super updateAttributes:attributes]; + + if (attributes[@"listData"]) { + NSArray *listData = attributes[@"listData"]; + [self _updateListData:listData withCompletion:nil animation:NO]; + } + if (attributes[@"switch"]) { + _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]]; + } + if (attributes[@"alias"]) { + _aliasKey = [WXConvert NSString:attributes[@"alias"]]; + } + if (attributes[@"index"]) { + _indexKey = [WXConvert NSString:attributes[@"index"]]; + } + if (attributes[@"scrollDirection"]) { + WXScrollDirection newScrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical; + [self _updateScrollDirection:newScrollDirection]; + } +} + +- (CGPoint)absolutePositionForComponent:(WXComponent *)component +{ + CGPoint position = CGPointZero; + UIView *view = component->_view; + while (view) { + if ([view isKindOfClass:[UICollectionViewCell class]]) { + NSIndexPath *indexPath = [_collectionView indexPathForCell:(UICollectionViewCell *)view]; + if (!indexPath) { + return CGPointMake(NAN, NAN); + } + UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:indexPath]; + CGPoint cellOrigin = attributes.frame.origin; + position = CGPointMake(position.x + cellOrigin.x, + position.y + cellOrigin.y); + break; + } + position = CGPointMake(position.x + view.frame.origin.x, + position.y + view.frame.origin.y); + view = view.superview; + } + + return position; +} + +- (void)setContentSize:(CGSize)contentSize +{ + // Do Nothing +} + +- (void)adjustSticky +{ + // Do Nothing, sticky is adjusted by layout +} + +#pragma mark - Load More Event + +- (void)loadMore +{ + [super loadMore]; + + _previousLoadMoreCellNumber = [_collectionView numberOfItemsInSection:0]; +} + +- (BOOL)isNeedLoadMore +{ + BOOL superNeedLoadMore = [super isNeedLoadMore]; + return superNeedLoadMore && _previousLoadMoreCellNumber != [_collectionView numberOfItemsInSection:0]; +} + +- (void)resetLoadmore +{ + [super resetLoadmore]; + _previousLoadMoreCellNumber = 0; +} + +#pragma mark - Exported Component Methods + +- (void)appendData:(id)appendingData +{ + if (!appendingData){ + return; + } + NSMutableArray * newListData = [[_dataManager data] mutableCopy]; + [newListData addObject:appendingData]; +} + +- (void)appendRange:(NSArray*)data +{ + if (![data isKindOfClass:[NSArray class]]) { + WXLogError(@"wrong format of appending data:%@", data); + return; + } + + NSArray *oldData = [_dataManager data]; + [_updateManager updateWithAppendingData:data oldData:oldData completion:nil animation:NO]; +} + +- (void)setListData:(NSArray*)data +{ + if ([data count]) { + [_dataManager updateData:data]; + } +} +- (void)insertData:(NSUInteger)index data:(id)data +{ + // TODO: bring the update logic to UpdateManager + // TODO: update cell because index has changed + NSMutableArray *newListData = [[_dataManager data] mutableCopy]; + if (index <= newListData.count) { + [newListData insertObject:data atIndex:index]; + [_dataManager updateData:newListData]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; + + [UIView performWithoutAnimation:^{ + [self->_collectionView insertItemsAtIndexPaths:@[indexPath]]; + }]; + } +} + +- (void)updateComponentData:(NSString*)componentDataId componentData:(NSDictionary*)componentData callback:(NSString*)callbackId +{ + NSMutableDictionary * virtualComponentData = [[_dataManager virtualComponentDataWithId:componentDataId] mutableCopy]; + NSIndexPath * indexPath = virtualComponentData[@"indexPath"]; + if (!indexPath) { + return; + } + virtualComponentData = virtualComponentData?:[NSMutableDictionary new]; + [virtualComponentData addEntriesFromDictionary:componentData]; + [_dataManager updateVirtualComponentData:componentDataId data:[virtualComponentData copy]]; + virtualComponentData[@"@phase"] = @"update"; + virtualComponentData[@"callbackId"] = callbackId; + [self _updateDataForCellSlotAtIndexPath:indexPath data:virtualComponentData]; +} + +- (void)_updateDataForCellSlotAtIndexPath:(NSIndexPath*)indexPath data:(NSDictionary*)data +{ + if(!indexPath || !data) { + return; + } + WXPerformBlockOnMainThread(^{ + UICollectionViewCell * cellView = [self->_collectionView cellForItemAtIndexPath:indexPath]; + WXCellSlotComponent * cellSlotComponent = (WXCellSlotComponent*)cellView.wx_component; + if (cellSlotComponent) { + [self _updateBindingData:data forCell:cellSlotComponent atIndexPath:indexPath]; + } + // callback when update virtual component data success. + NSString * callbackId = data[@"callbackId"]; + if (callbackId) { + [[WXSDKManager bridgeMgr] callBack:self.weexInstance.instanceId funcId:callbackId params:@{@"result":@"success"}]; + } + }); +} + +- (void)updateData:(NSUInteger)index data:(id)data +{ + NSMutableArray * newListData = [[_dataManager data] mutableCopy]; + if (!data && index > [newListData count]) { + return; + } + NSIndexPath * indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + NSDictionary * virtualComponentData = [_dataManager virtualComponentDataWithIndexPath:indexPath]; + if ([virtualComponentData[WXBindingOnceIdentify] boolValue]) { + return; + } + + // TODO: bring the update logic to UpdateManager + newListData[index] = data; + [_dataManager updateData:newListData]; + NSString* virtualComponentId = [_dataManager virtualComponentIdWithIndexPath:indexPath]; + [_dataManager updateVirtualComponentData:virtualComponentId data:data]; + NSMutableDictionary * newData = nil; + if (![data isKindOfClass:[NSDictionary class]]) { + newData = [NSMutableDictionary new]; + [newData setObject:@"data" forKey:data]; + data = newData; + } + newData = [data mutableCopy]; + newData[@"@phase"] = @"update"; + [self _updateDataForCellSlotAtIndexPath:indexPath data:[newData copy]]; +} + +- (void)insertRange:(NSInteger)index range:(NSArray*)data +{ + if (![data count]) { + WXLogDebug(@"ignore invalid insertRange"); + return; + } + + NSMutableArray * newListData = [[_dataManager data] mutableCopy]; + NSRange range = NSMakeRange(index,[data count]); + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range]; + [newListData insertObjects:data atIndexes:indexSet]; + [_dataManager updateData:newListData]; + [_collectionView reloadData]; +} + +- (void)removeData:(NSInteger)index count:(NSInteger)count +{ + // TODO: bring the update logic to UpdateManager + + NSMutableArray *newListData = [[_dataManager data] mutableCopy]; + if (index > [newListData count] || index + count - 1 > [newListData count]) { + + return; + } + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, count)]; + [newListData removeObjectsAtIndexes:indexSet]; + __block NSMutableArray<NSIndexPath*>* indexPaths = [NSMutableArray new]; + [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSIndexPath* indexPath = [NSIndexPath indexPathForRow:idx inSection:0]; + [indexPaths addObject:indexPath]; + }]; + + [_dataManager updateData:newListData]; + [_dataManager deleteVirtualComponentAtIndexPaths:indexPaths]; + [UIView performWithoutAnimation:^{ + [self->_collectionView deleteItemsAtIndexPaths:indexPaths]; + [self->_collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; + }]; +} + +- (void)moveData:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex +{ + // TODO: bring the update logic to UpdateManager + NSMutableArray *newListData = [[_dataManager data] mutableCopy]; + id data = newListData[fromIndex]; + [newListData removeObjectAtIndex:fromIndex]; + [newListData insertObject:data atIndex:toIndex]; + [_dataManager updateData:newListData]; + + NSIndexPath *fromIndexPath = [NSIndexPath indexPathForItem:fromIndex inSection:0]; + NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:toIndex inSection:0]; + [UIView performWithoutAnimation:^{ + [self->_collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; + }]; +} + +- (void)scrollTo:(NSUInteger)index options:(NSDictionary *)options +{ + NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:index inSection:0]; + BOOL animated = options[@"animated"] ? [WXConvert BOOL:options[@"animated"]] : NO; + [_collectionView scrollToItemAtIndexPath:toIndexPath atScrollPosition:UICollectionViewScrollPositionTop animated:animated]; +} + +#pragma mark - WXComponent Internal Methods + +- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index +{ + [super _insertSubcomponent:subcomponent atIndex:index]; + + if ([subcomponent isKindOfClass:[WXCellSlotComponent class]]) { + WXCellSlotComponent *cell = (WXCellSlotComponent*)subcomponent; + [self.weexInstance.componentManager _addUITask:^{ + [_templateManager addTemplate:cell]; + }]; + + //TODO: update collection view if adding template + } +} + +#pragma mark - Private + +- (void)_updateBindingData:(id)data forCell:(WXCellSlotComponent *)cellComponent atIndexPath:(NSIndexPath *)indexPath +{ + id originalData = data; + if (![originalData isKindOfClass:[NSDictionary class]]) { + if (_aliasKey) { + NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; + [dictionary setObject:data forKey:_aliasKey]; + data = dictionary; + } else { + return; + } + } + + if (!data[@"indexPath"] || !data[@"recycleListComponentRef"]) { + NSMutableDictionary * dataNew = [data mutableCopy]; + dataNew[@"recycleListComponentRef"] = self.ref; + dataNew[@"indexPath"] = indexPath; + data = dataNew; + } + + if ([originalData isKindOfClass:[NSDictionary class]] && _aliasKey &&!data[@"phase"]) { + data = @{_aliasKey:data,@"aliasKey":_aliasKey}; + } + + if (_indexKey) { + NSMutableDictionary *dataNew = [data mutableCopy]; + dataNew[_indexKey] = @(indexPath.item); + data = dataNew; + } + +#ifdef DEBUG + NSDate *startTime = [NSDate date]; +#endif + + WXPerformBlockSyncOnComponentThread(^{ + [cellComponent updateCellData:[data copy]]; + }); +#ifdef DEBUG + double duration = -[startTime timeIntervalSinceNow] * 1000; + WXLogDebug(@"cell:%li update data time:%f", (long)indexPath.item, duration); +#endif + + NSValue *cachedSize = _sizeCache[indexPath]; + if (!cachedSize || !CGSizeEqualToSize([cachedSize CGSizeValue] , cellComponent.calculatedFrame.size)) { + _sizeCache[indexPath] = [NSValue valueWithCGSize:cellComponent.calculatedFrame.size]; + [_collectionView.collectionViewLayout invalidateLayout]; + } + NSNumber *cachedSticky = _stickyCache[indexPath]; + BOOL isSticky = cellComponent->_positionType == WXPositionTypeSticky; + if (!cachedSticky || [cachedSticky boolValue] != isSticky) { + _stickyCache[indexPath] = @(isSticky); + } +} + +- (void)_updateListData:(NSArray *)newData + withCompletion:(WXRecycleListUpdateCompletion)completion + animation:(BOOL)animation +{ + if (![newData isKindOfClass:[NSArray class]]) { + WXLogError(@"wrong format of list data:%@", newData); + completion(NO); + return; + } + + NSArray *oldData = [_dataManager data]; + [_updateManager updateWithNewData:newData oldData:oldData completion:completion animation:animation]; +} + +- (void)_updateScrollDirection:(WXScrollDirection)newScrollDirection +{ + WXRecycleListLayout *layout = [self recycleListLayout]; + _collectionView.collectionViewLayout = layout; +} + +- (WXRecycleListLayout *)recycleListLayout +{ + WXRecycleListLayout *layout = [WXRecycleListLayout new]; + layout.delegate = self; + // to show cells that original width / height is zero, otherwise cellForItemAtIndexPath will not be called + layout.minimumLineSpacing = 0.01; + layout.minimumInteritemSpacing = 0.01; + if (WXScrollDirectionHorizontal == self.scrollDirection) { + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + } + return layout; +} + +#pragma mark - UICollectionViewDataSource + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + return 1; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return [_dataManager numberOfItems]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + // 1. get the data relating to the cell + id data = [_dataManager dataAtIndex:indexPath.row]; + + // 2. get the template type specified by data + NSString * templateType = [self templateType:indexPath]; + _templateManager.collectionView = collectionView; + if (!templateType) { + WXLogError(@"Each data should have a value for %@ to indicate template type", _templateSwitchKey); + return nil; + } + + // 3. dequeue a cell component by template type + UICollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:templateType forIndexPath:indexPath]; + WXCellSlotComponent *cellComponent = (WXCellSlotComponent *)cellView.wx_component; + if (!cellComponent) { + cellComponent = [_templateManager dequeueCellSlotWithType:templateType forIndexPath:indexPath]; + cellView.wx_component = cellComponent; + WXPerformBlockOnComponentThread(^{ + //TODO: How can we avoid this? + [super _insertSubcomponent:cellComponent atIndex:self.subcomponents.count]; + }); + } + + // 4. binding the data to the cell component + [self _updateBindingData:data forCell:cellComponent atIndexPath:indexPath]; + + // 5. Add cell component's view to content view. + UIView *contentView = cellComponent.view; + if (contentView.superview == cellView.contentView) { + return cellView; + } + + for (UIView *view in cellView.contentView.subviews) { + [view removeFromSuperview]; + } + [cellView.contentView addSubview:contentView]; + [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier]; + + WXLogDebug(@"Return cell view:%@, indexPath:%@", cellView, indexPath); + + [self handleAppear]; + + return cellView; +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return nil; +} + +#pragma mark - UICollectionViewDelegate + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +{ + WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath); +} + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +{ + WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath); +} + +#pragma mark - UICollectionViewDelegateFlowLayout + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + NSValue *size = _sizeCache[indexPath]; + if (size) { + return [size CGSizeValue]; + } else { + + WXCellSlotComponent *cell = [_templateManager templateWithType:[self templateType:indexPath]]; + CGSize size = cell.calculatedFrame.size; + _sizeCache[indexPath] = [NSValue valueWithCGSize:size]; + return CGSizeMake(_collectionView.frame.size.width, size.height); + } +} + +#pragma mark - WXRecycleListLayoutDelegate + +- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForIndexPath:(NSIndexPath *)indexPath +{ + NSNumber *cachedSticky = _stickyCache[indexPath]; + if (cachedSticky) { + return [cachedSticky boolValue]; + } else { + return NO; + } +} + +#pragma mark - WXRecycleListUpdateDelegate + +- (void)updateManager:(WXRecycleListUpdateManager *)manager willUpdateData:(id)newData +{ + [_dataManager updateData:newData]; +} + +- (void)updateManager:(WXRecycleListUpdateManager *)manager didUpdateData:(id)newData withSuccess:(BOOL)finished +{ + +} + +- (NSString*)templateType:(NSIndexPath*)indexPath +{ + NSDictionary *data = [_dataManager dataAtIndex:indexPath.row]; + // default is first template. + NSString *templateType = [_templateManager topTemplate].templateCaseType; + if (!data || ![data isKindOfClass:[NSDictionary class]]) { + return templateType; + } + + if (_templateSwitchKey &&data[_templateSwitchKey]){ + templateType = data[_templateSwitchKey]; + } else if (data[WXDefaultRecycleTemplateType]){ + // read the default type. + templateType = data[WXDefaultRecycleTemplateType]; + } + return templateType; +} + +@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m deleted file mode 100644 index 7ca1de6..0000000 --- a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m +++ /dev/null @@ -1,738 +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 "WXRecyclerComponent.h" -#import "WXComponent_internal.h" -#import "WXSDKInstance_private.h" -#import "WXRecyclerDataController.h" -#import "WXRecyclerUpdateController.h" -#import "WXMultiColumnLayout.h" -#import "WXHeaderComponent.h" -#import "WXFooterComponent.h" -#import "WXCellComponent.h" -#import "WXAssert.h" -#import "WXConvert.h" -#import "WXUtility.h" -#import "WXMonitor.h" -#import "NSObject+WXSwizzle.h" -#import "WXComponent+Events.h" -#import "WXRecyclerDragController.h" - -static NSString * const kCollectionCellReuseIdentifier = @"WXRecyclerCell"; -static NSString * const kCollectionHeaderReuseIdentifier = @"WXRecyclerHeader"; -static float const kRecyclerNormalColumnGap = 32; - -typedef enum : NSUInteger { - WXRecyclerLayoutTypeMultiColumn, - WXRecyclerLayoutTypeFlex, - WXRecyclerLayoutTypeGrid, -} WXRecyclerLayoutType; - -@interface WXCollectionView : UICollectionView - -@end - -@implementation WXCollectionView - -- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index -{ - [super insertSubview:view atIndex:index]; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - [self.wx_component layoutDidFinish]; -} - -- (void)setContentOffset:(CGPoint)contentOffset -{ - // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary. - // When UICollectionView 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 - -@interface WXCollectionViewCell : UICollectionViewCell - -@end - -@implementation WXCollectionViewCell - -- (void)prepareForReuse -{ - [super prepareForReuse]; - - WXCellComponent *cellComponent = (WXCellComponent *)self.wx_component; - if (cellComponent.isRecycle && [cellComponent isViewLoaded] && [self.contentView.subviews containsObject:cellComponent.view]) { - [cellComponent _unloadViewWithReusing:YES]; - } -} - -@end - -@interface WXRecyclerComponent () <UICollectionViewDataSource, UICollectionViewDelegate, WXMultiColumnLayoutDelegate, WXRecyclerUpdateControllerDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate, WXRecyclerDragControllerDelegate> - -@property (nonatomic, strong, readonly) WXRecyclerDataController *dataController; -@property (nonatomic, strong, readonly) WXRecyclerUpdateController *updateController; -@property (nonatomic, weak, readonly) UICollectionView *collectionView; -@property (nonatomic, strong) WXRecyclerDragController *dragController; - -@end - -@implementation WXRecyclerComponent -{ - WXRecyclerLayoutType _layoutType; - UICollectionViewLayout *_collectionViewlayout; - - UIEdgeInsets _padding; - NSUInteger _previousLoadMoreCellNumber; -} - -- (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]) { - [self _fillPadding]; - - if ([type isEqualToString:@"waterfall"] || (attributes[@"layout"] && [attributes[@"layout"] isEqualToString:@"multi-column"])) { - // TODO: abstraction - _layoutType = WXRecyclerLayoutTypeMultiColumn; - CGFloat scaleFactor = weexInstance.pixelScaleFactor; - _collectionViewlayout = [WXMultiColumnLayout new]; - WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout; - layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeAuto]; - layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0] ? : [WXLength lengthWithInt:1 type:WXLengthTypeFixed]; - if (attributes[@"leftGap"]) { - layout.leftGap = [WXConvert WXPixelType:attributes[@"leftGap"] scaleFactor:scaleFactor]; - } - if (attributes[@"rightGap"]) { - layout.rightGap = [WXConvert WXPixelType:attributes[@"rightGap"] scaleFactor:scaleFactor]; - } - layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeNormal])]; - - layout.delegate = self; - } else { - _collectionViewlayout = [UICollectionViewLayout new]; - } - - _dataController = [WXRecyclerDataController new]; - _updateController = [WXRecyclerUpdateController new]; - _updateController.delegate = self; - [self fixFlicker]; - - if ([attributes[@"draggable"] boolValue]) { - // lazy load - _dragController = [WXRecyclerDragController new]; - _dragController.delegate = self; - if([attributes[@"dragTriggerType"] isEqual: @"pan"]){ - _dragController.dragTriggerType = WXRecyclerDragTriggerPan; - } - _dragController.isDragable = YES; - } - } - - return self; -} - -- (void)dealloc -{ - _collectionView.delegate = nil; - _collectionView.dataSource = nil; -} - -#pragma mark - Public Subclass Methods - -- (UIView *)loadView -{ - return [[WXCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_collectionViewlayout]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - _collectionView = (UICollectionView *)self.view; - _collectionView.allowsSelection = NO; - _collectionView.allowsMultipleSelection = NO; - _collectionView.dataSource = self; - _collectionView.delegate = self; - - [_collectionView registerClass:[WXCollectionViewCell class] forCellWithReuseIdentifier:kCollectionCellReuseIdentifier]; - [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:kCollectionSupplementaryViewKindHeader withReuseIdentifier:kCollectionHeaderReuseIdentifier]; - - _dragController.dragingCell = [[WXCollectionViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100/2.0f)]; - _dragController.collectionView = _collectionView; - - [self performUpdatesWithCompletion:^(BOOL finished) { - - }]; -} - -- (void)viewWillUnload -{ - [super viewWillUnload]; - - _collectionView.dataSource = nil; - _collectionView.delegate = nil; -} - -- (void)updateAttributes:(NSDictionary *)attributes -{ - [super updateAttributes:attributes]; - - if (_layoutType == WXRecyclerLayoutTypeMultiColumn) { - CGFloat scaleFactor = self.weexInstance.pixelScaleFactor; - WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout; - BOOL needUpdateLayout = NO; - - if ([attributes[@"draggable"] boolValue]) { - if (!_dragController) { // lazy load - _dragController = [WXRecyclerDragController new]; - _dragController.delegate = self; - } - if([attributes[@"dragTriggerType"] isEqual: @"pan"]){ - _dragController.dragTriggerType = WXRecyclerDragTriggerPan; - } - _dragController.isDragable = YES; - } else { - _dragController.isDragable = NO; - } - - if (attributes[@"columnWidth"]) { - layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor]; - needUpdateLayout = YES; - } - - if (attributes[@"columnCount"]) { - layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0]; - - needUpdateLayout = YES; - } - if (attributes[@"columnGap"]) { - layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor])]; - needUpdateLayout = YES; - } - if (attributes[@"leftGap"]) { - layout.leftGap = [WXConvert WXPixelType:attributes[@"leftGap"] scaleFactor:scaleFactor]; - } - if (attributes[@"rightGap"]) { - layout.rightGap = [WXConvert WXPixelType:attributes[@"rightGap"] scaleFactor:scaleFactor]; - } - - if (needUpdateLayout) { - for (WXComponent *component in self.subcomponents) { - [component setNeedsLayout]; - } - - [self.collectionView reloadData]; - [self.collectionView.collectionViewLayout invalidateLayout]; - } - } - -} - -- (void)setContentSize:(CGSize)contentSize -{ - // Do Nothing -} - -- (void)adjustSticky -{ - // Do Nothing, sticky is adjusted by layout -} - -#pragma mark - Private Subclass Methods - -- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles -{ - [super _updateStylesOnComponentThread:styles resetStyles:resetStyles isUpdateStyles:isUpdateStyles]; - - [self _fillPadding]; -} - -- (void)_handleFirstScreenTime -{ - // Do Nothingï¼ firstScreenTime is set by cellDidRendered: -} - -- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated -{ - CGPoint contentOffset = _collectionView.contentOffset; - CGFloat contentOffsetY = 0; - - CGRect rect; - while (component) { - if ([component isKindOfClass:[WXCellComponent class]]) { - NSIndexPath *toIndexPath = [self.dataController indexPathForCell:(WXCellComponent *)component]; - UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:toIndexPath]; - rect = attributes.frame; - break; - } - if ([component isKindOfClass:[WXHeaderComponent class]]) { - NSUInteger toIndex = [self.dataController indexForHeader:(WXHeaderComponent *)component]; - UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForSupplementaryElementOfKind:kCollectionSupplementaryViewKindHeader atIndexPath:[NSIndexPath indexPathWithIndex:toIndex]]; - rect = attributes.frame; - break; - } - contentOffsetY += component.calculatedFrame.origin.y; - component = component.supercomponent; - } - - contentOffsetY += rect.origin.y; - contentOffsetY += offset * self.weexInstance.pixelScaleFactor; - - if (_collectionView.contentSize.height >= _collectionView.frame.size.height && contentOffsetY > _collectionView.contentSize.height - _collectionView.frame.size.height) { - contentOffset.y = _collectionView.contentSize.height - _collectionView.frame.size.height; - } else { - contentOffset.y = contentOffsetY; - } - - [_collectionView setContentOffset:contentOffset animated:animated]; - -} - -- (void)performUpdatesWithCompletion:(void (^)(BOOL finished))completion -{ - WXAssertMainThread(); - - //TODO: support completion - - if (![self isViewLoaded]) { - completion(NO); - } - - NSArray *oldData = [self.dataController.sections copy]; - NSArray *newData = [self _sectionArrayFromComponents:self.subcomponents]; - - [_updateController performUpdatesWithNewData:newData oldData:oldData view:_collectionView]; -} - -- (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; - } - - [super _insertSubcomponent:subcomponent atIndex:index]; - - if (![subcomponent isKindOfClass:[WXHeaderComponent class]] - && ![subcomponent isKindOfClass:[WXCellComponent class]]) { - return; - } - - WXPerformBlockOnMainThread(^{ - [self performUpdatesWithCompletion:^(BOOL finished) { - - }]; - }); -} - -- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index -{ - //Here will not insert cell/header/footer's view again - if (![subcomponent isKindOfClass:[WXCellComponent class]] - && ![subcomponent isKindOfClass:[WXHeaderComponent class]] - && ![subcomponent isKindOfClass:[WXFooterComponent class]]) { - [super insertSubview:subcomponent atIndex:index]; - } -} - -#pragma mark - WXRecyclerUpdateControllerDelegate - -- (void)updateController:(WXRecyclerUpdateController *)controller willPerformUpdateWithNewData:(NSArray<WXSectionDataController *> *)newData -{ - if (newData) { - [self.dataController updateData:newData]; - } -} - -- (void)updateController:(WXRecyclerUpdateController *)controller didPerformUpdateWithFinished:(BOOL)finished -{ - -} - -#pragma mark - UICollectionViewDataSource - -- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView -{ - WXLogDebug(@"section number:%li", (long)[self.dataController numberOfSections]); - return [self.dataController numberOfSections]; -} - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section -{ - NSInteger numberOfItems = [self.dataController numberOfItemsInSection:section]; - - WXLogDebug(@"Number of items is %ld in section:%ld", (long)numberOfItems, (long)section); - - return numberOfItems; -} - -- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"Getting cell at indexPath:%@", indexPath); - - WXCollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:kCollectionCellReuseIdentifier forIndexPath:indexPath]; - - UIView *contentView = [self.dataController cellForItemAtIndexPath:indexPath]; - - cellView.wx_component = contentView.wx_component; - - [self.dragController goThroughAnchor:cellView.wx_component indexPath:indexPath]; - - if (contentView.superview == cellView.contentView) { - return cellView; - } - - for (UIView *view in cellView.contentView.subviews) { - [view removeFromSuperview]; - } - - [cellView.contentView addSubview:contentView]; - [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier]; - - return cellView; -} - -- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - UICollectionReusableView *reusableView = nil; - if ([kind isEqualToString:kCollectionSupplementaryViewKindHeader]) { - reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kCollectionHeaderReuseIdentifier forIndexPath:indexPath]; - UIView *contentView = [self.dataController viewForHeaderAtIndexPath:indexPath]; - if (contentView.superview != reusableView) { - for (UIView *view in reusableView.subviews) { - [view removeFromSuperview]; - } - - [reusableView addSubview:contentView]; - } - } - - return reusableView; -} - -#pragma mark - UICollectionViewDelegate - -- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath); -} - -- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath -{ - WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath); -} - -#pragma mark - WXMultiColumnLayoutDelegate - -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView insetForLayout:(UICollectionViewLayout *)collectionViewLayout -{ - return _padding; -} - -- (CGFloat)collectionView:(UICollectionView *)collectionView contentWidthForLayout:(UICollectionViewLayout *)collectionViewLayout -{ - return self.scrollerCSSNode->style.dimensions[CSS_WIDTH]; -} - -- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath -{ - CGSize itemSize = [self.dataController sizeForItemAtIndexPath:indexPath]; - return itemSize.height; -} - -- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section -{ - CGSize headerSize = [self.dataController sizeForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; - return headerSize.height; -} - -- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout hasHeaderInSection:(NSInteger)section -{ - return [self.dataController hasHeaderInSection:section]; -} - -- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForHeaderInSection:(NSInteger)section -{ - return [self.dataController isStickyForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; -} - -#pragma mark - WXHeaderRenderDelegate - -- (float)headerWidthForLayout:(WXHeaderComponent *)header -{ - if (_layoutType == WXRecyclerLayoutTypeMultiColumn) { - return ((WXMultiColumnLayout *)_collectionViewlayout).computedHeaderWidth; - } - - return 0.0; -} - -- (void)headerDidLayout:(WXHeaderComponent *)header -{ - WXPerformBlockOnMainThread(^{ - [self.collectionView.collectionViewLayout invalidateLayout]; - }); -} - -- (void)headerDidRemove:(WXHeaderComponent *)header -{ - WXPerformBlockOnMainThread(^{ - [self performUpdatesWithCompletion:^(BOOL finished) { - - }]; - }); -} - -#pragma mark - WXCellRenderDelegate - -- (float)containerWidthForLayout:(WXCellComponent *)cell -{ - if (_layoutType == WXRecyclerLayoutTypeMultiColumn) { - return ((WXMultiColumnLayout *)_collectionViewlayout).computedColumnWidth; - } - - return 0.0; -} - -- (void)cellDidLayout:(WXCellComponent *)cell -{ - BOOL previousLayoutComplete = cell.isLayoutComplete; - cell.isLayoutComplete = YES; - WXPerformBlockOnMainThread(^{ - if (previousLayoutComplete) { - [self.updateController reloadItemsAtIndexPath:[self.dataController indexPathForCell:cell]]; - } else { - [self performUpdatesWithCompletion:^(BOOL finished) { - }]; - } - }); -} - -- (void)cellDidRendered:(WXCellComponent *)cell -{ - if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) { - return; - } - - NSIndexPath *indexPath = [self.dataController indexPathForCell:cell]; - - UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath]; - CGRect cellRect = attributes.frame; - if (cellRect.origin.y + cellRect.size.height >= _collectionView.frame.size.height) { - WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance); - } - - if (self.weexInstance.onRenderProgress) { - CGRect renderRect = [_collectionView convertRect:cellRect toView:self.weexInstance.rootView]; - self.weexInstance.onRenderProgress(renderRect); - } -} - -- (void)cellDidRemove:(WXCellComponent *)cell -{ - if (cell.isLayoutComplete) { - WXPerformBlockOnMainThread(^{ - [self performUpdatesWithCompletion:^(BOOL finished) { - }]; - }); - } -} - -- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index -{ - if (cell.isLayoutComplete) { - WXPerformBlockOnMainThread(^{ - [self performUpdatesWithCompletion:^(BOOL finished) { - }]; - }); - } -} - -#pragma mark - Load More Event - -- (void)setLoadmoreretry:(NSUInteger)loadmoreretry -{ - if (loadmoreretry != self.loadmoreretry) { - _previousLoadMoreCellNumber = 0; - } - - [super setLoadmoreretry:loadmoreretry]; -} - -- (void)loadMore -{ - [super loadMore]; - - _previousLoadMoreCellNumber = [self totalNumberOfCells]; -} - -- (BOOL)isNeedLoadMore -{ - BOOL superNeedLoadMore = [super isNeedLoadMore]; - return superNeedLoadMore && _previousLoadMoreCellNumber != [self totalNumberOfCells]; -} - -- (NSUInteger)totalNumberOfCells -{ - NSUInteger cellNumber = 0; - NSUInteger sectionCount = [_collectionView numberOfSections]; - for (int section = 0; section < sectionCount; section ++) { - cellNumber += [_collectionView numberOfItemsInSection:section]; - } - - return cellNumber; -} - -- (void)resetLoadmore{ - [super resetLoadmore]; - _previousLoadMoreCellNumber = 0; -} - -#pragma mark - Private - -- (float)_floatValueForColumnGap:(WXLength *)gap -{ - if (gap.isNormal) { - return kRecyclerNormalColumnGap * self.weexInstance.pixelScaleFactor; - } else { - return gap.floatValue; - } -} - -- (void)_fillPadding -{ - UIEdgeInsets padding = { - WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]), - WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]), - WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]), - WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT]) - }; - - if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) { - _padding = padding; - [self setNeedsLayout]; - - for (WXComponent *component in self.subcomponents) { - [component setNeedsLayout]; - } - - if (_collectionView) { - WXPerformBlockOnMainThread(^{ - [_collectionView.collectionViewLayout invalidateLayout]; - }); - } - } -} - -- (NSArray<WXSectionDataController *> *)_sectionArrayFromComponents:(NSArray<WXComponent *> *)components -{ - NSMutableArray<WXSectionDataController *> *sectionArray = [NSMutableArray array]; - NSMutableArray<WXCellComponent *> *cellArray = [NSMutableArray array]; - WXSectionDataController *currentSection; - - for (int i = 0; i < components.count; i++) { - if (!currentSection) { - currentSection = [WXSectionDataController new]; - } - - WXComponent* component = components[i]; - - if ([component isKindOfClass:[WXHeaderComponent class]]) { - if (i != 0 && (currentSection.headerComponent || cellArray.count > 0)) { - currentSection.cellComponents = [cellArray copy]; - [sectionArray addObject:currentSection]; - currentSection = [WXSectionDataController new]; - [cellArray removeAllObjects]; - } - currentSection.headerComponent = (WXHeaderComponent *)component; - } else if ([component isKindOfClass:[WXCellComponent class]] - && ((WXCellComponent *)component).isLayoutComplete) { - [cellArray addObject:(WXCellComponent *)component]; - } else if ([component isKindOfClass:[WXFooterComponent class]]) { - currentSection.footerComponent = component; - } else { - continue; - } - } - - if (cellArray.count > 0 || currentSection.headerComponent) { - currentSection.cellComponents = [cellArray copy]; - [sectionArray addObject:currentSection]; - } - - return sectionArray; -} - -- (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:[WXCollectionView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)]; - }); -} - -#define mark dragControllerDelegate - -- (void)updateDataSource{ - NSMutableArray *oldComponents = [[NSMutableArray alloc] initWithArray:self.dataController.sections[self.dragController.startIndexPath.section].cellComponents]; - if(oldComponents.count > 1){ - WXCellComponent *startComponent = self.dataController.sections[self.dragController.startIndexPath.section].cellComponents[self.dragController.startIndexPath.item]; - [oldComponents removeObject:startComponent]; - [oldComponents insertObject:startComponent atIndex:self.dragController.targetIndexPath.item]; - self.dataController.sections[self.dragController.startIndexPath.section].cellComponents = oldComponents; - } -} - -- (void)dragFireEvent:(NSString *)eventName params:(NSDictionary *)params{ - [self fireEvent:eventName params:params]; -} - -- (void)fixedFlickerSelector -{ - // DO NOT delete this method. -} - -@end