* [ios] Supporting events and data updating for recycle-list component.
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/74cc3b33 Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/74cc3b33 Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/74cc3b33 Branch: refs/heads/0.16-dev Commit: 74cc3b331d00b5f333e215dba8f6200ccf1bf668 Parents: 877487a Author: yinfeng <cxf...@apache.org> Authored: Thu Sep 21 17:11:10 2017 +0800 Committer: yinfeng <cxf...@apache.org> Committed: Thu Sep 21 17:11:10 2017 +0800 ---------------------------------------------------------------------- ios/sdk/WeexSDK.xcodeproj/project.pbxproj | 8 + .../Component/RecycleList/WXCellSlotComponent.m | 4 +- .../RecycleList/WXComponent+DataBinding.h | 2 +- .../RecycleList/WXComponent+DataBinding.mm | 139 +++++++--- .../Component/RecycleList/WXJSASTParser.h | 5 + .../Component/RecycleList/WXJSASTParser.mm | 46 ++++ .../RecycleList/WXRecycleListComponent.h | 4 +- .../RecycleList/WXRecycleListComponent.m | 272 +++++++++++++++++-- .../RecycleList/WXRecycleListDataManager.h | 2 + .../RecycleList/WXRecycleListDataManager.m | 7 + .../Component/RecycleList/WXRecycleListLayout.h | 21 ++ .../Component/RecycleList/WXRecycleListLayout.m | 102 +++++++ .../RecycleList/WXRecycleListTemplateManager.m | 2 +- .../RecycleList/WXRecycleListUpdateManager.h | 22 ++ .../RecycleList/WXRecycleListUpdateManager.m | 233 ++++++++++++++++ .../Recycler/WXSectionDataController.m | 2 +- .../WeexSDK/Sources/Component/WXCellComponent.m | 2 +- .../Sources/Component/WXComponent_internal.h | 14 +- .../Sources/Component/WXScrollerComponent.h | 2 + .../Sources/Component/WXScrollerComponent.m | 13 +- .../WeexSDK/Sources/Events/WXComponent+Events.m | 5 +- .../WeexSDK/Sources/Manager/WXBridgeManager.h | 13 +- .../WeexSDK/Sources/Manager/WXBridgeManager.m | 10 + .../Sources/Manager/WXComponentManager.h | 1 + .../Sources/Manager/WXComponentManager.m | 36 ++- ios/sdk/WeexSDK/Sources/Model/WXComponent.m | 20 ++ ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h | 23 +- ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m | 84 +++++- 28 files changed, 1024 insertions(+), 70 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK.xcodeproj/project.pbxproj ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj index d597bf0..5911fec 100644 --- a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj +++ b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj @@ -188,6 +188,8 @@ 74B8BEFE1DC47B72004A6027 /* WXRootView.h in Headers */ = {isa = PBXBuildFile; fileRef = 74B8BEFC1DC47B72004A6027 /* WXRootView.h */; }; 74B8BEFF1DC47B72004A6027 /* WXRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 74B8BEFD1DC47B72004A6027 /* WXRootView.m */; }; 74B8BF011DC49AFE004A6027 /* WXRootViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 74B8BF001DC49AFE004A6027 /* WXRootViewTests.m */; }; + 74BA4AB31F70F4B600AC29BF /* WXRecycleListLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 74BA4AB11F70F4B600AC29BF /* WXRecycleListLayout.h */; }; + 74BA4AB41F70F4B600AC29BF /* WXRecycleListLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 74BA4AB21F70F4B600AC29BF /* WXRecycleListLayout.m */; }; 74BB5FB91DFEE81A004FC3DF /* WXMetaModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 74BB5FB71DFEE81A004FC3DF /* WXMetaModule.h */; }; 74BB5FBA1DFEE81A004FC3DF /* WXMetaModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 74BB5FB81DFEE81A004FC3DF /* WXMetaModule.m */; }; 74BF19F81F5139BB00AEE3D7 /* WXJSASTParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 74BF19F61F5139BB00AEE3D7 /* WXJSASTParser.h */; }; @@ -778,6 +780,8 @@ 74B8BEFC1DC47B72004A6027 /* WXRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXRootView.h; sourceTree = "<group>"; }; 74B8BEFD1DC47B72004A6027 /* WXRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXRootView.m; sourceTree = "<group>"; }; 74B8BF001DC49AFE004A6027 /* WXRootViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXRootViewTests.m; sourceTree = "<group>"; }; + 74BA4AB11F70F4B600AC29BF /* WXRecycleListLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXRecycleListLayout.h; sourceTree = "<group>"; }; + 74BA4AB21F70F4B600AC29BF /* WXRecycleListLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WXRecycleListLayout.m; sourceTree = "<group>"; }; 74BB5FB71DFEE81A004FC3DF /* WXMetaModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXMetaModule.h; sourceTree = "<group>"; }; 74BB5FB81DFEE81A004FC3DF /* WXMetaModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXMetaModule.m; sourceTree = "<group>"; }; 74BF19F61F5139BB00AEE3D7 /* WXJSASTParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXJSASTParser.h; sourceTree = "<group>"; }; @@ -1143,6 +1147,8 @@ 74CFDD401F45941E007A1A66 /* WXRecycleListTemplateManager.m */, 74CFDD431F459443007A1A66 /* WXRecycleListUpdateManager.h */, 74CFDD441F459443007A1A66 /* WXRecycleListUpdateManager.m */, + 74BA4AB11F70F4B600AC29BF /* WXRecycleListLayout.h */, + 74BA4AB21F70F4B600AC29BF /* WXRecycleListLayout.m */, 746B92391F46BE36009AE86B /* WXCellSlotComponent.h */, 746B923A1F46BE36009AE86B /* WXCellSlotComponent.m */, 7423EB4F1F4ADE30001662D1 /* WXComponent+DataBinding.h */, @@ -1649,6 +1655,7 @@ 741081261CEDB4EC001BC6E5 /* WXComponent_internal.h in Headers */, 77E65A191C155F25008B8775 /* WXScrollerComponent.h in Headers */, C4E375381E5FCBD3009B2D9C /* WXComponent+BoxShadow.h in Headers */, + 74BA4AB31F70F4B600AC29BF /* WXRecycleListLayout.h in Headers */, 742AD7311DF98C45007DC46C /* WXResourceRequestHandlerDefaultImpl.h in Headers */, C4F0127D1E1502A6003378D0 /* WXWebSocketHandler.h in Headers */, DC03ADBA1D508719003F76E7 /* WXTextAreaComponent.h in Headers */, @@ -2193,6 +2200,7 @@ 7408C48F1CFB345D000BCCD0 /* WXComponent+Events.m in Sources */, C4F012871E150307003378D0 /* WXWebSocketLoader.m in Sources */, C4D872211E5DDEDA00E39BC1 /* WXInnerLayer.m in Sources */, + 74BA4AB41F70F4B600AC29BF /* WXRecycleListLayout.m in Sources */, 745ED2DB1C5F2C7E002DB5A8 /* WXView.m in Sources */, DC03ADB91D508719003F76E7 /* WXTextAreaComponent.m in Sources */, 59A596231CB6311F0012CD52 /* WXNavigatorModule.m in Sources */, http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/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 index 2561d86..0cbd97b 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m @@ -76,9 +76,9 @@ static const NSString *WXDefaultRecycleTemplateType = @"WXDefaultRecycleTemplate if ([self needsLayout]) { layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT); -// if ([WXLog logLevel] >= WXLogLevelDebug) { + if ([WXLog logLevel] >= WXLogLevelDebug) { print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN); -// } + } } NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set]; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.h b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.h index ce3ab15..610f82f 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.h +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.h @@ -24,7 +24,7 @@ static const NSString *WXBindingMatchIdentify = @"[[match]]"; static const NSString *WXBindingRepeatIdentify = @"[[repeat]]"; static const NSString *WXBindingRepeatExprIdentify = @"@expression"; static const NSString *WXBindingRepeatIndexIdentify = @"@index"; -static const NSString *WXBindingRepeatLabelIdentify = @"@label"; +static const NSString *WXBindingRepeatLabelIdentify = @"@alias"; @interface WXComponent (DataBinding) http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.mm index 51f5724..b15ebd8 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.mm +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXComponent+DataBinding.mm @@ -27,8 +27,10 @@ #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" typedef enum : NSUInteger { - WXDataBindingTypeStyle = 0, + WXDataBindingTypeProp = 0, + WXDataBindingTypeStyle, WXDataBindingTypeAttributes, + WXDataBindingTypeEvents, WXDataBindingTypeCount, } WXDataBindingType; @@ -57,6 +59,19 @@ static JSContext *jsContext; return; } + if (templateComponent->_bindingProps) { + NSMutableDictionary *newData = [NSMutableDictionary dictionary]; + [templateComponent->_bindingProps enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, WXDataBindingBlock _Nonnull block, BOOL * _Nonnull stop) { + BOOL needUpdate; + id value = block(data, &needUpdate); + if (value) { + newData[key] = value; + } + }]; + + data = newData; + } + if (!_isRepeating) { WXDataBindingBlock repeatBlock = templateComponent->_bindingRepeat; if (repeatBlock) { @@ -80,7 +95,7 @@ static JSContext *jsContext; } for (int i = WXDataBindingTypeStyle; i < WXDataBindingTypeCount; i++) { - NSDictionary *bindingMap = i == WXDataBindingTypeStyle ? templateComponent->_bindingStyles : templateComponent->_bindingAttributes; + NSDictionary *bindingMap = i == WXDataBindingTypeStyle ? templateComponent->_bindingStyles : (i == WXDataBindingTypeAttributes ? templateComponent->_bindingAttributes : templateComponent->_bindingEvents); if (!bindingMap || bindingMap.count == 0) { continue; } @@ -98,8 +113,10 @@ static JSContext *jsContext; [self.weexInstance.componentManager startComponentTasks]; if (i == WXDataBindingTypeStyle) { [self.weexInstance.componentManager updateStyles:newAttributesOrStyles forComponent:self.ref]; - } else { + } else if (i == WXDataBindingTypeAttributes) { [self.weexInstance.componentManager updateAttributes:newAttributesOrStyles forComponent:self.ref]; + } else if (i == WXDataBindingTypeEvents) { + [self _addEventParams:newAttributesOrStyles]; } } } @@ -163,41 +180,78 @@ static JSContext *jsContext; } } -- (void)_storeBindingsWithStyles:(NSDictionary *)styles attributes:(NSDictionary *)attributes +- (void)_storeBindingsWithProps:(NSDictionary *)props styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSDictionary *)events { - if (!_bindingAttributes) { - _bindingAttributes = [NSMutableDictionary new]; + WXAssertComponentThread(); + + if (props.count > 0) { + if (!_bindingProps) { + _bindingProps = [NSMutableDictionary dictionary]; + } + [self _storeBindings:props type:WXDataBindingTypeProp]; } - if (!_bindingStyles) { - _bindingStyles = [NSMutableDictionary new]; + + if (styles.count > 0) { + if (!_bindingStyles) { + _bindingStyles = [NSMutableDictionary dictionary]; + } + [self _storeBindings:styles type:WXDataBindingTypeStyle]; + } + + if (attributes.count > 0) { + if (!_bindingAttributes) { + _bindingAttributes = [NSMutableDictionary dictionary]; + } + [self _storeBindings:attributes type:WXDataBindingTypeAttributes]; + } + + if (events.count > 0) { + if (!_bindingEvents) { + _bindingEvents = [NSMutableDictionary dictionary]; + } + [self _storeBindings:events type:WXDataBindingTypeEvents]; } - [self _storeBindings:styles type:WXDataBindingTypeStyle]; - [self _storeBindings:attributes type:WXDataBindingTypeAttributes]; } -- (void)_storeBindings:(NSDictionary *)attributesOrStyles type:(WXDataBindingType)type +- (void)_storeBindings:(NSDictionary *)stylesOrAttributesOrEvents type:(WXDataBindingType)type { WXAssertComponentThread(); - NSMutableDictionary *bindingMap = type == WXDataBindingTypeStyle ? _bindingStyles : _bindingAttributes; + NSMutableDictionary *bindingMap; + switch (type) { + case WXDataBindingTypeProp: + bindingMap = _bindingProps; + break; + case WXDataBindingTypeStyle: + bindingMap = _bindingStyles; + break; + case WXDataBindingTypeAttributes: + bindingMap = _bindingAttributes; + break; + case WXDataBindingTypeEvents: + bindingMap = _bindingEvents; + break; + default: + WXAssert(NO, @"error binding type:%z", type); + break; + } - // many-to-many relationship between attibuteName and bindingKey - [attributesOrStyles enumerateKeysAndObjectsUsingBlock:^(id _Nonnull attributeOrStyleName, id _Nonnull attributeOrStyle, BOOL * _Nonnull stop) { - if ([attributeOrStyle isKindOfClass:[NSDictionary class]] && attributeOrStyle[WXBindingIdentify]) { + [stylesOrAttributesOrEvents enumerateKeysAndObjectsUsingBlock:^(id _Nonnull name, id _Nonnull binding, BOOL * _Nonnull stop) { + if ([binding isKindOfClass:[NSDictionary class]] && binding[WXBindingIdentify]) { // {"attributeOrStyleName":{"@binding":"bindingExpression"} - NSString *bindingExpression = attributeOrStyle[WXBindingIdentify]; + NSString *bindingExpression = binding[WXBindingIdentify]; WXJSASTParser *parser = [WXJSASTParser parserWithScript:bindingExpression]; WXJSExpression *expression = [parser parseExpression]; WXDataBindingBlock block = [self bindingBlockWithExpression:expression]; - bindingMap[attributeOrStyleName] = block; - } else if ([attributeOrStyle isKindOfClass:[NSArray class]]) { + bindingMap[name] = block; + } else if ([binding isKindOfClass:[NSArray class]]) { // {"attributeOrStyleName":[..., "string", {"@binding":"bindingExpression"}, "string", {"@binding":"bindingExpression"}, ...] NSMutableDictionary *bindingBlocksForIndex = [NSMutableDictionary dictionary]; __block BOOL isBinding = NO; - [attributeOrStyle enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - if ([obj isKindOfClass:[NSDictionary class]] && obj[WXBindingIdentify]) { + [binding enumerateObjectsUsingBlock:^(id _Nonnull bindingInArray, NSUInteger idx, BOOL * _Nonnull stop) { + if ([bindingInArray isKindOfClass:[NSDictionary class]] && bindingInArray[WXBindingIdentify]) { isBinding = YES; - NSString *bindingExpression = obj[WXBindingIdentify]; + NSString *bindingExpression = bindingInArray[WXBindingIdentify]; WXJSASTParser *parser = [WXJSASTParser parserWithScript:bindingExpression]; WXJSExpression *expression = [parser parseExpression]; WXDataBindingBlock block = [self bindingBlockWithExpression:expression]; @@ -205,9 +259,9 @@ static JSContext *jsContext; } }]; - bindingMap[attributeOrStyleName] = ^NSString *(NSDictionary *data, BOOL *needUpdate) { - NSMutableArray *newArray = [attributeOrStyle mutableCopy]; - [attributeOrStyle enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + bindingMap[name] = ^id(NSDictionary *data, BOOL *needUpdate) { + NSMutableArray *newArray = [binding mutableCopy]; + [binding enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { BOOL _needUpdate = NO; WXDataBindingBlock block = bindingBlocksForIndex[@(idx)]; if (block) { @@ -221,21 +275,21 @@ static JSContext *jsContext; } }]; - return [newArray componentsJoinedByString:@""]; + return type == WXDataBindingTypeEvents ? newArray : [newArray componentsJoinedByString:@""]; }; } if (type == WXDataBindingTypeAttributes) { - if ([WXBindingMatchIdentify isEqualToString:attributeOrStyleName]) { - WXJSASTParser *parser = [WXJSASTParser parserWithScript:attributeOrStyle]; + if ([WXBindingMatchIdentify isEqualToString:name]) { + WXJSASTParser *parser = [WXJSASTParser parserWithScript:binding]; WXJSExpression *expression = [parser parseExpression]; _bindingMatch = [self bindingBlockWithExpression:expression]; - } else if ([WXBindingRepeatIdentify isEqualToString:attributeOrStyleName]) { - WXJSASTParser *parser = [WXJSASTParser parserWithScript:attributeOrStyle[WXBindingRepeatExprIdentify]]; + } else if ([WXBindingRepeatIdentify isEqualToString:name]) { + WXJSASTParser *parser = [WXJSASTParser parserWithScript:binding[WXBindingRepeatExprIdentify]]; WXJSExpression *expression = [parser parseExpression]; _bindingRepeat = [self bindingBlockWithExpression:expression]; - _repeatIndexIdentify = attributeOrStyle[WXBindingRepeatIndexIdentify]; - _repeatLabelIdentify = attributeOrStyle[WXBindingRepeatLabelIdentify]; + _repeatIndexIdentify = binding[WXBindingRepeatIndexIdentify]; + _repeatLabelIdentify = binding[WXBindingRepeatLabelIdentify]; } } }]; @@ -274,6 +328,7 @@ static JSContext *jsContext; id object = [self bindingBlockWithExpression:member->object](data, &objectNeedUpdate); if (member->computed) { id propertyName = [self bindingBlockWithExpression:member->property](data, &propertyNeedUpdate); + *needUpdate = objectNeedUpdate || propertyNeedUpdate; if ([object isKindOfClass:[NSDictionary class]] && [propertyName isKindOfClass:[NSString class]]) { return object[propertyName]; } else if ([object isKindOfClass:[NSArray class]] && [propertyName isKindOfClass:[NSNumber class]]) { @@ -281,10 +336,32 @@ static JSContext *jsContext; } } else { NSString *propertyName = [NSString stringWithCString:(((WXJSStringLiteral *)member->property)->value).c_str() encoding:[NSString defaultCStringEncoding]]; + *needUpdate = objectNeedUpdate; return object[propertyName]; } return nil; + } else if (expression->is<WXJSArrayExpression>()) { + WXJSArrayExpression *expr = (WXJSArrayExpression *)expression; + std::vector<WXJSExpression *> expressions = expr->expressions; + NSMutableArray *array = [NSMutableArray array]; + for(WXJSExpression *expr : expressions) { + if (expr == NULL) { + continue; + } + WXDataBindingBlock block = [self bindingBlockWithExpression:expr]; + *needUpdate = NO; + if (block) { + BOOL elementNeedUpdate; + id object = block(data, &elementNeedUpdate); + if (object) { + *needUpdate = *needUpdate || elementNeedUpdate; + [array addObject:object]; + } + } + } + + return array; } else if (expression->is<WXJSUnaryExpression>()) { WXJSUnaryExpression *expr = (WXJSUnaryExpression *)expression; std::string operator_ = expr->operator_; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.h b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.h index 597aa99..76ebff3 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.h +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.h @@ -19,6 +19,7 @@ #import <Foundation/Foundation.h> #include <string> +#include <vector> typedef enum : NSUInteger { WXJSExpressionTypeUnary, @@ -61,6 +62,10 @@ struct WXJSMemberExpression : WXJSExpression { bool computed; }; +struct WXJSArrayExpression : WXJSExpression { + std::vector<WXJSExpression *> expressions; +}; + struct WXJSUnaryExpression : WXJSExpression { std::string operator_; bool prefix; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm index eaa8394..7e7a565 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXJSASTParser.mm @@ -762,6 +762,13 @@ static int binaryPrecedence(WXJSToken *token) { int type = _lookahead->type; + if (type == WXJSTokenTypePunctuator) { + if (_lookahead->value == "[") { + return [self parseArrayExpression]; + } else if (_lookahead->value == "(") { + return [self parseGroupExpression]; + } + } if (type == WXJSTokenTypeIdentifier) { WXJSIdentifier *identifier = new WXJSIdentifier(); identifier->name = [self nextToken]->value; @@ -776,6 +783,45 @@ static int binaryPrecedence(WXJSToken *token) } } +- (WXJSArrayExpression *)parseArrayExpression +{ + std::vector<WXJSExpression *> expressions; + + [self expect:"["]; + + while (![self match:"]"]) { + if ([self match:","]) { + [self nextToken]; + expressions.push_back(NULL); + } else { + expressions.push_back([self parseConditionalExpression]); + + if (![self match:"]"]) { + [self expect:","]; + } + } + } + + [self expect:"]"]; + + WXJSArrayExpression *array = new WXJSArrayExpression(); + array->expressions = expressions; + + return array; +} + +- (WXJSExpression *)parseGroupExpression +{ + WXJSExpression *expr; + [self expect:"("]; + + expr = [self parseConditionalExpression]; + + [self expect:")"]; + + return expr; +} + - (WXJSExpression *)parseMemberExpression { http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.h b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.h index 0e038a7..a072752 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.h +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.h @@ -18,8 +18,8 @@ */ #import <Foundation/Foundation.h> -#import "WXComponent.h" +#import "WXScrollerComponent.h" -@interface WXRecycleListComponent : WXComponent <UICollectionViewDelegateFlowLayout, UICollectionViewDataSource> +@interface WXRecycleListComponent : WXScrollerComponent @end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/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 index 4d119c2..71a52f5 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m @@ -24,12 +24,13 @@ #import "WXSDKInstance_private.h" #import "WXCellSlotComponent.h" +#import "WXRecycleListLayout.h" #import "WXRecycleListComponent.h" #import "WXRecycleListDataManager.h" #import "WXRecycleListTemplateManager.h" #import "WXRecycleListUpdateManager.h" -@interface WXRecycleListComponent () +@interface WXRecycleListComponent () <WXRecycleListLayoutDelegate, WXRecycleListUpdateDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource> @end @@ -40,11 +41,23 @@ WXRecycleListUpdateManager *_updateManager; NSString *_templateKey; + NSString *_aliasKey; + NSString *_indexKey; __weak UICollectionView *_collectionView; NSMutableDictionary *_sizeCache; + NSMutableDictionary *_stickyCache; + + NSUInteger _previousLoadMoreCellNumber; } +WX_EXPORT_METHOD(@selector(appendData:)) +WX_EXPORT_METHOD(@selector(insertData:atIndex:)) +WX_EXPORT_METHOD(@selector(updateData:atIndex:)) +WX_EXPORT_METHOD(@selector(removeData:)) +WX_EXPORT_METHOD(@selector(moveData:toIndex:)) +WX_EXPORT_METHOD(@selector(scrollTo:options:)) + - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles @@ -56,8 +69,12 @@ _dataManager = [[WXRecycleListDataManager alloc] initWithData:attributes[@"listData"]]; _templateManager = [WXRecycleListTemplateManager new]; _updateManager = [WXRecycleListUpdateManager new]; + _updateManager.delegate = self; _templateKey = [WXConvert NSString:attributes[@"templateKey"]] ? : @"templateType"; + _aliasKey = [WXConvert NSString:attributes[@"alias"]]; + _indexKey = [WXConvert NSString:attributes[@"index"]]; _sizeCache = [NSMutableDictionary dictionary]; + _stickyCache = [NSMutableDictionary dictionary]; } return self; @@ -67,7 +84,11 @@ - (UIView *)loadView { - UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new]; + 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; return [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; } @@ -83,7 +104,6 @@ _templateManager.collectionView = _collectionView; _updateManager.collectionView = _collectionView; -// [_updateManager reload]; } - (void)viewWillUnload @@ -94,6 +114,38 @@ _collectionView.delegate = nil; } +- (void)updateAttributes:(NSDictionary *)attributes +{ + if (attributes[@"listData"]) { + NSArray *listData = attributes[@"listData"]; + [self _updateListData:listData withCompletion:nil animation:NO]; + } +} + +- (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 @@ -104,6 +156,119 @@ // 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:(NSArray *)appendingData +{ + if (![appendingData isKindOfClass:[NSArray class]]) { + WXLogError(@"wrong format of appending data:%@", appendingData); + return; + } + + NSArray *oldData = [_dataManager data]; + [_updateManager updateWithAppendingData:appendingData oldData:oldData completion:nil animation:NO]; +} + +- (void)insertData:(id)data atIndex:(NSUInteger)index +{ + // 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:^{ + [_collectionView insertItemsAtIndexPaths:@[indexPath]]; + }]; + } +} + +- (void)updateData:(id)data atIndex:(NSUInteger)index +{ + // TODO: bring the update logic to UpdateManager + NSMutableArray *newListData = [[_dataManager data] mutableCopy]; + if (index < newListData.count) { + newListData[index] = data; + [_dataManager updateData:newListData]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; + UICollectionViewCell *cellView = [_collectionView cellForItemAtIndexPath:indexPath]; + WXCellSlotComponent *cellComponent = (WXCellSlotComponent *)cellView.wx_component; + if (cellComponent) { + [self _updateBindingData:data forCell:cellComponent atIndexPath:indexPath]; + } + } +} + +- (void)removeData:(NSArray *)indexes +{ + // TODO: bring the update logic to UpdateManager + NSMutableArray *newListData = [[_dataManager data] mutableCopy]; + NSMutableIndexSet *indexSet = [NSMutableIndexSet new]; + NSMutableArray *indexPaths = [NSMutableArray array]; + for (NSNumber *index in indexes) { + if ([index unsignedIntegerValue] >= newListData.count) { + WXLogError(@"invalid remove index:%@", index); + continue; + } + [indexSet addIndex:[index unsignedIntegerValue]]; + [indexPaths addObject:[NSIndexPath indexPathForItem:[index unsignedIntegerValue] inSection:0]]; + } + + [newListData removeObjectsAtIndexes:indexSet]; + [_dataManager updateData:newListData]; + [UIView performWithoutAnimation:^{ + [_collectionView deleteItemsAtIndexPaths:indexPaths]; + }]; +} + +- (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:^{ + [_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 - WXComonent Internal Methods - (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index @@ -120,6 +285,56 @@ } } +#pragma mark - Private + +- (void)_updateBindingData:(NSDictionary *)data forCell:(WXCellSlotComponent *)cellComponent atIndexPath:(NSIndexPath *)indexPath +{ + if (_aliasKey) { + data = @{_aliasKey:data}; + } + if (_indexKey) { + NSMutableDictionary *dataNew = [data mutableCopy]; + dataNew[_indexKey] = @(indexPath.item); + data = dataNew; + } + +#ifdef DEBUG + NSDate *startTime = [NSDate date]; +#endif + WXPerformBlockSyncOnComponentThread(^{ + [cellComponent updateCellData:data]; + }); +#ifdef DEBUG + double duration = -[startTime timeIntervalSinceNow] * 1000; + WXLogDebug(@"cell:%zi update data time:%f", 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]; +} + #pragma mark - UICollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView @@ -137,7 +352,7 @@ // 1. get the data relating to the cell NSDictionary *data = [_dataManager dataAtIndex:indexPath.row]; if (!data || ![data isKindOfClass:[NSDictionary class]]) { - WXLogError(@"No data or wrong data format for index:%zd, data:%@", indexPath.row, data); + WXLogError(@"No data or wrong data format for index:%zd, data:%@", indexPath.item, data); return nil; } @@ -160,21 +375,9 @@ }); } - // 4. binding the data to the cell component - WXPerformBlockOnComponentThread(^{ - [cellComponent updateCellData:data]; - WXPerformBlockOnMainThread(^{ - NSValue *cacheSize = _sizeCache[indexPath]; - if (!cacheSize || !CGSizeEqualToSize([cacheSize CGSizeValue] , cellComponent.calculatedFrame.size)) { - _sizeCache[indexPath] = [NSValue valueWithCGSize:cellComponent.calculatedFrame.size]; - [UIView performWithoutAnimation:^{ - [_collectionView reloadItemsAtIndexPaths:@[indexPath]]; - }]; - } - }); - }); - + [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) { @@ -187,6 +390,12 @@ [cellView.contentView addSubview:contentView]; [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier]; + WXLogDebug(@"Return cell view:%@, indexPath:%@", cellView, indexPath); + + dispatch_async(dispatch_get_main_queue(), ^{ + [self handleAppear]; + }); + return cellView; } @@ -208,6 +417,7 @@ } #pragma mark - UICollectionViewDelegateFlowLayout + - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { NSValue *size = _sizeCache[indexPath]; @@ -218,8 +428,32 @@ WXCellSlotComponent *cell = [_templateManager templateWithType:data[_templateKey]]; CGSize size = cell.calculatedFrame.size; _sizeCache[indexPath] = [NSValue valueWithCGSize:size]; - return 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 +{ + +} + @end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.h b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.h index c340f9d..fc053c5 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.h +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.h @@ -25,6 +25,8 @@ - (void)updateData:(NSArray *)data; +- (NSArray *)data; + - (NSDictionary *)dataAtIndex:(NSInteger)index; - (NSInteger)numberOfItems; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.m index 49ed216..8d27171 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.m +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListDataManager.m @@ -47,6 +47,13 @@ _data = data; } +- (NSArray *)data +{ + WXAssertMainThread(); + + return _data; +} + - (NSDictionary *)dataAtIndex:(NSInteger)index { WXAssertMainThread(); http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.h b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.h new file mode 100644 index 0000000..04e65ea --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.h @@ -0,0 +1,21 @@ +// +// WXRecycleListLinearLayout.h +// WeexSDK +// +// Created by yinfeng on 2017/9/19. +// Copyright © 2017年 taobao. All rights reserved. +// + +#import <UIKit/UIKit.h> + +@protocol WXRecycleListLayoutDelegate + +- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForIndexPath:(NSIndexPath *)indexPath; + +@end + +@interface WXRecycleListLayout : UICollectionViewFlowLayout + +@property (nonatomic, weak) id<WXRecycleListLayoutDelegate> delegate; + +@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.m new file mode 100644 index 0000000..e5bc8c3 --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListLayout.m @@ -0,0 +1,102 @@ +// +// WXRecycleListLinearLayout.m +// WeexSDK +// +// Created by yinfeng on 2017/9/19. +// Copyright © 2017年 taobao. All rights reserved. +// + +#import "WXRecycleListLayout.h" + +@implementation WXRecycleListLayout +{ + NSMutableDictionary<NSNumber *, UICollectionViewLayoutAttributes *> *_stickyCellsAttributes; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect +{ + NSArray *cellAttributes = [super layoutAttributesForElementsInRect:rect]; + NSMutableDictionary *lastCellsAttributes = [NSMutableDictionary dictionary]; + + __block NSInteger currentStickyIndex = -1; + [cellAttributes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + UICollectionViewLayoutAttributes *attributes = obj; + NSIndexPath *indexPath = attributes.indexPath; + if ([self.delegate collectionView:self.collectionView layout:self isNeedStickyForIndexPath:indexPath]) { + if (!_stickyCellsAttributes) { + _stickyCellsAttributes = [NSMutableDictionary dictionary]; + } + + currentStickyIndex = indexPath.item; + [_stickyCellsAttributes setObject:attributes forKey:@(indexPath.item)]; + } else { + [_stickyCellsAttributes removeObjectForKey:@(indexPath.item)]; + + // bottom cell above sticky cell + UICollectionViewLayoutAttributes *currentLastCell = [lastCellsAttributes objectForKey:@(currentStickyIndex)]; + if (!currentLastCell || indexPath.item > currentLastCell.indexPath.item) { + [lastCellsAttributes setObject:obj forKey:@(currentStickyIndex)]; + } + } + + attributes.zIndex = 1; + }]; + + NSMutableArray *newCellAttributes = [cellAttributes mutableCopy]; + [lastCellsAttributes enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + UICollectionViewLayoutAttributes *attributes = obj; + + UICollectionViewLayoutAttributes *stickyCell = _stickyCellsAttributes[key]; + if (!stickyCell) { + NSInteger item = attributes.indexPath.item; + while (item >= 0) { + if (_stickyCellsAttributes[@(item)]) { + stickyCell = [self.collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0]]; + break; + } else { + item --; + } + } + } + + if (stickyCell) { + [newCellAttributes addObject:stickyCell]; + [self _adjustStickyForCellAttributes:stickyCell lastCellAttributes:attributes]; + } + }]; + + return newCellAttributes; +} + +- (void)_adjustStickyForCellAttributes:(UICollectionViewLayoutAttributes *)cell + lastCellAttributes:(UICollectionViewLayoutAttributes *)lastCell +{ + cell.zIndex = 99; + cell.hidden = NO; + + CGFloat maxY = CGRectGetMaxY(lastCell.frame) - cell.frame.size.height; + CGFloat minY = CGRectGetMinY(self.collectionView.bounds) + self.collectionView.contentInset.top; + CGFloat y = MIN(MAX(minY, cell.frame.origin.y), maxY); + +// NSLog(@"%zi : %zi, %.1f, %.1f, %.1f, %.1f", cell.indexPath.item, lastCell.indexPath.item, maxY, minY, cell.frame.origin.y, y); + + CGPoint origin = cell.frame.origin; + origin.y = y; + + cell.frame = (CGRect){ + origin, + cell.frame.size + }; +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds +{ + if (_stickyCellsAttributes.count > 0) { + // always return yes to trigger resetting sticky header's frame. + return YES; + } + + return [super shouldInvalidateLayoutForBoundsChange:newBounds]; +} + +@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListTemplateManager.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListTemplateManager.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListTemplateManager.m index 0ac528e..87ecd90 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListTemplateManager.m +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListTemplateManager.m @@ -87,7 +87,7 @@ - (void)_registerCellClassForReuseID:(NSString *)templateID { WXLogDebug(@"register cell class for template id:%@", templateID); - //TODO: register class updateTemplateId + //TODO: register class update TemplateId [_collectionView registerClass:[WXReusableCollectionViewCell class] forCellWithReuseIdentifier:templateID]; } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.h b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.h index c96bbcd..1753b2b 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.h +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.h @@ -19,10 +19,32 @@ #import <Foundation/Foundation.h> +typedef void(^WXRecycleListUpdateCompletion)(BOOL isFinished); +@class WXRecycleListUpdateManager; + +@protocol WXRecycleListUpdateDelegate + +- (void)updateManager:(WXRecycleListUpdateManager *)manager willUpdateData:(id)newData; + +- (void)updateManager:(WXRecycleListUpdateManager *)manager didUpdateData:(id)newData withSuccess:(BOOL)finished; + +@end + @interface WXRecycleListUpdateManager : NSObject @property (nonatomic, weak) UICollectionView *collectionView; +@property (nonatomic, weak) id<WXRecycleListUpdateDelegate> delegate; - (void)reload; +- (void)updateWithNewData:(NSArray *)newData + oldData:(NSArray *)oldData + completion:(WXRecycleListUpdateCompletion)completion + animation:(BOOL)isAnimated; + +- (void)updateWithAppendingData:(NSArray *)appendingData + oldData:(NSArray *)oldData + completion:(WXRecycleListUpdateCompletion)completion + animation:(BOOL)isAnimated; + @end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.m index 1395607..7ad4caa 100644 --- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.m +++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListUpdateManager.m @@ -18,12 +18,245 @@ */ #import "WXRecycleListUpdateManager.h" +#import "WXLog.h" +#import "WXAssert.h" +#import "WXDiffUtil.h" + +@interface WXRecycleListDiffResult : NSObject + +@property (nonatomic, strong, readonly) NSMutableSet<NSIndexPath *> *deleteIndexPaths; +@property (nonatomic, strong, readonly) NSMutableSet<NSIndexPath *> *insertIndexPaths; +@property (nonatomic, strong, readonly) NSMutableSet<NSIndexPath *> *reloadIndexPaths; + +- (BOOL)hasChanges; + +@end + +@implementation WXRecycleListDiffResult + +- (instancetype)initWithInsertIndexPaths:(NSMutableSet<NSIndexPath *> *)insertIndexPaths + deleteIndexPaths:(NSMutableSet<NSIndexPath *> *)deleteIndexPaths + reloadIndexPaths:(NSMutableSet<NSIndexPath *> *)reloadIndexPaths +{ + if (self = [super init]) { + _insertIndexPaths = [insertIndexPaths copy]; + _deleteIndexPaths = [deleteIndexPaths copy]; + _reloadIndexPaths = [reloadIndexPaths copy]; + } + + return self; +} + +- (BOOL)hasChanges +{ + return _insertIndexPaths.count > 0 || _deleteIndexPaths.count > 0 || _reloadIndexPaths.count > 0; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; insert index paths: %@; delete index paths: %@; reload index paths: %@", NSStringFromClass([self class]), self, _insertIndexPaths, _deleteIndexPaths, _reloadIndexPaths]; +} + +@end + +@interface WXRecycleListUpdateManager () + +@property (nonatomic, copy) NSArray *newerData; +@property (nonatomic, copy) NSArray *appendingData; +@property (nonatomic, copy) NSArray *olderData; +@property (nonatomic, assign) BOOL isUpdating; +@property (nonatomic, strong) NSMutableArray *completions; + +@property (nonatomic, strong) NSMutableSet<NSIndexPath *> *reloadIndexPaths; + +@end @implementation WXRecycleListUpdateManager +- (instancetype)init +{ + if (self = [super init]) { + _completions = [NSMutableArray array]; + } + + return self; +} + - (void)reload { [_collectionView reloadData]; } +- (void)updateWithNewData:(NSArray *)newData + oldData:(NSArray *)oldData + completion:(WXRecycleListUpdateCompletion)completion + animation:(BOOL)isAnimated +{ + WXAssertMainThread(); + + if (!_collectionView) { + WXLogError(@"Update list with no collection view"); + completion(NO); + return; + } + + self.newerData = newData; + self.appendingData = nil; + self.olderData = oldData; + + if (completion) { + [_completions addObject:completion]; + } + + [self checkUpdates]; +} + +- (void)updateWithAppendingData:(NSArray *)appendingData + oldData:(NSArray *)oldData + completion:(WXRecycleListUpdateCompletion)completion + animation:(BOOL)isAnimated +{ + if (!_collectionView) { + WXLogError(@"Update list with no collection view"); + completion(NO); + return; + } + + self.appendingData = appendingData; + self.olderData = oldData; + + if (completion) { + [_completions addObject:completion]; + } + + [self checkUpdates]; +} + + +- (void)checkUpdates +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.isUpdating) { + return ; + } + + [self performBatchUpdates]; + }); +} + +- (void)performBatchUpdates +{ + WXAssertMainThread(); + WXAssert(!self.isUpdating, @"Can not perform updates while an updating is being performed"); + + UICollectionView *collectionView = self.collectionView; + if (!collectionView) { + return; + } + + NSArray *newData = [self.newerData copy]; + NSArray *oldData = [self.olderData copy]; + NSArray *appendingData = [self.appendingData copy]; + //TODO use completionBlocks +// NSArray *completionBlocks = [self.completions copy]; + + [self cleanup]; + + WXDiffResult *diffResult; + if (appendingData) { + newData = [oldData arrayByAddingObjectsFromArray:appendingData]; + NSIndexSet *inserts = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(oldData.count, appendingData.count)]; + diffResult = [[WXDiffResult alloc] initWithInserts:inserts deletes:nil updates:nil]; + } else if (newData){ + diffResult = [WXDiffUtil diffWithMinimumDistance:newData oldArray:oldData]; + } + + WXRecycleListDiffResult *recycleListDiffResult = [self recycleListUpdatesByDiffResult:diffResult]; + + if (![diffResult hasChanges] && self.reloadIndexPaths.count == 0) { + return; + } + + void (^updates)() = [^{ + [self.delegate updateManager:self willUpdateData:newData]; + [UIView setAnimationsEnabled:NO]; + NSLog(@"UICollectionView update:%@", recycleListDiffResult); + [self applyUpdateWithDiffResult:recycleListDiffResult]; + } copy]; + + void (^completion)(BOOL) = [^(BOOL finished) { + [UIView setAnimationsEnabled:YES]; + self.isUpdating = NO; + [self.delegate updateManager:self didUpdateData:newData withSuccess:finished]; + + [self.reloadIndexPaths removeAllObjects]; + [self checkUpdates]; + } copy]; + + self.isUpdating = YES; + + if (!self.delegate || !collectionView.dataSource) { + return; + } + + [collectionView performBatchUpdates:updates completion:completion]; +} + +- (WXRecycleListDiffResult *)recycleListUpdatesByDiffResult:(WXDiffResult *)diffResult +{ + NSMutableSet<NSIndexPath *> *reloadIndexPaths = [NSMutableSet set]; + NSMutableSet<NSIndexPath *> *deleteIndexPaths = [NSMutableSet set]; + NSMutableSet<NSIndexPath *> *insertIndexPaths = [NSMutableSet set]; + + for (WXDiffUpdateIndex *update in diffResult.updates) { + NSIndexPath *reloadIndexPath = [NSIndexPath indexPathForItem:update.oldIndex inSection:0]; + [reloadIndexPaths addObject:reloadIndexPath]; + } + + [diffResult.updates enumerateObjectsUsingBlock:^(WXDiffUpdateIndex * _Nonnull update, NSUInteger idx, BOOL * _Nonnull stop) { + NSIndexPath *reloadIndexPath = [NSIndexPath indexPathForItem:update.oldIndex inSection:0]; + [reloadIndexPaths addObject:reloadIndexPath]; + }]; + + [diffResult.inserts enumerateIndexesUsingBlock:^(NSUInteger insertIndex, BOOL * _Nonnull stop) { + NSIndexPath *insertIndexPath = [NSIndexPath indexPathForItem:insertIndex inSection:0]; + [insertIndexPaths addObject:insertIndexPath]; + }]; + + [diffResult.deletes enumerateIndexesUsingBlock:^(NSUInteger deleteIndex, BOOL * _Nonnull stop) { + NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForItem:deleteIndex inSection:0]; + [deleteIndexPaths addObject:deleteIndexPath]; + }]; + + WXRecycleListDiffResult *result = [[WXRecycleListDiffResult alloc] initWithInsertIndexPaths:insertIndexPaths deleteIndexPaths:deleteIndexPaths reloadIndexPaths:reloadIndexPaths]; + + return result; +} + + +- (void)applyUpdateWithDiffResult:(WXRecycleListDiffResult *)diffResult +{ + if (!_collectionView) { + return; + } + + // reload index paths should not inculde delete index paths, otherwise it will cause crash: + // Assertion failure in + // -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:] + NSMutableSet *reloadIndexPaths = self.reloadIndexPaths ? [[diffResult.reloadIndexPaths setByAddingObjectsFromSet:self.reloadIndexPaths] mutableCopy]: [diffResult.reloadIndexPaths mutableCopy]; + [reloadIndexPaths minusSet:diffResult.deleteIndexPaths]; + + [_collectionView deleteItemsAtIndexPaths:[diffResult.deleteIndexPaths allObjects]]; + [_collectionView insertItemsAtIndexPaths:[diffResult.insertIndexPaths allObjects]]; + [_collectionView reloadItemsAtIndexPaths:[reloadIndexPaths allObjects]]; +} + +- (void)cleanup +{ + self.newerData = nil; + self.appendingData = nil; + self.olderData = nil; + [self.completions removeAllObjects]; +} + @end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m index af1f760..1bc7d23 100644 --- a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m +++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m @@ -65,7 +65,7 @@ return [super hash]; } -- (BOOL)isEqualToWXObject:(id<WXDiffable>)object +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object { if ([object isKindOfClass:[WXSectionDataController class]]) { WXSectionDataController *controller = (WXSectionDataController *)object; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m index 0902eab..07621c7 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m @@ -56,7 +56,7 @@ } -- (BOOL)isEqualToWXObject:(id<WXDiffable>)object +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object { return self == object; } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h index 1b0f784..98c100d 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h +++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h @@ -145,8 +145,12 @@ typedef id (^WXDataBindingBlock)(NSDictionary *data, BOOL *needUpdate); BOOL _isRepeating; BOOL _isSkipUpdate; - NSMutableDictionary<NSString *, NSArray<NSString *> *> *_bindingAttributes; - NSMutableDictionary<NSString *, NSArray<NSString *> *> *_bindingStyles; + NSMutableDictionary<NSString *, WXDataBindingBlock> *_bindingProps; + NSMutableDictionary<NSString *, WXDataBindingBlock> *_bindingAttributes; + NSMutableDictionary<NSString *, WXDataBindingBlock> *_bindingStyles; + NSMutableDictionary<NSString *, WXDataBindingBlock> *_bindingEvents; + + NSMutableDictionary<NSString *, NSArray *> *_eventParameters; } ///-------------------------------------- @@ -222,6 +226,10 @@ typedef id (^WXDataBindingBlock)(NSDictionary *data, BOOL *needUpdate); - (void)_removeAllEvents; +- (void)_addEventParams:(NSDictionary *)params; + +- (NSArray *)_paramsForEvent:(NSString *)eventName; + - (void)_setupNavBarWithStyles:(NSMutableDictionary *)styles attributes:(NSMutableDictionary *)attributes; - (void)_initCompositingAttribute:(NSDictionary *)attributes; @@ -242,7 +250,7 @@ typedef id (^WXDataBindingBlock)(NSDictionary *data, BOOL *needUpdate); - (void)setGradientLayer; -- (void)_storeBindingsWithStyles:(NSDictionary *)styles attributes:(NSDictionary *)attributes; +- (void)_storeBindingsWithProps:(NSDictionary *)props styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSDictionary *)events; - (void)_didInserted; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h index 39bf0d7..db0c155 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h +++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h @@ -34,5 +34,7 @@ - (void)handleAppear; +- (CGPoint)absolutePositionForComponent:(WXComponent *)component; + @end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m index a960cc6..306631b 100644 --- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m +++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m @@ -566,25 +566,30 @@ WX_EXPORT_METHOD(@selector(resetLoadmore)) } } +- (CGPoint)absolutePositionForComponent:(WXComponent *)component +{ + return [component->_view.superview convertPoint:component->_view.frame.origin toView:_view]; +} + #pragma mark Private Methods - (void)scrollToTarget:(WXScrollToTarget *)target scrollRect:(CGRect)rect { WXComponent *component = target.target; - if (![component isViewLoaded]) { + if (![component isViewLoaded]) { return; } CGFloat ctop; if (component && component->_view && component->_view.superview) { - ctop = [component->_view.superview convertPoint:component->_view.frame.origin toView:_view].y; + ctop = [self absolutePositionForComponent:component].y; } else { ctop = 0.0; } CGFloat cbottom = ctop + CGRectGetHeight(component.calculatedFrame); CGFloat cleft; if (component && component->_view && component->_view.superview) { - cleft = [component->_view.superview convertPoint:component->_view.frame.origin toView:_view].x; + cleft = [self absolutePositionForComponent:component].x; } else { cleft = 0.0; } @@ -595,6 +600,7 @@ WX_EXPORT_METHOD(@selector(resetLoadmore)) if(!target.hasAppear && component){ target.hasAppear = YES; if (component->_appearEvent) { +// NSLog(@"appear:%@, %.2f", component, ctop); [component fireEvent:@"appear" params:_direction ? @{@"direction":_direction} : nil]; } } @@ -602,6 +608,7 @@ WX_EXPORT_METHOD(@selector(resetLoadmore)) if(target.hasAppear && component){ target.hasAppear = NO; if(component->_disappearEvent){ +// NSLog(@"disappear:%@", component); [component fireEvent:@"disappear" params:_direction ? @{@"direction":_direction} : nil]; } } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m index d3dceb0..51e3fd6 100644 --- a/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m +++ b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m @@ -109,7 +109,10 @@ [dict addEntriesFromDictionary:params]; } - [[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId ref:self.ref type:eventName params:dict domChanges:domChanges]; + NSArray *handlerArguments = [self _paramsForEvent:eventName]; + NSString *ref = _templateComponent ? _templateComponent.ref : self.ref; + + [[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId ref:ref type:eventName params:dict domChanges:domChanges handlerArguments:handlerArguments]; } - (void)addEvent:(NSString *)addEventName http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h index fee0be7..7ff2e01 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h +++ b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h @@ -125,12 +125,23 @@ extern void WXPerformBlockOnBridgeThread(void (^block)()); * @param instanceId instance id * @param ref : node reference * @param type : event type - * @param params : parameters + * @param params : parameters in event object * @param domChanges dom value changes, used for two-way data binding **/ - (void)fireEvent:(NSString *)instanceId ref:(NSString *)ref type:(NSString *)type params:(NSDictionary *)params domChanges:(NSDictionary *)domChanges; /** + * FireEvent + * @param instanceId instance id + * @param ref : node reference + * @param type : event type + * @param params : parameters in event object + * @param domChanges: dom value changes, used for two-way data binding + * @param eventArguments : arguments passed to event handler + **/ +- (void)fireEvent:(NSString *)instanceId ref:(NSString *)ref type:(NSString *)type params:(NSDictionary *)params domChanges:(NSDictionary *)domChanges handlerArguments:(NSArray *)handlerArguments; + +/** * callBack * * @param instanceId instanceId http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m index 0bf5aca..791aceb 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m +++ b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m @@ -304,12 +304,22 @@ void WXPerformBlockOnBridgeThread(void (^block)()) - (void)fireEvent:(NSString *)instanceId ref:(NSString *)ref type:(NSString *)type params:(NSDictionary *)params domChanges:(NSDictionary *)domChanges { + [self fireEvent:instanceId ref:ref type:type params:params domChanges:domChanges handlerArguments:nil]; +} + +- (void)fireEvent:(NSString *)instanceId ref:(NSString *)ref type:(NSString *)type params:(NSDictionary *)params domChanges:(NSDictionary *)domChanges handlerArguments:(NSArray *)handlerArguments +{ if (!type || !ref) { WXLogError(@"Event type and component ref should not be nil"); return; } NSArray *args = @[ref, type, params?:@{}, domChanges?:@{}]; + if (handlerArguments) { + NSMutableArray *newArgs = [args mutableCopy]; + [newArgs addObject:@{@"params":handlerArguments}]; + args = newArgs; + } WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; WXCallJSMethod *method = [[WXCallJSMethod alloc] initWithModuleName:nil methodName:@"fireEvent" arguments:args instance:instance]; http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.h b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.h index 387e3d0..ff5182c 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.h +++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.h @@ -29,6 +29,7 @@ extern "C" { #endif void WXPerformBlockOnComponentThread(void (^block)()); +void WXPerformBlockSyncOnComponentThread(void (^block)()); #ifdef __cplusplus } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m index bfd96eb..fa42445 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m +++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m @@ -136,6 +136,18 @@ static NSThread *WXComponentThread; } } ++ (void)_performBlockSyncOnComponentThread:(void (^)())block +{ + if([NSThread currentThread] == [self componentThread]){ + block(); + } else { + [self performSelector:@selector(_performBlockOnComponentThread:) + onThread:WXComponentThread + withObject:[block copy] + waitUntilDone:YES]; + } +} + - (void)startComponentTasks { [self _awakeDisplayLink]; @@ -406,7 +418,9 @@ static css_node_t * rootNodeGetChild(void *context, int i) NSDictionary *bindingStyles; NSDictionary *bindingAttibutes; NSDictionary *bindingEvents; + NSDictionary *bindingProps; if (isTemplate) { + bindingProps = [self _extractBindingProps:&attributes]; bindingStyles = [self _extractBindings:&styles]; bindingAttibutes = [self _extractBindings:&attributes]; bindingEvents = [self _extractBindingEvents:&events]; @@ -417,7 +431,7 @@ static css_node_t * rootNodeGetChild(void *context, int i) if (isTemplate) { component->_isTemplate = YES; - [component _storeBindingsWithStyles:bindingStyles attributes:bindingAttibutes]; + [component _storeBindingsWithProps:bindingProps styles:bindingStyles attributes:bindingAttibutes events:bindingEvents]; } WXAssert(component, @"Component build failed for data:%@", data); @@ -480,7 +494,7 @@ static css_node_t * rootNodeGetChild(void *context, int i) NSString *eventName = event[@"type"]; NSString *bindingParams = event[@"params"]; bindingEvents[eventName] = bindingParams; - [newEvents removeObject:event]; + newEvents[idx] = eventName; } }]; @@ -488,6 +502,19 @@ static css_node_t * rootNodeGetChild(void *context, int i) return bindingEvents; } +- (NSDictionary *)_extractBindingProps:(NSDictionary **)attributesPoint +{ + NSDictionary *attributes = *attributesPoint; + if (attributes[@"@componentProps"]) { + NSMutableDictionary *newAttributes = [attributes mutableCopy]; + [newAttributes removeObjectForKey:@"@componentProps"]; + *attributesPoint = newAttributes; + return attributes[@"@componentProps"]; + } + + return nil; +} + #pragma mark Reset -(BOOL)isShouldReset:(id )value { @@ -878,3 +905,8 @@ void WXPerformBlockOnComponentThread(void (^block)()) { [WXComponentManager _performBlockOnComponentThread:block]; } + +void WXPerformBlockSyncOnComponentThread(void (^block)()) +{ + [WXComponentManager _performBlockSyncOnComponentThread:block]; +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Model/WXComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m index 76fb446..491e272 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m @@ -412,6 +412,26 @@ return _cssNode; } +- (void)_addEventParams:(NSDictionary *)params +{ + pthread_mutex_lock(&_propertyMutex); + if (!_eventParameters) { + _eventParameters = [NSMutableDictionary dictionary]; + } + [_eventParameters addEntriesFromDictionary:params]; + pthread_mutex_unlock(&_propertyMutex); +} + +- (NSArray *)_paramsForEvent:(NSString *)eventName +{ + NSArray *params; + pthread_mutex_lock(&_propertyMutex); + params = _eventParameters[eventName]; + pthread_mutex_unlock(&_propertyMutex); + + return params; +} + #pragma mark Component Hierarchy - (NSArray<WXComponent *> *)subcomponents http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h index f494247..8c7aee3 100644 --- a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h +++ b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h @@ -21,7 +21,7 @@ @protocol WXDiffable <NSObject> -- (BOOL)isEqualToWXObject:(id<WXDiffable>)object; +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object; @end @@ -40,6 +40,10 @@ - (BOOL)hasChanges; +- (instancetype)initWithInserts:(NSIndexSet *)inserts + deletes:(NSIndexSet *)deletes + updates:(NSArray<WXDiffUpdateIndex *> *)updates; + @end @interface WXDiffUtil : NSObject @@ -47,3 +51,20 @@ + (WXDiffResult *)diffWithMinimumDistance:(NSArray<id<WXDiffable>> *)newArray oldArray:(NSArray<id<WXDiffable>> *)oldArray; @end + +@interface NSNumber (WXDiffable) <WXDiffable> + +@end + +@interface NSString (WXDiffable) <WXDiffable> + +@end + +@interface NSArray (WXDiffable) <WXDiffable> + +@end + +@interface NSDictionary (WXDiffable) <WXDiffable> + +@end + http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/74cc3b33/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m index 4b05df3..fabdbd1 100644 --- a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m +++ b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m @@ -96,7 +96,7 @@ typedef enum : NSUInteger { for (int oldIndex = 1; oldIndex < oldSize; oldIndex ++) { for (int newIndex = 1; newIndex < newSize; newIndex ++) { - if ([oldArray[oldIndex - 1] isEqualToWXObject:newArray[newIndex - 1]]) { + if ([oldArray[oldIndex - 1] weex_isEqualTo:newArray[newIndex - 1]]) { matrix[oldIndex][newIndex] = matrix[oldIndex - 1][newIndex - 1]; } else { int updateCost = matrix[oldIndex - 1][newIndex - 1] + 1; @@ -197,3 +197,85 @@ typedef enum : NSUInteger { } @end + +@implementation NSNumber (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + return [self isEqual:object]; +} + +@end + +@implementation NSString (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + return [self isEqual:object]; +} + +@end + +@implementation NSArray (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + if (![object isKindOfClass:[NSArray class]]) { + return NO; + } + + NSArray *array = (NSArray *)object; + if (self.count != array.count) { + return NO; + } + + __block BOOL isEqual = YES; + [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + id obj2 = [self objectAtIndex:idx]; + if ([obj conformsToProtocol:@protocol(WXDiffable)] && [obj2 conformsToProtocol:@protocol(WXDiffable)]) { + if (![obj weex_isEqualTo:obj2]) { + isEqual = NO; + *stop = YES; + } + } else { + isEqual = NO; + *stop = YES; + } + }]; + + return isEqual; +} + +@end + +@implementation NSDictionary (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + if (![object isKindOfClass:[NSDictionary class]]) { + return NO; + } + + NSDictionary *dictionary = (NSDictionary *)object; + if (self.count != dictionary.count) { + return NO; + } + + __block BOOL isEqual = YES; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + id obj2 = [self objectForKey:key]; + if (obj2 && [obj2 conformsToProtocol:@protocol(WXDiffable)] && [obj conformsToProtocol:@protocol(WXDiffable)]) { + if (![obj weex_isEqualTo:obj2]) { + isEqual = NO; + *stop = YES; + } + } else { + isEqual = NO; + *stop = YES; + } + }]; + + return isEqual; +} + +@end