You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by cx...@apache.org on 2017/03/02 03:28:41 UTC

[07/25] incubator-weex git commit: + [ios] support waterfall layout * Add recycler component to be based component of UICollectionView * Support column-count/column-gap/column-width for multi-column layout

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/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
new file mode 100644
index 0000000..c4270d7
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m
@@ -0,0 +1,85 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXSectionDataController.h"
+#import "WXCellComponent.h"
+#import "WXHeaderComponent.h"
+#import "WXAssert.h"
+
+@interface WXSectionDataController ()
+
+@end
+
+@implementation WXSectionDataController
+
+- (NSInteger)numberOfItems
+{
+    return self.cellComponents.count;
+}
+
+- (UIView *)cellForItemAtIndex:(NSInteger)index
+{
+    WXAssertMainThread();
+    
+    WXCellComponent *cellComponent = self.cellComponents[index];
+    return cellComponent.view;
+}
+
+- (CGSize)sizeForItemAtIndex:(NSInteger)index
+{
+    WXAssertMainThread();
+    
+    WXCellComponent *cellComponent = self.cellComponents[index];
+    return cellComponent.calculatedFrame.size;
+}
+
+- (UIView *)viewForHeaderAtIndex:(NSInteger)index;
+{
+    return self.headerComponent.view;
+}
+
+- (CGSize)sizeForHeaderAtIndex:(NSInteger)index
+{
+    return self.headerComponent.calculatedFrame.size;
+}
+
+- (BOOL)isStickyForHeaderAtIndex:(NSInteger)index
+{
+    return self.headerComponent.isSticky;
+}
+
+- (NSUInteger)hash
+{
+    return [super hash];
+}
+
+- (BOOL)isEqual:(id)object
+{
+    if ([object isKindOfClass:[WXSectionDataController class]]) {
+        WXSectionDataController *controller = (WXSectionDataController *)object;
+        BOOL headerEqual = (self.headerComponent && controller.headerComponent && self.headerComponent == controller.headerComponent) || (!self.headerComponent && !controller.headerComponent);
+        BOOL footerEqual = (self.footerComponent && controller.footerComponent && self.footerComponent == controller.footerComponent) || (!self.footerComponent && !controller.footerComponent);
+        BOOL cellEqual = self.cellComponents && controller.cellComponents && self.cellComponents.count == controller.cellComponents.count;
+        if (cellEqual) {
+            for (int i = 0; i < self.cellComponents.count; i ++) {
+                if (self.cellComponents[i] != controller.cellComponents[i]) {
+                    cellEqual = NO;
+                    break;
+                }
+            }
+        } else {
+            cellEqual = !self.cellComponents && controller.cellComponents;
+        }
+        
+        return headerEqual && footerEqual && cellEqual;
+    } else {
+        return NO;
+    }
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
index 2e5bf59..29aa1c6 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
@@ -7,14 +7,29 @@
  */
 
 #import "WXComponent.h"
-@class WXListComponent;
+@class WXCellComponent;
+
+@protocol WXCellRenderDelegate <NSObject>
+
+- (float)cellWidthForLayout:(WXCellComponent *)cell;
+
+- (void)cellDidLayout:(WXCellComponent *)cell;
+
+- (void)cellDidRendered:(WXCellComponent *)cell;
+
+- (void)cellDidRemove:(WXCellComponent *)cell;
+
+- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index;
+
+@end
 
 @interface WXCellComponent : WXComponent
 
 @property (nonatomic, strong) NSString *scope;
 @property (nonatomic, assign) BOOL isRecycle;
+@property (nonatomic, assign) BOOL isLayoutComplete;
 @property (nonatomic, assign) UITableViewRowAnimation insertAnimation;
 @property (nonatomic, assign) UITableViewRowAnimation deleteAnimation;
-@property (nonatomic, weak) WXListComponent *list;
+@property (nonatomic, weak) id<WXCellRenderDelegate> delegate;
 
 @end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/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 adf91a8..593628a 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
@@ -15,6 +15,7 @@
 @implementation WXCellComponent
 {
     NSIndexPath *_indexPathBeforeMove;
+    BOOL _isUseContainerWidth;
 }
 
 - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
@@ -38,12 +39,18 @@
     
 }
 
+- (BOOL)isEqual:(id)object
+{
+    WXCellComponent *cell = object;
+    return self == cell && self.isLayoutComplete == cell.isLayoutComplete && CGRectEqualToRect(self.calculatedFrame, cell.calculatedFrame);
+}
+
 - (void)_frameDidCalculated:(BOOL)isChanged
 {
     [super _frameDidCalculated:isChanged];
     
     if (isChanged) {
-        [self.list cellDidLayout:self];
+        [self.delegate cellDidLayout:self];
     }
 }
 
@@ -54,7 +61,7 @@
             [super displayCompletionBlock](layer, finished);
         }
         
-        [self.list cellDidRendered:self];
+        [self.delegate cellDidRendered:self];
     };
 }
 
@@ -79,8 +86,8 @@
 
 - (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
 {
-    if (self.list == newSupercomponent) {
-        [self.list cell:self didMoveToIndex:index];
+    if (self.delegate == newSupercomponent) {
+        [self.delegate cell:self didMoveToIndex:index];
         [super _removeFromSupercomponent];
         [newSupercomponent _insertSubcomponent:self atIndex:index];
     } else {
@@ -92,7 +99,7 @@
 {
     [super _removeFromSupercomponent];
     
-    [self.list cellDidRemove:self];
+    [self.delegate cellDidRemove:self];
 }
 
 - (void)removeFromSuperview
@@ -102,8 +109,10 @@
 
 - (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
 {
-    if (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) && self.list) {
-        self.cssNode->style.dimensions[CSS_WIDTH] = self.list.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+    if (self.delegate && (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) || _isUseContainerWidth)) {
+        self.cssNode->style.dimensions[CSS_WIDTH] = [self.delegate cellWidthForLayout:self];
+        //TODO: set _isUseContainerWidth to NO if updateStyles have width
+        _isUseContainerWidth = YES;
     }
     
     if ([self needsLayout]) {

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.h
index 0706eed..92b7596 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.h
@@ -1,10 +1,11 @@
-//
-//  WXComponent+GradientColor.h
-//  Pods
-//
-//  Created by bobning on 16/12/23.
-//
-//
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
 
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.m b/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.m
index e710ed0..86c3379 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent+GradientColor.m
@@ -1,10 +1,11 @@
-//
-//  WXComponent+GradientColor.m
-//  Pods
-//
-//  Created by bobning on 16/12/23.
-//
-//
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
 
 #import "WXComponent+GradientColor.h"
 #import "WXComponent_internal.h"

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.h
new file mode 100644
index 0000000..e9c801c
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.h
@@ -0,0 +1,13 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <WeexSDK/WeexSDK.h>
+
+@interface WXFooterComponent : WXComponent
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.m
new file mode 100644
index 0000000..f139eaf
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXFooterComponent.m
@@ -0,0 +1,13 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXFooterComponent.h"
+
+@implementation WXFooterComponent
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.h
new file mode 100644
index 0000000..5aaf616
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.h
@@ -0,0 +1,25 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <WeexSDK/WeexSDK.h>
+@class WXHeaderComponent;
+
+@protocol WXHeaderRenderDelegate <NSObject>
+
+- (float)headerWidthForLayout:(WXHeaderComponent *)cell;
+
+- (void)headerDidLayout:(WXHeaderComponent *)cell;
+
+@end
+
+@interface WXHeaderComponent : WXComponent
+
+@property (nonatomic, weak) id<WXHeaderRenderDelegate> delegate;
+@property (nonatomic, assign, readonly) BOOL isSticky;
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
new file mode 100644
index 0000000..f54938d
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
@@ -0,0 +1,62 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXHeaderComponent.h"
+#import "WXComponent_internal.h"
+
+@implementation WXHeaderComponent
+{
+    BOOL _isUseContainerWidth;
+}
+
+//TODO: header remove->need reload
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+    self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+    
+    if (self) {
+        _async = YES;
+        _isNeedJoinLayoutSystem = NO;
+    }
+    
+    return self;
+}
+
+- (BOOL)isSticky
+{
+    return _positionType == WXPositionTypeSticky;
+}
+
+- (void)_frameDidCalculated:(BOOL)isChanged
+{
+    [super _frameDidCalculated:isChanged];
+    
+    if (isChanged) {
+        [self.delegate headerDidLayout:self];
+    }
+}
+
+- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
+{
+    if (self.delegate && (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) || _isUseContainerWidth)) {
+        self.cssNode->style.dimensions[CSS_WIDTH] = [self.delegate headerWidthForLayout:self];
+        //TODO: set _isUseContainerWidth to NO if updateStyles have width
+        _isUseContainerWidth = YES;
+    }
+    
+    if ([self needsLayout]) {
+        layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
+        if ([WXLog logLevel] >= WXLogLevelDebug) {
+            print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
+        }
+    }
+    
+    [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.h
index 007f502..2497c3b 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.h
@@ -8,18 +8,6 @@
 
 #import "WXScrollerComponent.h"
 
-@class WXCellComponent;
-@class WXHeaderComponent;
 @interface WXListComponent : WXScrollerComponent
 
-- (void)cellDidRemove:(WXCellComponent *)cell;
-
-- (void)cellDidLayout:(WXCellComponent *)cell;
-
-- (void)headerDidLayout:(WXHeaderComponent *)header;
-
-- (void)cellDidRendered:(WXCellComponent *)cell;
-
-- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index;
-
 @end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
index 4ab6b2c..943bcf8 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
@@ -8,6 +8,7 @@
 
 #import "WXListComponent.h"
 #import "WXCellComponent.h"
+#import "WXHeaderComponent.h"
 #import "WXComponent.h"
 #import "WXComponent_internal.h"
 #import "NSArray+Weex.h"
@@ -52,53 +53,6 @@
 
 @end
 
-@interface WXHeaderComponent : WXComponent
-
-@property (nonatomic, weak) WXListComponent *list;
-
-@end
-
-@implementation WXHeaderComponent
-
-//TODO: header remove->need reload
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
-    self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
-    
-    if (self) {
-        _async = YES;
-        _isNeedJoinLayoutSystem = NO;
-    }
-    
-    return self;
-}
-
-- (void)_frameDidCalculated:(BOOL)isChanged
-{
-    [super _frameDidCalculated:isChanged];
-    
-    if (isChanged) {
-        [self.list headerDidLayout:self];
-    }
-}
-
-- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
-{
-    if (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) && self.list) {
-        self.cssNode->style.dimensions[CSS_WIDTH] = self.list.scrollerCSSNode->style.dimensions[CSS_WIDTH];
-    }
-    
-    if ([self needsLayout]) {
-        layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
-        if ([WXLog logLevel] >= WXLogLevelDebug) {
-            print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
-        }
-    }
-    
-    [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
-}
-
-@end
 
 @interface WXSection : NSObject<NSCopying>
 
@@ -133,7 +87,7 @@
 }
 @end
 
-@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate>
+@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate>
 
 @end
 
@@ -246,9 +200,9 @@
 - (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
 {
     if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
-        ((WXCellComponent *)subcomponent).list = self;
+        ((WXCellComponent *)subcomponent).delegate = self;
     } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
-        ((WXHeaderComponent *)subcomponent).list = self;
+        ((WXHeaderComponent *)subcomponent).delegate = self;
     } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]]
                && ![subcomponent isKindOfClass:[WXLoadingComponent class]]
                && subcomponent->_positionType != WXPositionTypeFixed) {
@@ -295,6 +249,13 @@
     }
 }
 
+#pragma mark - WXHeaderRenderDelegate
+
+- (float)headerWidthForLayout:(WXHeaderComponent *)cell
+{
+    return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+}
+
 - (void)headerDidLayout:(WXHeaderComponent *)header
 {
     [self.weexInstance.componentManager _addUITask:^{
@@ -302,10 +263,16 @@
         [_tableView beginUpdates];
         [_tableView endUpdates];
     }];
-    
 }
 
 
+#pragma mark - WXCellRenderDelegate
+
+- (float)cellWidthForLayout:(WXCellComponent *)cell
+{
+    return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+}
+
 - (void)cellDidRemove:(WXCellComponent *)cell
 {
     WXAssertComponentThread();

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Component/WXTransform.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTransform.m b/ios/sdk/WeexSDK/Sources/Component/WXTransform.m
index a72f39b..e5ed68b 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXTransform.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTransform.m
@@ -87,7 +87,7 @@
     }
     
     if (_translateX || _translateY) {
-        nativeTransform = CGAffineTransformTranslate(nativeTransform, _translateX ? [_translateX valueForMaximumValue:view.bounds.size.width] : 0,  _translateY ? [_translateY valueForMaximumValue:view.bounds.size.height] : 0);
+        nativeTransform = CGAffineTransformTranslate(nativeTransform, _translateX ? [_translateX valueForMaximum:view.bounds.size.width] : 0,  _translateY ? [_translateY valueForMaximum:view.bounds.size.height] : 0);
     }
     
     nativeTransform = CGAffineTransformScale(nativeTransform, _scaleX, _scaleY);
@@ -131,8 +131,8 @@
           * http://ronnqvi.st/translate-rotate-translate/
           **/
         CGPoint anchorPoint = CGPointMake(
-                                          _originX ? [_originX valueForMaximumValue:view.bounds.size.width] / view.bounds.size.width : 0.5,
-                                          _originY ? [_originY valueForMaximumValue:view.bounds.size.width] / view.bounds.size.height : 0.5);
+                                          _originX ? [_originX valueForMaximum:view.bounds.size.width] / view.bounds.size.width : 0.5,
+                                          _originY ? [_originY valueForMaximum:view.bounds.size.width] / view.bounds.size.height : 0.5);
         [self setAnchorPoint:anchorPoint forView:view];
     }
     
@@ -220,8 +220,8 @@
         }
     }
     
-    _originX = [WXLength lengthWithValue:originX type:typeX];
-    _originY = [WXLength lengthWithValue:originY type:typeY];
+    _originX = [WXLength lengthWithFloat:originX type:typeX];
+    _originY = [WXLength lengthWithFloat:originY type:typeY];
 }
 
 - (void)parseRotate:(NSArray *)value
@@ -235,20 +235,20 @@
     WXLength *translateX;
     double x = [value[0] doubleValue];
     if ([value[0] hasSuffix:@"%"]) {
-        translateX = [WXLength lengthWithValue:x type:WXLengthTypePercent];
+        translateX = [WXLength lengthWithFloat:x type:WXLengthTypePercent];
     } else {
         x = WXPixelScale(x, self.weexInstance.pixelScaleFactor);
-        translateX = [WXLength lengthWithValue:x type:WXLengthTypeFixed];
+        translateX = [WXLength lengthWithFloat:x type:WXLengthTypeFixed];
     }
 
     WXLength *translateY;
     if (value.count > 1) {
         double y = [value[1] doubleValue];
         if ([value[1] hasSuffix:@"%"]) {
-            translateY = [WXLength lengthWithValue:y type:WXLengthTypePercent];
+            translateY = [WXLength lengthWithFloat:y type:WXLengthTypePercent];
         } else {
             y = WXPixelScale(y, self.weexInstance.pixelScaleFactor);
-            translateY = [WXLength lengthWithValue:y type:WXLengthTypeFixed];
+            translateY = [WXLength lengthWithFloat:y type:WXLengthTypeFixed];
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
index e804aea..cd9f50e 100644
--- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
+++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
@@ -72,6 +72,8 @@
     [self registerComponent:@"image" withClass:NSClassFromString(@"WXImageComponent") withProperties:nil];
     [self registerComponent:@"scroller" withClass:NSClassFromString(@"WXScrollerComponent") withProperties:nil];
     [self registerComponent:@"list" withClass:NSClassFromString(@"WXListComponent") withProperties:nil];
+    [self registerComponent:@"recycler" withClass:NSClassFromString(@"WXRecyclerComponent") withProperties:nil];
+    [self registerComponent:@"waterfall" withClass:NSClassFromString(@"WXRecyclerComponent") withProperties:nil];
     
     [self registerComponent:@"header" withClass:NSClassFromString(@"WXHeaderComponent")];
     [self registerComponent:@"cell" withClass:NSClassFromString(@"WXCellComponent")];

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
index 3eafce0..81fd481 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
@@ -167,16 +167,16 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
             if ((wxTransform.translateX && ![wxTransform.translateX isEqualToLength:oldTransform.translateX]) || (!wxTransform.translateX && oldTransform.translateX)) {
                 WXAnimationInfo *newInfo = [info copy];
                 newInfo.propertyName = @"transform.translation.x";
-                newInfo.fromValue = @([oldTransform.translateX valueForMaximumValue:view.bounds.size.width]);
-                newInfo.toValue = @([wxTransform.translateX valueForMaximumValue:view.bounds.size.width]);
+                newInfo.fromValue = @([oldTransform.translateX valueForMaximum:view.bounds.size.width]);
+                newInfo.toValue = @([wxTransform.translateX valueForMaximum:view.bounds.size.width]);
                 [infos addObject:newInfo];
             }
             
             if ((wxTransform.translateY && ![wxTransform.translateY isEqualToLength:oldTransform.translateY]) || (!wxTransform.translateY && oldTransform.translateY)) {
                 WXAnimationInfo *newInfo = [info copy];
                 newInfo.propertyName = @"transform.translation.y";
-                newInfo.fromValue = @([oldTransform.translateY valueForMaximumValue:view.bounds.size.height]);
-                newInfo.toValue = @([wxTransform.translateY valueForMaximumValue:view.bounds.size.height]);
+                newInfo.fromValue = @([oldTransform.translateY valueForMaximum:view.bounds.size.height]);
+                newInfo.toValue = @([wxTransform.translateY valueForMaximum:view.bounds.size.height]);
                 [infos addObject:newInfo];
             }
             

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.h b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.h
index 75658c6..bef72d0 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.h
@@ -11,6 +11,7 @@
 #import "WXLog.h"
 #import "WXLayoutDefine.h"
 #import "WXType.h"
+#import "WXLength.h"
 
 @interface WXConvert : NSObject
 
@@ -68,6 +69,8 @@ typedef BOOL WXClipType;
 
 + (WXGradientType)gradientType:(id)value;
 
++ (WXLength *)WXLength:(id)value isFloat:(BOOL)isFloat scaleFactor:(CGFloat)scaleFactor;
+
 @end
 
 @interface WXConvert (Deprecated)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
index 15c5f6c..b72d7df 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
@@ -8,6 +8,7 @@
 
 #import "WXConvert.h"
 #import "WXUtility.h"
+#import "WXAssert.h"
 
 @implementation WXConvert
 
@@ -723,6 +724,34 @@ WX_NUMBER_CONVERT(NSUInteger, unsignedIntegerValue)
     return type;
 }
 
+#pragma mark - Length
+
++ (WXLength *)WXLength:(id)value isFloat:(BOOL)isFloat scaleFactor:(CGFloat)scaleFactor
+{
+    if (!value) {
+        return nil;
+    }
+    
+    WXLengthType type = WXLengthTypeFixed;
+    if ([value isKindOfClass:[NSString class]]) {
+        if ([value isEqualToString:@"auto"]) {
+            type = WXLengthTypeAuto;
+        } else if ([value isEqualToString:@"normal"]){
+            type = WXLengthTypeNormal;
+        } else if ([value hasSuffix:@"%"]) {
+            type = WXLengthTypePercent;
+        }
+    } else if (![value isKindOfClass:[NSNumber class]]) {
+        WXAssert(NO, @"Unsupported type:%@ for WXLength", NSStringFromClass([value class]));
+    }
+    
+    if (isFloat) {
+        return [WXLength lengthWithFloat:([value floatValue] * scaleFactor) type:type];
+    } else {
+        return [WXLength lengthWithInt:([value intValue] * scaleFactor) type:type];
+    }
+}
+
 @end
 
 @implementation WXConvert (Deprecated)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/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
new file mode 100644
index 0000000..c7edda9
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h
@@ -0,0 +1,32 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface WXDiffUpdateIndex : NSObject
+
+@property (nonatomic, assign, readonly) NSUInteger oldIndex;
+@property (nonatomic, assign, readonly) NSUInteger newIndex;
+
+@end
+
+@interface WXDiffResult : NSObject
+
+@property (nonatomic, strong, readonly) NSIndexSet *inserts;
+@property (nonatomic, strong, readonly) NSIndexSet *deletes;
+@property (nonatomic, strong, readonly) NSArray<WXDiffUpdateIndex *> *updates;
+
+- (BOOL)hasChanges;
+
+@end
+
+@interface WXDiffUtil : NSObject
+
++ (WXDiffResult *)diffWithMinimumDistance:(NSArray *)newArray oldArray:(NSArray *)oldArray;
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/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
new file mode 100644
index 0000000..44bd737
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m
@@ -0,0 +1,165 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXDiffUtil.h"
+
+typedef enum : NSUInteger {
+    WXDiffOperationDoNothing,
+    WXDiffOperationUpdate,
+    WXDiffOperationDelete,
+    WXDiffOperationInsert
+} WXDiffOperation;
+
+@implementation WXDiffUpdateIndex
+
+- (instancetype)initWithOldIndex:(NSUInteger)oldIndex newIndex:(NSUInteger)newIndex
+{
+    if (self = [super init]) {
+        _oldIndex = oldIndex;
+        _newIndex = newIndex;
+    }
+    
+    return self;
+}
+
+@end
+
+@implementation WXDiffResult
+
+- (instancetype)initWithInserts:(NSIndexSet *)inserts
+                        deletes:(NSIndexSet *)deletes
+                        updates:(NSArray<WXDiffUpdateIndex *> *)updates
+{
+    if (self = [super init]) {
+        _inserts = [inserts copy];
+        _deletes = [deletes copy];
+        _updates = [updates copy];
+    }
+    
+    return self;
+}
+
+- (BOOL)hasChanges
+{
+    return _updates.count > 0 || _inserts.count > 0 || _deletes.count > 0;
+}
+
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"<%@: %p; %zi inserts; %zi deletes; %zi updates", NSStringFromClass([self class]), self, _inserts.count, _deletes.count, _updates.count];
+}
+
+
+@end
+
+@implementation WXDiffUtil
+
++ (WXDiffResult *)diffWithMinimumDistance:(NSArray *)newArray oldArray:(NSArray *)oldArray
+{
+    // Using the levenshtein algorithm
+    // https://en.wikipedia.org/wiki/Levenshtein_distance
+    
+    int oldSize = (int)(oldArray.count + 1);
+    int newSize = (int)(newArray.count + 1);
+    
+    int **matrix = malloc(oldSize * sizeof(int *));
+    for (int i = 0; i < oldSize; i++) {
+        matrix[i] = malloc(newSize * sizeof(int));
+    }
+    
+    matrix[0][0] = 0;
+    
+    for (int i = 1; i < oldSize; i++) {
+        matrix[i][0] = i;
+    }
+    
+    for (int j = 1; j < newSize; j++) {
+        matrix[0][j] = j;
+    }
+    
+    for (int oldIndex = 1; oldIndex < oldSize; oldIndex ++) {
+        for (int newIndex = 1; newIndex < newSize; newIndex ++) {
+            if ([oldArray[oldIndex - 1] isEqual:newArray[newIndex - 1]]) {
+                matrix[oldIndex][newIndex] = matrix[oldIndex - 1][newIndex - 1];
+            } else {
+                int updateCost = matrix[oldIndex - 1][newIndex - 1] + 1;
+                int insertCost = matrix[oldIndex][newIndex - 1] + 1;
+                int deleteCost = matrix[oldIndex - 1][newIndex] + 1;
+                matrix[oldIndex][newIndex] = MIN(MIN(insertCost, deleteCost), updateCost);
+            }
+        }
+    }
+    
+    NSMutableArray *updates = [NSMutableArray array];
+    NSMutableIndexSet *inserts = [NSMutableIndexSet indexSet];
+    NSMutableIndexSet *deletes = [NSMutableIndexSet indexSet];
+    int oldIndex = oldSize - 1;
+    int newIndex = newSize - 1;
+    while (oldIndex != 0 || newIndex != 0) {
+        WXDiffOperation operation = [self _operationInMatrix:matrix newIndex:newIndex oldIndex:oldIndex];
+        switch (operation) {
+            case WXDiffOperationUpdate:
+                newIndex --;
+                oldIndex --;
+                [updates addObject:[[WXDiffUpdateIndex alloc] initWithOldIndex:oldIndex newIndex:newIndex]];
+                break;
+            case WXDiffOperationDelete:
+                oldIndex --;
+                [deletes addIndex:oldIndex];
+                break;
+            case WXDiffOperationInsert:
+                newIndex --;
+                [inserts addIndex:newIndex];
+                break;
+            case WXDiffOperationDoNothing:
+                newIndex --;
+                oldIndex --;
+                break;
+        }
+    }
+    
+    for (int i = 0; i < oldSize; i++) {
+        free(matrix[i]);
+    }
+    free(matrix);
+    
+    WXDiffResult *result = [[WXDiffResult alloc] initWithInserts:inserts deletes:deletes updates:updates];
+    return result;
+}
+
++ (WXDiffOperation)_operationInMatrix:(int **)matrix newIndex:(int)newIndex oldIndex:(int)oldIndex
+{
+    if (newIndex == 0) {
+        return WXDiffOperationDelete;
+    }
+    
+    if (oldIndex == 0) {
+        return WXDiffOperationInsert;
+    }
+    
+    int cost = matrix[oldIndex][newIndex];
+    
+    int costBeforeInsert = matrix[oldIndex][newIndex - 1];
+    if (costBeforeInsert + 1 == cost) {
+        return WXDiffOperationInsert;
+    }
+    
+    int costBeforDelete = matrix[oldIndex - 1][newIndex];
+    if (costBeforDelete + 1 == cost) {
+        return WXDiffOperationDelete;
+    }
+    
+    int costBeforUpdate = matrix[oldIndex - 1][newIndex - 1];
+    if (costBeforUpdate + 1 == cost) {
+        return WXDiffOperationUpdate;
+    }
+    
+    return WXDiffOperationDoNothing;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Utility/WXLength.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXLength.h b/ios/sdk/WeexSDK/Sources/Utility/WXLength.h
index b8480ea..491d2d7 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXLength.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXLength.h
@@ -12,13 +12,20 @@ typedef enum : NSUInteger {
     WXLengthTypeFixed,
     WXLengthTypePercent,
     WXLengthTypeAuto,
+    WXLengthTypeNormal
 } WXLengthType;
 
 @interface WXLength : NSObject
 
-+ (instancetype)lengthWithValue:(float)value type:(WXLengthType)type;
++ (instancetype)lengthWithFloat:(float)value type:(WXLengthType)type;
 
-- (float)valueForMaximumValue:(float)maximumValue;
++ (instancetype)lengthWithInt:(int)value type:(WXLengthType)type;
+
+- (float)valueForMaximum:(float)maximumValue;
+
+- (int)intValue;
+
+- (float)floatValue;
 
 - (BOOL)isEqualToLength:(WXLength *)length;
 
@@ -28,4 +35,6 @@ typedef enum : NSUInteger {
 
 - (BOOL)isAuto;
 
+- (BOOL)isNormal;
+
 @end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/1c96da7a/ios/sdk/WeexSDK/Sources/Utility/WXLength.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXLength.m b/ios/sdk/WeexSDK/Sources/Utility/WXLength.m
index 30cf57e..e64526d 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXLength.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXLength.m
@@ -11,25 +11,38 @@
 
 @implementation WXLength
 {
-    float _value;
+    float _floatValue;
+    int _intValue;
     WXLengthType _type;
+    BOOL _isFloat;
 }
 
-+ (instancetype)lengthWithValue:(float)value type:(WXLengthType)type
++ (instancetype)lengthWithFloat:(float)value type:(WXLengthType)type
 {
     WXLength *length = [WXLength new];
-    length->_value = value;
+    length->_floatValue = value;
     length->_type = type;
+    length->_isFloat = YES;
     return length;
 }
 
-- (float)valueForMaximumValue:(float)maximumValue
++ (instancetype)lengthWithInt:(int)value type:(WXLengthType)type
 {
+    WXLength *length = [WXLength new];
+    length->_intValue = value;
+    length->_type = type;
+    length->_isFloat = NO;
+    return length;
+}
+
+- (float)valueForMaximum:(float)maximumValue
+{
+    
     switch (_type) {
         case WXLengthTypeFixed:
-            return _value;
+            return _isFloat ? _floatValue : _intValue;
         case WXLengthTypePercent:
-            return maximumValue * _value / 100.0;
+            return maximumValue * (_isFloat ? _floatValue : _intValue) / 100.0;
         case WXLengthTypeAuto:
             return maximumValue;
         default:
@@ -38,9 +51,22 @@
     }
 }
 
+- (int)intValue
+{
+    WXAssert(!_isFloat, @"call `intValue` for non-int length");
+    return _intValue;
+}
+
+- (float)floatValue
+{
+    WXAssert(_isFloat,  @"call `floatValue` for non-float length");
+    return _floatValue;
+}
+
 - (BOOL)isEqualToLength:(WXLength *)length
 {
-    return length && _type == length->_type && _value == length->_value;
+    return length && _type == length->_type && _isFloat == length->_isFloat
+    && _floatValue == length->_floatValue && _intValue == length->_intValue;
 }
 
 - (BOOL)isFixed
@@ -58,4 +84,9 @@
     return _type == WXLengthTypeAuto;
 }
 
+- (BOOL)isNormal
+{
+    return _type == WXLengthTypeNormal;
+}
+
 @end