* [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

Reply via email to