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/06 11:10:11 UTC
[05/12] incubator-weex git commit: * [dev] Add Feature Recycler and
Waterfall
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
index 2a0f761..29ccab4 100644
--- a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
+++ b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
@@ -115,6 +115,22 @@
744BEA561D05178F00452B5D /* WXComponent+Display.m in Sources */ = {isa = PBXBuildFile; fileRef = 744BEA541D05178F00452B5D /* WXComponent+Display.m */; };
744BEA591D0520F300452B5D /* WXComponent+Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 744BEA571D0520F300452B5D /* WXComponent+Layout.h */; };
744BEA5A1D0520F300452B5D /* WXComponent+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 744BEA581D0520F300452B5D /* WXComponent+Layout.m */; };
+ 744D610C1E49978200B624B3 /* WXHeaderComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 744D610A1E49978200B624B3 /* WXHeaderComponent.h */; };
+ 744D610D1E49978200B624B3 /* WXHeaderComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D610B1E49978200B624B3 /* WXHeaderComponent.m */; };
+ 744D61101E49979000B624B3 /* WXFooterComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 744D610E1E49979000B624B3 /* WXFooterComponent.h */; };
+ 744D61111E49979000B624B3 /* WXFooterComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D610F1E49979000B624B3 /* WXFooterComponent.m */; };
+ 744D61141E4AF23E00B624B3 /* WXDiffUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 744D61121E4AF23E00B624B3 /* WXDiffUtil.h */; };
+ 744D61151E4AF23E00B624B3 /* WXDiffUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D61131E4AF23E00B624B3 /* WXDiffUtil.m */; };
+ 745B2D681E5A8E1E0092D38A /* WXMultiColumnLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D5E1E5A8E1E0092D38A /* WXMultiColumnLayout.h */; };
+ 745B2D691E5A8E1E0092D38A /* WXMultiColumnLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */; };
+ 745B2D6A1E5A8E1E0092D38A /* WXRecyclerComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */; };
+ 745B2D6B1E5A8E1E0092D38A /* WXRecyclerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */; };
+ 745B2D6C1E5A8E1E0092D38A /* WXRecyclerDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D621E5A8E1E0092D38A /* WXRecyclerDataController.h */; };
+ 745B2D6D1E5A8E1E0092D38A /* WXRecyclerDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */; };
+ 745B2D6E1E5A8E1E0092D38A /* WXRecyclerUpdateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D641E5A8E1E0092D38A /* WXRecyclerUpdateController.h */; };
+ 745B2D6F1E5A8E1E0092D38A /* WXRecyclerUpdateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D651E5A8E1E0092D38A /* WXRecyclerUpdateController.m */; };
+ 745B2D701E5A8E1E0092D38A /* WXSectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D661E5A8E1E0092D38A /* WXSectionDataController.h */; };
+ 745B2D711E5A8E1E0092D38A /* WXSectionDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D671E5A8E1E0092D38A /* WXSectionDataController.m */; };
745ED2DA1C5F2C7E002DB5A8 /* WXView.h in Headers */ = {isa = PBXBuildFile; fileRef = 745ED2D61C5F2C7E002DB5A8 /* WXView.h */; };
745ED2DB1C5F2C7E002DB5A8 /* WXView.m in Sources */ = {isa = PBXBuildFile; fileRef = 745ED2D71C5F2C7E002DB5A8 /* WXView.m */; };
7461F8901CFB373100F62D44 /* WXDisplayQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 7461F88C1CFB373100F62D44 /* WXDisplayQueue.h */; };
@@ -407,6 +423,22 @@
744BEA541D05178F00452B5D /* WXComponent+Display.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WXComponent+Display.m"; sourceTree = "<group>"; };
744BEA571D0520F300452B5D /* WXComponent+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WXComponent+Layout.h"; path = "Layout/WXComponent+Layout.h"; sourceTree = "<group>"; };
744BEA581D0520F300452B5D /* WXComponent+Layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "WXComponent+Layout.m"; path = "Layout/WXComponent+Layout.m"; sourceTree = "<group>"; };
+ 744D610A1E49978200B624B3 /* WXHeaderComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXHeaderComponent.h; sourceTree = "<group>"; };
+ 744D610B1E49978200B624B3 /* WXHeaderComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXHeaderComponent.m; sourceTree = "<group>"; };
+ 744D610E1E49979000B624B3 /* WXFooterComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXFooterComponent.h; sourceTree = "<group>"; };
+ 744D610F1E49979000B624B3 /* WXFooterComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXFooterComponent.m; sourceTree = "<group>"; };
+ 744D61121E4AF23E00B624B3 /* WXDiffUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXDiffUtil.h; sourceTree = "<group>"; };
+ 744D61131E4AF23E00B624B3 /* WXDiffUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXDiffUtil.m; sourceTree = "<group>"; };
+ 745B2D5E1E5A8E1E0092D38A /* WXMultiColumnLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXMultiColumnLayout.h; path = WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.h; sourceTree = SOURCE_ROOT; };
+ 745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXMultiColumnLayout.m; path = WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.m; sourceTree = SOURCE_ROOT; };
+ 745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXRecyclerComponent.h; path = WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.h; sourceTree = SOURCE_ROOT; };
+ 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXRecyclerComponent.m; path = WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m; sourceTree = SOURCE_ROOT; };
+ 745B2D621E5A8E1E0092D38A /* WXRecyclerDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXRecyclerDataController.h; path = WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.h; sourceTree = SOURCE_ROOT; };
+ 745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXRecyclerDataController.m; path = WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.m; sourceTree = SOURCE_ROOT; };
+ 745B2D641E5A8E1E0092D38A /* WXRecyclerUpdateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXRecyclerUpdateController.h; path = WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.h; sourceTree = SOURCE_ROOT; };
+ 745B2D651E5A8E1E0092D38A /* WXRecyclerUpdateController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXRecyclerUpdateController.m; path = WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.m; sourceTree = SOURCE_ROOT; };
+ 745B2D661E5A8E1E0092D38A /* WXSectionDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXSectionDataController.h; path = WeexSDK/Sources/Component/Recycler/WXSectionDataController.h; sourceTree = SOURCE_ROOT; };
+ 745B2D671E5A8E1E0092D38A /* WXSectionDataController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXSectionDataController.m; path = WeexSDK/Sources/Component/Recycler/WXSectionDataController.m; sourceTree = SOURCE_ROOT; };
745ED2D61C5F2C7E002DB5A8 /* WXView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXView.h; sourceTree = "<group>"; };
745ED2D71C5F2C7E002DB5A8 /* WXView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXView.m; sourceTree = "<group>"; };
7461F88C1CFB373100F62D44 /* WXDisplayQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXDisplayQueue.h; sourceTree = "<group>"; };
@@ -778,6 +810,24 @@
path = WeexSDKTests;
sourceTree = "<group>";
};
+ 74D8DB401E4825920078B667 /* Recycler */ = {
+ isa = PBXGroup;
+ children = (
+ 745B2D5E1E5A8E1E0092D38A /* WXMultiColumnLayout.h */,
+ 745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */,
+ 745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */,
+ 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */,
+ 745B2D621E5A8E1E0092D38A /* WXRecyclerDataController.h */,
+ 745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */,
+ 745B2D641E5A8E1E0092D38A /* WXRecyclerUpdateController.h */,
+ 745B2D651E5A8E1E0092D38A /* WXRecyclerUpdateController.m */,
+ 745B2D661E5A8E1E0092D38A /* WXSectionDataController.h */,
+ 745B2D671E5A8E1E0092D38A /* WXSectionDataController.m */,
+ );
+ name = Recycler;
+ path = Grid;
+ sourceTree = "<group>";
+ };
74EF31C11DE6932900667A07 /* handler */ = {
isa = PBXGroup;
children = (
@@ -990,6 +1040,8 @@
74896F2F1D1AC79400D1D593 /* NSObject+WXSwizzle.m */,
747DF6801E31AEE4005C53A8 /* WXLength.h */,
747DF6811E31AEE4005C53A8 /* WXLength.m */,
+ 744D61121E4AF23E00B624B3 /* WXDiffUtil.h */,
+ 744D61131E4AF23E00B624B3 /* WXDiffUtil.m */,
);
path = Utility;
sourceTree = "<group>";
@@ -1036,6 +1088,7 @@
children = (
C4B3D6D21E6954300013F38D /* WXEditComponent.h */,
C4B3D6D31E6954300013F38D /* WXEditComponent.m */,
+ 74D8DB401E4825920078B667 /* Recycler */,
2A837AAC1CD9DE9200AEDF03 /* WXLoadingComponent.h */,
2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.m */,
2A837AAE1CD9DE9200AEDF03 /* WXLoadingIndicator.h */,
@@ -1060,12 +1113,12 @@
77E65A181C155F25008B8775 /* WXScrollerComponent.m */,
2A44AB0F1C1AD5B00067A7EA /* WXSliderComponent.h */,
59D3CA461CFC3CC0008835DC /* WXSliderComponent.m */,
- 74CC7A1A1C2BC5F800829368 /* WXCellComponent.h */,
74CC7A1B1C2BC5F800829368 /* WXCellComponent.m */,
74CC7A1E1C2BF9DC00829368 /* WXListComponent.h */,
74CC7A1F1C2BF9DC00829368 /* WXListComponent.m */,
2AC750221C7565690041D390 /* WXIndicatorComponent.h */,
2AC750231C7565690041D390 /* WXIndicatorComponent.m */,
+ 74CC7A1A1C2BC5F800829368 /* WXCellComponent.h */,
2A1F57B51C75C6A600B58017 /* WXTextInputComponent.h */,
2A1F57B61C75C6A600B58017 /* WXTextInputComponent.m */,
DC03ADB81D508719003F76E7 /* WXTextAreaComponent.h */,
@@ -1083,6 +1136,10 @@
D33451071D3E19480083598A /* WXCanvasComponent.m */,
59970D2C1E0D228D0049F535 /* WXComponent+GradientColor.h */,
59970D2D1E0D228D0049F535 /* WXComponent+GradientColor.m */,
+ 744D610A1E49978200B624B3 /* WXHeaderComponent.h */,
+ 744D610B1E49978200B624B3 /* WXHeaderComponent.m */,
+ 744D610E1E49979000B624B3 /* WXFooterComponent.h */,
+ 744D610F1E49979000B624B3 /* WXFooterComponent.m */,
);
path = Component;
sourceTree = "<group>";
@@ -1182,9 +1239,12 @@
77D161621C02ED790010B15B /* WXLog.h in Headers */,
77D1614B1C02E3790010B15B /* WXConvert.h in Headers */,
59A596221CB6311F0012CD52 /* WXNavigatorModule.h in Headers */,
+ 745B2D6A1E5A8E1E0092D38A /* WXRecyclerComponent.h in Headers */,
749DC27B1D40827B009E1C91 /* WXMonitor.h in Headers */,
77E659DA1C07F594008B8775 /* WXDomModule.h in Headers */,
74EF31AD1DE58BE200667A07 /* WXURLRewriteDefaultImpl.h in Headers */,
+ 744D61101E49979000B624B3 /* WXFooterComponent.h in Headers */,
+ 744D61141E4AF23E00B624B3 /* WXDiffUtil.h in Headers */,
74862F791E02B88D00B7A041 /* JSValue+Weex.h in Headers */,
2A1F57B71C75C6A600B58017 /* WXTextInputComponent.h in Headers */,
C4F012791E1502A6003378D0 /* SRWebSocket+Weex.h in Headers */,
@@ -1198,6 +1258,8 @@
74A4BA961CB365D100195969 /* WXAppConfiguration.h in Headers */,
7461F8921CFB373100F62D44 /* WXLayer.h in Headers */,
594C28931CF9E61A009793A4 /* WXAnimationModule.h in Headers */,
+ 745B2D701E5A8E1E0092D38A /* WXSectionDataController.h in Headers */,
+ 745B2D6E1E5A8E1E0092D38A /* WXRecyclerUpdateController.h in Headers */,
D3FC0DF71C508B2A002B9E31 /* WXTimerModule.h in Headers */,
D312CE3B1C730DEB00046D68 /* WXWebComponent.h in Headers */,
741081261CEDB4EC001BC6E5 /* WXComponent_internal.h in Headers */,
@@ -1240,10 +1302,13 @@
D33451081D3E19480083598A /* WXCanvasComponent.h in Headers */,
74B8BEFE1DC47B72004A6027 /* WXRootView.h in Headers */,
77E65A111C155EA8008B8775 /* WXImageComponent.h in Headers */,
+ 745B2D6C1E5A8E1E0092D38A /* WXRecyclerDataController.h in Headers */,
+ 745B2D681E5A8E1E0092D38A /* WXMultiColumnLayout.h in Headers */,
2A60CE9C1C91733E00857B9F /* WXSwitchComponent.h in Headers */,
DCDFED011E68238F00C228D7 /* WXJSExceptionProtocol.h in Headers */,
2A4445BF1CA8FD56009E7C6D /* WXTextComponentProtocol.h in Headers */,
746319021C60AFC100EFEBD4 /* WXThreadSafeCounter.h in Headers */,
+ 744D610C1E49978200B624B3 /* WXHeaderComponent.h in Headers */,
77D1613C1C02DEA60010B15B /* WXJSCoreBridge.h in Headers */,
74D205201E091B8000128F44 /* WXCallJSMethod.h in Headers */,
741DFE061DDD9B30009B020F /* UIBezierPath+Weex.h in Headers */,
@@ -1460,6 +1525,9 @@
C4F0127C1E1502A6003378D0 /* WXWebSocketDefaultImpl.m in Sources */,
77E65A0E1C155E99008B8775 /* WXDivComponent.m in Sources */,
2A60CE9D1C91733E00857B9F /* WXSwitchComponent.m in Sources */,
+ 744D61111E49979000B624B3 /* WXFooterComponent.m in Sources */,
+ 745B2D6F1E5A8E1E0092D38A /* WXRecyclerUpdateController.m in Sources */,
+ 745B2D6B1E5A8E1E0092D38A /* WXRecyclerComponent.m in Sources */,
2A837AB71CD9DE9200AEDF03 /* WXRefreshComponent.m in Sources */,
74A4BA9B1CB3BAA100195969 /* WXThreadSafeMutableDictionary.m in Sources */,
77E65A1A1C155F25008B8775 /* WXScrollerComponent.m in Sources */,
@@ -1488,6 +1556,7 @@
74AD99851D5B0E59008F0336 /* WXPolyfillSet.m in Sources */,
D317338D1C57257000BB7539 /* WXTransform.m in Sources */,
7461F8A91CFC33A800F62D44 /* WXThreadSafeMutableArray.m in Sources */,
+ 745B2D6D1E5A8E1E0092D38A /* WXRecyclerDataController.m in Sources */,
2AC750251C7565690041D390 /* WXIndicatorComponent.m in Sources */,
591DD3311D23AD5800BE8709 /* WXErrorView.m in Sources */,
59D3CA4B1CFC3CE1008835DC /* NSTimer+Weex.m in Sources */,
@@ -1500,6 +1569,7 @@
C4D872261E5DDF7500E39BC1 /* WXBoxShadow.m in Sources */,
746319031C60AFC100EFEBD4 /* WXThreadSafeCounter.m in Sources */,
74A4BAA71CB4F98300195969 /* WXStreamModule.m in Sources */,
+ 744D610D1E49978200B624B3 /* WXHeaderComponent.m in Sources */,
59597F991D2A041700EE9317 /* WXDebugLoggerBridge.m in Sources */,
77E659F21C0C3612008B8775 /* WXModuleFactory.m in Sources */,
DCF343681E49CAEE00A2FB34 /* WXJSExceptionInfo.m in Sources */,
@@ -1508,6 +1578,7 @@
2A919DA71E321F1F006EB6B5 /* WXBridgeMethod.m in Sources */,
DCAB35FF1D658EB700C0EA70 /* WXRuleManager.m in Sources */,
77D161251C02DDD10010B15B /* WXSDKInstance.m in Sources */,
+ 744D61151E4AF23E00B624B3 /* WXDiffUtil.m in Sources */,
74EF31AE1DE58BE200667A07 /* WXURLRewriteDefaultImpl.m in Sources */,
C4B3D6D51E6954300013F38D /* WXEditComponent.m in Sources */,
C4C30DE81E1B833D00786B6C /* WXComponent+PseudoClassManagement.m in Sources */,
@@ -1519,6 +1590,7 @@
D334510D1D3E19B80083598A /* WXCanvasModule.m in Sources */,
741081241CED6756001BC6E5 /* WXComponentFactory.m in Sources */,
D362F9501C83EDA20003F546 /* WXWebViewModule.m in Sources */,
+ 745B2D711E5A8E1E0092D38A /* WXSectionDataController.m in Sources */,
2A1F57B81C75C6A600B58017 /* WXTextInputComponent.m in Sources */,
74CC7A1D1C2BC5F800829368 /* WXCellComponent.m in Sources */,
74862F821E03A24500B7A041 /* WXComponentMethod.m in Sources */,
@@ -1554,6 +1626,7 @@
C4B834271DE69B09007AD27E /* WXPickerModule.m in Sources */,
C4F0127A1E1502A6003378D0 /* SRWebSocket+Weex.m in Sources */,
59970D2F1E0D228D0049F535 /* WXComponent+GradientColor.m in Sources */,
+ 745B2D691E5A8E1E0092D38A /* WXMultiColumnLayout.m in Sources */,
77D161391C02DE940010B15B /* WXBridgeManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.h b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.h
new file mode 100644
index 0000000..f107db0
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.h
@@ -0,0 +1,42 @@
+/**
+ * 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 <UIKit/UIKit.h>
+#import "WXLength.h"
+
+extern NSString * const kCollectionSupplementaryViewKindHeader;
+
+@protocol WXMultiColumnLayoutDelegate <NSObject>
+
+- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView insetForLayout:(UICollectionViewLayout *)collectionViewLayout;
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView contentWidthForLayout:(UICollectionViewLayout *)collectionViewLayout;
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section;
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForHeaderInSection:(NSInteger)section;
+
+@end
+
+@interface WXMultiColumnLayout : UICollectionViewLayout
+
+@property (nonatomic, weak) id<WXMultiColumnLayoutDelegate> delegate;
+
+@property (nonatomic, strong) WXLength *columnCount;
+
+@property (nonatomic, strong) WXLength *columnWidth;
+
+@property (nonatomic, assign) float columnGap;
+
+@property (nonatomic, assign, readonly) CGFloat computedColumnWidth;
+@property (nonatomic, assign, readonly) int computedColumnCount;
+@property (nonatomic, assign, readonly) CGFloat computedHeaderWidth;
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.m
new file mode 100644
index 0000000..f5d641e
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.m
@@ -0,0 +1,388 @@
+/**
+ * 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 "WXMultiColumnLayout.h"
+#import "NSArray+Weex.h"
+#import "WXUtility.h"
+#import "WXAssert.h"
+
+void computeColumnWidthAndCount(float availableWidth, WXLength *columnCount, WXLength *columnWidth, float columnGap, int *N, float *W)
+{
+ /* Pseudo-algorithm according to
+ * https://www.w3.org/TR/css3-multicol/
+ * Note that, in most cases, only one of \u2018column-width\u2019 and \u2018column-count\u2019 affect the layout.
+ * If \u2018column-width\u2019 has a value other than \u2018auto\u2019, \u2018column-count\u2019 indicates the maximum number of columns.
+ **/
+ if (columnWidth.isAuto && columnCount.isAuto) {
+ WXAssert(NO, @"Unsupport both of column-width and column-count being auto.");
+ return;
+ }
+
+ if (columnWidth.isAuto && !columnCount.isAuto) {
+ *N = columnCount.intValue;
+ *W = MAX(0, (availableWidth - ((*N -1) * columnGap)) / *N);
+ }
+
+ if (!columnWidth.isAuto && columnCount.isAuto) {
+ *N = MAX(1, WXFloorPixelValue((availableWidth + columnGap) / (columnWidth.floatValue + columnGap)));
+ *W = ((availableWidth + columnGap) / *N) - columnGap;
+ }
+
+ if (!columnWidth.isAuto && !columnCount.isAuto) {
+ *N = MIN(columnCount.intValue, WXFloorPixelValue((availableWidth + columnGap) / (columnWidth.floatValue + columnGap)));
+ *W = ((availableWidth + columnGap) / *N) - columnGap;
+ }
+}
+
+NSString * const kCollectionSupplementaryViewKindHeader = @"WXCollectionSupplementaryViewKindHeader";
+NSString * const kMultiColumnLayoutHeader = @"WXMultiColumnLayoutHeader";
+NSString * const kMultiColumnLayoutCell = @"WXMultiColumnLayoutCell";
+
+@interface WXMultiColumnLayoutHeaderAttributes : UICollectionViewLayoutAttributes
+
+@property (nonatomic, assign) BOOL isSticky;
+
+@end
+
+@implementation WXMultiColumnLayoutHeaderAttributes
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ WXMultiColumnLayoutHeaderAttributes *copy = [super copyWithZone:zone];
+ copy.isSticky = self.isSticky;
+
+ return copy;
+}
+
+@end
+
+@interface WXMultiColumnLayout ()
+
+@property (nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary<id, UICollectionViewLayoutAttributes *> *> *layoutAttributes;
+@property (nonatomic, strong) NSMutableArray<NSNumber *> *columnsMaxHeights;
+
+@property (nonatomic, assign, readwrite) CGFloat computedColumnWidth;
+@property (nonatomic, assign, readwrite) int computedColumnCount;
+
+@end
+
+@implementation WXMultiColumnLayout
+
+- (instancetype)init
+{
+ if (self = [super init]) {
+ _layoutAttributes = [NSMutableDictionary dictionary];
+ _columnsMaxHeights = [NSMutableArray array];
+ }
+
+ return self;
+}
+
+#pragma mark - Public Accessors
+
+- (void)setColumnCount:(WXLength *)columnCount
+{
+ if (!(columnCount.isAuto && _columnCount.isAuto) || _columnCount.intValue != columnCount.intValue) {
+ _columnCount = columnCount;
+ [self _cleanComputed];
+ }
+}
+
+- (void)setColumnWidth:(WXLength *)columnWidth
+{
+ if (!(columnWidth.isAuto && _columnWidth.isAuto) || _columnWidth.floatValue != columnWidth.floatValue) {
+ _columnWidth = columnWidth;
+ [self _cleanComputed];
+ }
+}
+
+- (void)setColumnGap:(float)columnGap
+{
+ if (_columnGap != columnGap) {
+ _columnGap = columnGap;
+ [self _cleanComputed];
+ }
+}
+
+- (CGFloat)computedColumnWidth
+{
+ if (!_computedColumnWidth && !_computedColumnCount) {
+ [self _computeColumnWidthAndCount];
+ }
+
+ return _computedColumnWidth;
+}
+
+- (int)computedColumnCount
+{
+ if (!_computedColumnWidth && !_computedColumnCount) {
+ [self _computeColumnWidthAndCount];
+ }
+
+ return _computedColumnCount;
+}
+
+- (CGFloat)computedHeaderWidth
+{
+ UIEdgeInsets insets = [self.delegate collectionView:self.collectionView insetForLayout:self];
+ return self.contentWidth - (insets.left + insets.right);
+}
+
+#pragma mark - Methods to Override for UICollectionViewLayout
+
+- (void)prepareLayout
+{
+ [super prepareLayout];
+
+ [self _cleanup];
+
+ NSInteger numberOfSections = [self.collectionView numberOfSections];
+ UIEdgeInsets insets = [self.delegate collectionView:self.collectionView insetForLayout:self];
+
+ float columnWidth = self.computedColumnWidth;
+ int columnCount = self.computedColumnCount;
+ float columnGap = self.columnGap;
+
+ CGFloat currentHeight = insets.top;
+ NSMutableDictionary *headersAttributes = [NSMutableDictionary dictionaryWithCapacity:numberOfSections];
+ NSMutableDictionary *cellAttributes = [NSMutableDictionary dictionary];
+ for (NSInteger i = 0; i < columnCount; i++) {
+ [self.columnsMaxHeights addObject:@(currentHeight)];
+ }
+
+ for (NSInteger section = 0; section < numberOfSections; section++) {
+ CGFloat headerHeight = [self.delegate collectionView:self.collectionView layout:self heightForHeaderInSection:section];
+ // header
+ if (headerHeight > 0) {
+ WXMultiColumnLayoutHeaderAttributes *headerAttributes = [WXMultiColumnLayoutHeaderAttributes layoutAttributesForSupplementaryViewOfKind:kCollectionSupplementaryViewKindHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
+ headerAttributes.frame = CGRectMake(insets.left, currentHeight, self.contentWidth - (insets.left + insets.right), headerHeight);
+ headerAttributes.isSticky = [self.delegate collectionView:self.collectionView layout:self isNeedStickyForHeaderInSection:section];
+ headerAttributes.zIndex = headerAttributes.isSticky ? 1 : 0;
+ headersAttributes[@(section)] = headerAttributes;
+
+ currentHeight = CGRectGetMaxY(headerAttributes.frame);
+ [self _columnsReachToHeight:currentHeight];
+ }
+
+ // cells
+ for (NSInteger item = 0; item < [self.collectionView numberOfItemsInSection:section]; item++) {
+ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
+ CGFloat itemHeight = [self.delegate collectionView:self.collectionView layout:self heightForItemAtIndexPath:indexPath];
+ UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
+ NSUInteger column = [self _minHeightColumnForAllColumns];
+ CGFloat x = insets.left + (columnWidth + columnGap) * column;
+ CGFloat y = [self.columnsMaxHeights[column] floatValue];
+ itemAttributes.frame = CGRectMake(x, y, columnWidth, itemHeight);
+ cellAttributes[indexPath] = itemAttributes;
+
+ self.columnsMaxHeights[column] = @(CGRectGetMaxY(itemAttributes.frame));
+ }
+ }
+
+ currentHeight = [self _maxHeightForAllColumns] + insets.bottom;
+ [self _columnsReachToHeight:currentHeight];
+
+ self.layoutAttributes[kMultiColumnLayoutHeader] = headersAttributes;
+ self.layoutAttributes[kMultiColumnLayoutCell] = cellAttributes;
+}
+
+- (CGSize)collectionViewContentSize
+{
+ NSInteger numberOfSections = [self.collectionView numberOfSections];
+ if (numberOfSections == 0) {
+ return CGSizeZero;
+ }
+
+ return CGSizeMake(self.contentWidth, self.contentHeight);
+}
+
+- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
+{
+ NSMutableArray<WXMultiColumnLayoutHeaderAttributes *> *stickyHeaders = [NSMutableArray array];
+ NSMutableArray<UICollectionViewLayoutAttributes *> *result = [NSMutableArray array];
+
+ [self.layoutAttributes enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSDictionary<id,UICollectionViewLayoutAttributes *> * _Nonnull dictionary, BOOL * _Nonnull stop) {
+ [dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, UICollectionViewLayoutAttributes * _Nonnull attributes, BOOL * _Nonnull stop) {
+ if (attributes.representedElementKind == kCollectionSupplementaryViewKindHeader
+ && [self.delegate collectionView:self.collectionView layout:self isNeedStickyForHeaderInSection:attributes.indexPath.section]) {
+ [stickyHeaders addObject:(WXMultiColumnLayoutHeaderAttributes *)attributes];
+ } else if (CGRectIntersectsRect(rect, attributes.frame)) {
+ [result addObject:attributes];
+ }
+ }];
+ }];
+
+ [stickyHeaders sortUsingComparator:^NSComparisonResult(WXMultiColumnLayoutHeaderAttributes *obj1, WXMultiColumnLayoutHeaderAttributes *obj2) {
+ if (obj1.indexPath.section < obj2.indexPath.section) {
+ return NSOrderedAscending;
+ } else {
+ return NSOrderedDescending;
+ }
+ }];
+
+ for (int i = 0; i < stickyHeaders.count; i++) {
+ WXMultiColumnLayoutHeaderAttributes *header = stickyHeaders[i];
+ [self _adjustStickyForHeaderAttributes:header next:(i == stickyHeaders.count - 1) ? nil : stickyHeaders[i + 1]];
+ [result addObject:header];
+ }
+
+ WXLogDebug(@"return result attributes:%@ for rect:%@", result, NSStringFromCGRect(rect));
+
+ return result;
+}
+
+- (void)_adjustStickyForHeaderAttributes:(WXMultiColumnLayoutHeaderAttributes *)header
+ next:(WXMultiColumnLayoutHeaderAttributes *)nextHeader
+{
+ CGRect bounds = self.collectionView.bounds;
+ CGFloat originY = header.frame.origin.y;
+ CGFloat maxY = nextHeader ? (nextHeader.frame.origin.y - header.frame.size.height) : (CGRectGetMaxY(bounds) - header.frame.size.height);
+ CGFloat currentY = CGRectGetMaxY(bounds) - bounds.size.height + self.collectionView.contentInset.top;
+
+ CGFloat resultY = MIN(MAX(currentY, originY), maxY);
+ CGPoint origin = header.frame.origin;
+ origin.y = resultY;
+
+ header.frame = (CGRect){origin, header.frame.size};
+ header.hidden = NO;
+}
+
+- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
+{
+ if ([elementKind isEqualToString:kCollectionSupplementaryViewKindHeader]) {
+ UICollectionViewLayoutAttributes *attributes = self.layoutAttributes[kMultiColumnLayoutHeader][@(indexPath.section)];
+ WXLogDebug(@"return header attributes:%@ for index path:%@", attributes, indexPath);
+
+ return attributes;
+ }
+
+ return nil;
+}
+
+- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ if (self.layoutAttributes.count == 0) {
+ [self prepareLayout];
+ }
+
+ UICollectionViewLayoutAttributes *attributes = self.layoutAttributes[kMultiColumnLayoutCell][indexPath];
+ WXLogDebug(@"return item attributes:%@ for index path:%@", attributes, indexPath);
+ return attributes;
+}
+
+- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
+{
+ __block BOOL hasStickyHeader = NO;
+ [self.layoutAttributes[kMultiColumnLayoutHeader] enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, UICollectionViewLayoutAttributes * _Nonnull obj, BOOL * _Nonnull stop) {
+ WXMultiColumnLayoutHeaderAttributes *attribute = (WXMultiColumnLayoutHeaderAttributes *)obj;
+ if (attribute.isSticky) {
+ hasStickyHeader = YES;
+ *stop = YES;
+ }
+ }];
+
+ if (hasStickyHeader) {
+ // always return yes no trigger resetting sticky header's frame.
+ return YES;
+ } else {
+ CGRect oldBounds = self.collectionView.bounds;
+ if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+#pragma mark - Private
+
+- (CGFloat)contentWidth
+{
+ return [self.delegate collectionView:self.collectionView contentWidthForLayout:self];
+}
+
+- (CGFloat)contentHeight
+{
+ return [self _maxHeightForAllColumns];
+}
+
+- (void)_computeColumnWidthAndCount
+{
+ UIEdgeInsets insets = [self.delegate collectionView:self.collectionView insetForLayout:self];
+
+ int columnCount;
+ float columnWidth ;
+ float availableWidth = self.contentWidth - (insets.left + insets.right);
+ computeColumnWidthAndCount(availableWidth, self.columnCount, self.columnWidth, self.columnGap, &columnCount, &columnWidth);
+ if (availableWidth <= 0) {
+ return;
+ }
+ WXAssert(columnCount > 0, @"invalid column count");
+ WXAssert(columnWidth > 0, @"invalid column width");
+
+ _computedColumnWidth = columnWidth;
+ _computedColumnCount = columnCount;
+}
+
+- (CGFloat)_maxHeightForAllColumns
+{
+ CGFloat maxHeight = 0.0;
+ for (NSNumber *number in self.columnsMaxHeights) {
+ CGFloat height = [number floatValue];
+ if (height > maxHeight) {
+ maxHeight = height;
+ }
+ }
+
+ return maxHeight;
+}
+
+- (NSUInteger)_minHeightColumnForAllColumns
+{
+ __block NSUInteger index = 0;
+ __block CGFloat minHeight = FLT_MAX;
+
+ [self.columnsMaxHeights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ CGFloat height = [obj floatValue];
+ if (height < minHeight) {
+ minHeight = height;
+ index = idx;
+ }
+ }];
+
+ return index;
+}
+
+- (void)_columnsReachToHeight:(CGFloat)height
+{
+ for (NSInteger i = 0; i < self.columnsMaxHeights.count; i ++) {
+ self.columnsMaxHeights[i] = @(height);
+ }
+}
+
+- (void)_cleanup
+{
+ [self.layoutAttributes removeAllObjects];
+ [self.columnsMaxHeights removeAllObjects];
+}
+
+- (void)_cleanComputed
+{
+ _computedColumnWidth = 0;
+ _computedColumnCount = 0;
+}
+
+- (void)invalidateLayout
+{
+ [super invalidateLayout];
+
+ [self _cleanComputed];
+}
+
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.h b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.h
new file mode 100644
index 0000000..2b13e4b
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.h
@@ -0,0 +1,12 @@
+/**
+ * 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 WXRecyclerComponent : WXScrollerComponent
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m
new file mode 100644
index 0000000..7dffe8c
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m
@@ -0,0 +1,525 @@
+/**
+ * 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 "WXRecyclerComponent.h"
+#import "WXComponent_internal.h"
+#import "WXSDKInstance_private.h"
+#import "WXRecyclerDataController.h"
+#import "WXRecyclerUpdateController.h"
+#import "WXMultiColumnLayout.h"
+#import "WXHeaderComponent.h"
+#import "WXFooterComponent.h"
+#import "WXCellComponent.h"
+#import "WXAssert.h"
+#import "WXConvert.h"
+
+static NSString * const kCollectionCellReuseIdentifier = @"WXRecyclerCell";
+static NSString * const kCollectionHeaderReuseIdentifier = @"WXRecyclerHeader";
+static float const kRecyclerNormalColumnGap = 32;
+
+typedef enum : NSUInteger {
+ WXRecyclerLayoutTypeMultiColumn,
+ WXRecyclerLayoutTypeFlex,
+ WXRecyclerLayoutTypeGrid,
+} WXRecyclerLayoutType;
+
+@interface WXCollectionView : UICollectionView
+
+@end
+
+@implementation WXCollectionView
+
+- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
+{
+ [super insertSubview:view atIndex:index];
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+ [self.wx_component layoutDidFinish];
+}
+
+@end
+
+@interface WXCollectionViewCell : UICollectionViewCell
+
+@end
+
+@implementation WXCollectionViewCell
+
+- (void)prepareForReuse
+{
+ [super prepareForReuse];
+
+ WXCellComponent *cellComponent = (WXCellComponent *)self.wx_component;
+ if (cellComponent.isRecycle && [cellComponent isViewLoaded] && [self.contentView.subviews containsObject:cellComponent.view]) {
+ [cellComponent _unloadViewWithReusing:YES];
+ }
+}
+
+@end
+
+@interface WXRecyclerComponent () <UICollectionViewDataSource, UICollectionViewDelegate, WXMultiColumnLayoutDelegate, WXRecyclerUpdateControllerDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate>
+
+@property (nonatomic, strong, readonly) WXRecyclerDataController *dataController;
+@property (nonatomic, strong, readonly) WXRecyclerUpdateController *updateController;
+@property (nonatomic, weak, readonly) UICollectionView *collectionView;
+
+@end
+
+@implementation WXRecyclerComponent
+{
+ WXRecyclerLayoutType _layoutType;
+ UICollectionViewLayout *_collectionViewlayout;
+
+ UIEdgeInsets _padding;
+}
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ [self _fillPadding];
+
+ if ([type isEqualToString:@"waterfall"] || (attributes[@"layout"] && [attributes[@"layout"] isEqualToString:@"multi-column"])) {
+ // TODO: abstraction
+ _layoutType = WXRecyclerLayoutTypeMultiColumn;
+ CGFloat scaleFactor = weexInstance.pixelScaleFactor;
+ _collectionViewlayout = [WXMultiColumnLayout new];
+ WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout;
+ layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeAuto];
+ layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0] ? : [WXLength lengthWithInt:1 type:WXLengthTypeFixed];
+ layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeNormal])];
+
+ layout.delegate = self;
+ }
+
+ _dataController = [WXRecyclerDataController new];
+ _updateController = [WXRecyclerUpdateController new];
+ _updateController.delegate = self;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ _collectionView.delegate = nil;
+ _collectionView.dataSource = nil;
+}
+
+#pragma mark - Public Subclass Methods
+
+- (UIView *)loadView
+{
+ return [[WXCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_collectionViewlayout];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _collectionView = (UICollectionView *)self.view;
+ _collectionView.allowsSelection = NO;
+ _collectionView.allowsMultipleSelection = NO;
+ _collectionView.dataSource = self;
+ _collectionView.delegate = self;
+
+ [_collectionView registerClass:[WXCollectionViewCell class] forCellWithReuseIdentifier:kCollectionCellReuseIdentifier];
+ [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:kCollectionSupplementaryViewKindHeader withReuseIdentifier:kCollectionHeaderReuseIdentifier];
+
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+
+ }];
+}
+
+- (void)viewWillUnload
+{
+ [super viewWillUnload];
+
+ _collectionView.dataSource = nil;
+ _collectionView.delegate = nil;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ [super updateAttributes:attributes];
+
+ if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
+ CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
+ WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout;
+ BOOL needUpdateLayout = NO;
+ if (attributes[@"columnWidth"]) {
+ layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor];
+ needUpdateLayout = YES;
+ }
+
+ if (attributes[@"columnCount"]) {
+ layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0];
+
+ needUpdateLayout = YES;
+ }
+ if (attributes[@"columnGap"]) {
+ layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor])];
+ needUpdateLayout = YES;
+ }
+
+ if (needUpdateLayout) {
+ for (WXComponent *component in self.subcomponents) {
+ [component setNeedsLayout];
+ }
+
+ [self.collectionView reloadData];
+ [self.collectionView.collectionViewLayout invalidateLayout];
+ }
+ }
+
+}
+
+- (void)setContentSize:(CGSize)contentSize
+{
+ // Do Nothing
+}
+
+- (void)adjustSticky
+{
+ // Do Nothing, sticky is adjusted by layout
+}
+
+#pragma mark - Private Subclass Methods
+
+- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
+{
+ [super _updateStylesOnComponentThread:styles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
+
+ [self _fillPadding];
+}
+
+- (void)_handleFirstScreenTime
+{
+ // Do Nothing\uff0c firstScreenTime is set by cellDidRendered:
+}
+
+- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
+{
+ [super scrollToComponent:component withOffset:offset animated:animated];
+}
+
+- (void)performUpdatesWithCompletion:(void (^)(BOOL finished))completion
+{
+ WXAssertMainThread();
+
+ //TODO: support completion
+
+ if (![self isViewLoaded]) {
+ completion(NO);
+ }
+
+ NSArray *oldData = [self.dataController.sections copy];
+ NSArray *newData = [self _sectionArrayFromComponents:self.subcomponents];
+
+ [_updateController performUpdatesWithNewData:newData oldData:oldData view:_collectionView];
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ // TODO: refresh loading fixed
+ if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
+ ((WXCellComponent *)subcomponent).delegate = self;
+ } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+ ((WXHeaderComponent *)subcomponent).delegate = self;
+ }
+
+ [super _insertSubcomponent:subcomponent atIndex:index];
+
+ if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
+ && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
+ return;
+ }
+
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+
+ }];
+ });
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ //Here will not insert cell/header/footer's view again
+ if (![subcomponent isKindOfClass:[WXCellComponent class]]
+ && ![subcomponent isKindOfClass:[WXHeaderComponent class]]
+ && ![subcomponent isKindOfClass:[WXFooterComponent class]]) {
+ [super insertSubview:subcomponent atIndex:index];
+ }
+}
+
+#pragma mark - WXRecyclerUpdateControllerDelegate
+
+- (void)updateController:(WXRecyclerUpdateController *)controller willPerformUpdateWithNewData:(NSArray<WXSectionDataController *> *)newData
+{
+ if (newData) {
+ [self.dataController updateData:newData];
+ }
+}
+
+- (void)updateController:(WXRecyclerUpdateController *)controller didPerformUpdateWithFinished:(BOOL)finished
+{
+
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
+{
+ WXLogDebug(@"section number:%zi", [self.dataController numberOfSections]);
+ return [self.dataController numberOfSections];
+}
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
+{
+ NSInteger numberOfItems = [self.dataController numberOfItemsInSection:section];
+
+ WXLogDebug(@"Number of items is %ld in section:%ld", numberOfItems, section);
+
+ return numberOfItems;
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
+
+ WXCollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:kCollectionCellReuseIdentifier forIndexPath:indexPath];
+
+ UIView *contentView = [self.dataController cellForItemAtIndexPath:indexPath];
+
+ cellView.wx_component = contentView.wx_component;
+
+ if (contentView.superview == cellView.contentView) {
+ return cellView;
+ }
+
+ for (UIView *view in cellView.contentView.subviews) {
+ [view removeFromSuperview];
+ }
+
+ [cellView.contentView addSubview:contentView];
+
+ return cellView;
+}
+
+- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
+{
+ UICollectionReusableView *reusableView = nil;
+ if ([kind isEqualToString:kCollectionSupplementaryViewKindHeader]) {
+ reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kCollectionHeaderReuseIdentifier forIndexPath:indexPath];
+ UIView *contentView = [self.dataController viewForHeaderAtIndexPath:indexPath];
+ if (contentView.superview != reusableView) {
+ for (UIView *view in reusableView.subviews) {
+ [view removeFromSuperview];
+ }
+
+ [reusableView addSubview:contentView];
+ }
+ }
+
+ return reusableView;
+}
+
+#pragma mark - UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath);
+}
+
+- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
+}
+
+#pragma mark - WXMultiColumnLayoutDelegate
+
+- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView insetForLayout:(UICollectionViewLayout *)collectionViewLayout
+{
+ return _padding;
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView contentWidthForLayout:(UICollectionViewLayout *)collectionViewLayout
+{
+ return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ CGSize itemSize = [self.dataController sizeForItemAtIndexPath:indexPath];
+ return itemSize.height;
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section
+{
+ CGSize headerSize = [self.dataController sizeForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
+ return headerSize.height;
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForHeaderInSection:(NSInteger)section
+{
+ return [self.dataController isStickyForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
+}
+
+#pragma mark - WXHeaderRenderDelegate
+
+- (float)headerWidthForLayout:(WXHeaderComponent *)header
+{
+ if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
+ return ((WXMultiColumnLayout *)_collectionViewlayout).computedHeaderWidth;
+ }
+
+ return 0.0;
+}
+
+- (void)headerDidLayout:(WXHeaderComponent *)header
+{
+ WXPerformBlockOnMainThread(^{
+ [self.collectionView.collectionViewLayout invalidateLayout];
+ });
+}
+
+- (void)headerDidRemove:(WXHeaderComponent *)header
+{
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+
+ }];
+ });
+}
+
+#pragma mark - WXCellRenderDelegate
+
+- (float)containerWidthForLayout:(WXCellComponent *)cell
+{
+ if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
+ return ((WXMultiColumnLayout *)_collectionViewlayout).computedColumnWidth;
+ }
+
+ return 0.0;
+}
+
+- (void)cellDidLayout:(WXCellComponent *)cell
+{
+ BOOL previousLayoutComplete = cell.isLayoutComplete;
+ cell.isLayoutComplete = YES;
+ WXPerformBlockOnMainThread(^{
+ if (previousLayoutComplete) {
+ [self.updateController reloadItemsAtIndexPath:[self.dataController indexPathForCell:cell]];
+ } else {
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+ }];
+ }
+ });
+}
+
+- (void)cellDidRendered:(WXCellComponent *)cell
+{
+
+}
+
+- (void)cellDidRemove:(WXCellComponent *)cell
+{
+ if (cell.isLayoutComplete) {
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+ }];
+ });
+ }
+}
+
+- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
+{
+ if (cell.isLayoutComplete) {
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+ }];
+ });
+ }
+}
+
+#pragma makrk - private
+
+- (float)_floatValueForColumnGap:(WXLength *)gap
+{
+ if (gap.isNormal) {
+ return kRecyclerNormalColumnGap * self.weexInstance.pixelScaleFactor;
+ } else {
+ return gap.floatValue;
+ }
+}
+
+- (void)_fillPadding
+{
+ UIEdgeInsets padding = {
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT])
+ };
+
+ if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
+ _padding = padding;
+ [self setNeedsLayout];
+
+ for (WXComponent *component in self.subcomponents) {
+ [component setNeedsLayout];
+ }
+
+ if (_collectionView) {
+ WXPerformBlockOnMainThread(^{
+ [_collectionView.collectionViewLayout invalidateLayout];
+ });
+ }
+ }
+}
+
+- (NSArray<WXSectionDataController *> *)_sectionArrayFromComponents:(NSArray<WXComponent *> *)components
+{
+ NSMutableArray<WXSectionDataController *> *sectionArray = [NSMutableArray array];
+ NSMutableArray<WXCellComponent *> *cellArray = [NSMutableArray array];
+ WXSectionDataController *currentSection;
+
+ for (int i = 0; i < components.count; i++) {
+ if (!currentSection) {
+ currentSection = [WXSectionDataController new];
+ }
+
+ WXComponent* component = components[i];
+
+ if ([component isKindOfClass:[WXHeaderComponent class]]) {
+ if (i != 0) {
+ currentSection.cellComponents = [cellArray copy];
+ [sectionArray addObject:currentSection];
+ currentSection = [WXSectionDataController new];
+ [cellArray removeAllObjects];
+ }
+ currentSection.headerComponent = (WXHeaderComponent *)component;
+ } else if ([component isKindOfClass:[WXCellComponent class]]
+ && ((WXCellComponent *)component).isLayoutComplete) {
+ [cellArray addObject:(WXCellComponent *)component];
+ } else if ([component isKindOfClass:[WXFooterComponent class]]) {
+ currentSection.footerComponent = component;
+ }
+
+ if (i == components.count - 1 && cellArray.count > 0) {
+ currentSection.cellComponents = [cellArray copy];
+ [sectionArray addObject:currentSection];
+ }
+ }
+
+ return sectionArray;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.h b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.h
new file mode 100644
index 0000000..3b85155
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.h
@@ -0,0 +1,34 @@
+/**
+ * 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 "WXSectionDataController.h"
+
+@interface WXRecyclerDataController : NSObject
+
+@property (nonatomic, strong, readonly) NSArray<WXSectionDataController *> *sections;
+
+- (void)updateData:(NSArray<WXSectionDataController *> *)newData;
+
+- (NSInteger)numberOfSections;
+
+- (NSInteger)numberOfItemsInSection:(NSInteger)section;
+
+- (UIView *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+- (CGSize)sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+- (UIView *)viewForHeaderAtIndexPath:(NSIndexPath *)indexPath;
+
+- (CGSize)sizeForHeaderAtIndexPath:(NSIndexPath *)indexPath;
+
+- (BOOL)isStickyForHeaderAtIndexPath:(NSIndexPath *)indexPath;
+
+- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell;
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.m
new file mode 100644
index 0000000..aa5732d
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.m
@@ -0,0 +1,114 @@
+/**
+ * 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 "WXRecyclerDataController.h"
+#import "WXCellComponent.h"
+#import "NSArray+Weex.h"
+#import "WXAssert.h"
+
+@interface WXRecyclerDataController ()
+
+@property (nonatomic, strong, readwrite) NSArray<WXSectionDataController *> *sections;
+@property (nonatomic, strong, readonly) NSMapTable<WXCellComponent *, NSIndexPath*> *cellToIndexPathTable;
+
+@end
+
+@implementation WXRecyclerDataController
+
+- (instancetype)init
+{
+ if (self = [super init]) {
+ _sections = [NSArray new];
+ _cellToIndexPathTable = [NSMapTable weakToStrongObjectsMapTable];
+ }
+
+ return self;
+}
+
+#pragma mark - Public
+
+- (void)updateData:(NSArray<WXSectionDataController *> *)newData
+{
+ WXAssertMainThread();
+
+ [self cleanup];
+ _sections = [newData copy];
+
+ [newData enumerateObjectsUsingBlock:^(WXSectionDataController * _Nonnull controller, NSUInteger idx, BOOL * _Nonnull stop) {
+ [controller.cellComponents enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull obj, NSUInteger idx2, BOOL * _Nonnull stop) {
+ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx2 inSection:idx];
+ [_cellToIndexPathTable setObject:indexPath forKey:obj];
+ }];
+ }];
+}
+
+- (NSInteger)numberOfSections
+{
+ WXAssertMainThread();
+ return self.sections.count;
+}
+
+- (NSInteger)numberOfItemsInSection:(NSInteger)section
+{
+ WXSectionDataController *sectionController = [self dataControllerForSection:section];
+ WXAssert(sectionController, @"No section controller found for section:%ld", section);
+
+ return [sectionController numberOfItems];
+}
+
+- (UIView *)cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXSectionDataController *sectionController = [self dataControllerForSection:indexPath.section];
+ UIView *contentView = [sectionController cellForItemAtIndex:indexPath.item];
+
+ return contentView;
+}
+
+- (CGSize)sizeForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXSectionDataController *sectionController = [self dataControllerForSection:indexPath.section];
+ return [sectionController sizeForItemAtIndex:indexPath.item];
+}
+
+- (UIView *)viewForHeaderAtIndexPath:(NSIndexPath *)indexPath;
+{
+ WXSectionDataController *sectionController = [self dataControllerForSection:indexPath.section];
+ return [sectionController viewForHeaderAtIndex:indexPath.item];
+}
+
+- (CGSize)sizeForHeaderAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXSectionDataController *sectionController = [self dataControllerForSection:indexPath.section];
+ return [sectionController sizeForHeaderAtIndex:indexPath.item];
+}
+
+- (BOOL)isStickyForHeaderAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXSectionDataController *sectionController = [self dataControllerForSection:indexPath.section];
+ return [sectionController isStickyForHeaderAtIndex:indexPath.item];
+}
+
+- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell
+{
+ return [_cellToIndexPathTable objectForKey:cell];
+}
+
+#pragma mark - Private
+
+- (WXSectionDataController *)dataControllerForSection:(NSInteger)section
+{
+ WXAssertMainThread();
+ return [self.sections wx_safeObjectAtIndex:section];
+}
+
+- (void)cleanup
+{
+ [_cellToIndexPathTable removeAllObjects];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.h b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.h
new file mode 100644
index 0000000..e376c19
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.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>
+#import "WXSectionDataController.h"
+@class WXRecyclerUpdateController;
+
+@protocol WXRecyclerUpdateControllerDelegate <NSObject>
+
+- (void)updateController:(WXRecyclerUpdateController *)controller willPerformUpdateWithNewData:(NSArray<WXSectionDataController *> *)newData;
+
+- (void)updateController:(WXRecyclerUpdateController *)controller didPerformUpdateWithFinished:(BOOL)finished;
+
+@end
+
+@interface WXRecyclerUpdateController : NSObject
+
+@property (nonatomic, weak) id<WXRecyclerUpdateControllerDelegate> delegate;
+
+- (void)performUpdatesWithNewData:(NSArray<WXSectionDataController *> *)newData
+ oldData:(NSArray<WXSectionDataController *> *)oldData
+ view:(UICollectionView *)collectionView;
+
+- (void)reloadItemsAtIndexPath:(NSIndexPath *)indexPath;
+
+@end
+
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.m
new file mode 100644
index 0000000..31c9dd0
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.m
@@ -0,0 +1,248 @@
+/**
+ * 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 "WXRecyclerUpdateController.h"
+#import "WXCellComponent.h"
+#import "WXAssert.h"
+#import "WXLog.h"
+#import "WXDiffUtil.h"
+#import "NSArray+Weex.h"
+
+@interface WXRecyclerDiffResult : NSObject
+
+@property (nonatomic, strong, readonly) NSIndexSet *insertSections;
+@property (nonatomic, strong, readonly) NSIndexSet *deleteSections;
+@property (nonatomic, strong, readonly) NSIndexSet *reloadSections;
+
+@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 WXRecyclerDiffResult
+
+- (instancetype)initWithInsertSections:(NSIndexSet *)insertSections
+ deleteSections:(NSIndexSet *)deletesSections
+ reloadSections:(NSIndexSet *)reloadSections
+ insertIndexPaths:(NSMutableSet<NSIndexPath *> *)insertIndexPaths
+ deleteIndexPaths:(NSMutableSet<NSIndexPath *> *)deleteIndexPaths
+ reloadIndexPaths:(NSMutableSet<NSIndexPath *> *)reloadIndexPaths
+{
+ if (self = [super init]) {
+ _insertSections = [insertSections copy];
+ _deleteSections = [deletesSections copy];
+ _reloadSections = [reloadSections copy];
+ _insertIndexPaths = [insertIndexPaths copy];
+ _deleteIndexPaths = [deleteIndexPaths copy];
+ _reloadIndexPaths = [reloadIndexPaths copy];
+ }
+
+ return self;
+}
+
+- (BOOL)hasChanges
+{
+ return _insertSections.count > 0 || _deleteSections.count > 0 || _reloadSections.count > 0 || _insertIndexPaths.count > 0 || _deleteIndexPaths.count > 0 || _reloadIndexPaths.count > 0;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; insert sections: %@; delete sections: %@; reload sections: %@; insert index paths: %@; delete index paths: %@; reload index paths: %@", NSStringFromClass([self class]), self,_insertSections, _deleteSections, _reloadSections, _insertIndexPaths, _deleteIndexPaths, _reloadIndexPaths];
+}
+
+@end
+
+@interface WXRecyclerUpdateController ()
+
+@property (nonatomic, copy) NSArray<WXSectionDataController *> *theNewData;
+@property (nonatomic, copy) NSArray<WXSectionDataController *> *theOldData;
+@property (nonatomic, weak) UICollectionView *collectionView;
+@property (nonatomic, strong) NSMutableSet<NSIndexPath *> *reloadIndexPaths;
+@property (nonatomic, assign) BOOL isUpdating;
+
+@end
+
+@implementation WXRecyclerUpdateController
+
+- (void)performUpdatesWithNewData:(NSArray<WXSectionDataController *> *)newData oldData:(NSArray<WXSectionDataController *> *)oldData view:(UICollectionView *)collectionView
+{
+ if (!collectionView) {
+ return;
+ }
+
+ self.theNewData = newData;
+ self.theOldData = oldData;
+ self.collectionView = collectionView;
+
+ [self checkUpdates];
+}
+
+- (void)reloadItemsAtIndexPath:(NSIndexPath *)indexPath
+{
+ if (!indexPath) {
+ return;
+ }
+
+ if (!_reloadIndexPaths) {
+ _reloadIndexPaths = [NSMutableSet set];
+ }
+
+ [_reloadIndexPaths addObject:indexPath];
+
+ [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<WXSectionDataController *> *newData = [self.theNewData copy];
+ NSArray<WXSectionDataController *> *oldData = [self.theOldData copy];
+
+ [self cleanup];
+
+ WXRecyclerDiffResult *diffResult = [self diffWithNewData:newData oldData:oldData];
+ if (![diffResult hasChanges] && self.reloadIndexPaths.count == 0) {
+ return;
+ }
+
+ void (^updates)() = ^{
+ [UIView setAnimationsEnabled:NO];
+ WXLogDebug(@"UICollectionView update:%@", diffResult);
+ [self applyUpdate:diffResult toCollectionView:self.collectionView];
+ };
+
+ void (^completion)(BOOL) = ^(BOOL finished) {
+ [UIView setAnimationsEnabled:YES];
+ self.isUpdating = NO;
+ [self.delegate updateController:self didPerformUpdateWithFinished:finished];
+ [self.reloadIndexPaths removeAllObjects];
+ [self checkUpdates];
+ };
+
+ self.isUpdating = YES;
+
+ if (!self.delegate) {
+ return;
+ }
+
+ [self.delegate updateController:self willPerformUpdateWithNewData:newData];
+
+ NSLog(@"Diff result:%@", diffResult);
+ [collectionView performBatchUpdates:updates completion:completion];
+}
+
+- (void)cleanup
+{
+ self.theNewData = nil;
+ self.theOldData = nil;
+}
+
+- (WXRecyclerDiffResult *)diffWithNewData:(NSArray<WXSectionDataController *> *)newData
+ oldData:(NSArray<WXSectionDataController *> *)oldData
+{
+ NSMutableIndexSet *reloadSections = [NSMutableIndexSet indexSet];
+ NSMutableSet<NSIndexPath *> *reloadIndexPaths = [NSMutableSet set];
+ NSMutableSet<NSIndexPath *> *deleteIndexPaths = [NSMutableSet set];
+ NSMutableSet<NSIndexPath *> *insertIndexPaths = [NSMutableSet set];
+
+ WXDiffResult *sectionDiffResult = [WXDiffUtil diffWithMinimumDistance:newData oldArray:oldData];
+
+ WXLogDebug(@"section diff result:%@", sectionDiffResult);
+
+ [sectionDiffResult.inserts enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
+ WXSectionDataController *newSection = [newData wx_safeObjectAtIndex:idx];
+ [newSection.cellComponents enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull obj, NSUInteger idx2, BOOL * _Nonnull stop) {
+ if (obj.isLayoutComplete) {
+ NSIndexPath *insertIndexPath = [NSIndexPath indexPathForItem:idx2 inSection:idx];
+ [insertIndexPaths addObject:insertIndexPath];
+ }
+ }];
+ WXAssert(newSection, @"No section found in new index:%ld");
+ }];
+
+ for (WXDiffUpdateIndex *sectionUpdate in sectionDiffResult.updates) {
+ WXSectionDataController *oldSection = [oldData wx_safeObjectAtIndex:sectionUpdate.oldIndex];
+ WXSectionDataController *newSection = [newData wx_safeObjectAtIndex:sectionUpdate.newIndex];
+ WXAssert(newSection && oldSection, @"No section found in old index:%ld, new index:%ld", sectionUpdate.oldIndex, sectionUpdate.newIndex);
+
+ WXDiffResult *itemDiffResult = [WXDiffUtil diffWithMinimumDistance:newSection.cellComponents oldArray:oldSection.cellComponents];
+ if (![itemDiffResult hasChanges]) {
+ // header or footer need to be updated
+ [reloadSections addIndex:sectionUpdate.oldIndex];
+ } else {
+ for (WXDiffUpdateIndex *update in itemDiffResult.updates) {
+ NSIndexPath *reloadIndexPath = [NSIndexPath indexPathForItem:update.oldIndex inSection:sectionUpdate.oldIndex];
+ [reloadIndexPaths addObject:reloadIndexPath];
+ }
+
+ [itemDiffResult.inserts enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
+ WXCellComponent *cell = [newSection.cellComponents wx_safeObjectAtIndex:idx];
+ if (cell.isLayoutComplete) {
+ NSIndexPath *insertIndexPath = [NSIndexPath indexPathForItem:idx inSection:sectionUpdate.oldIndex];
+ [insertIndexPaths addObject:insertIndexPath];
+ }
+ }];
+
+ [itemDiffResult.deletes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
+ NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForItem:idx inSection:sectionUpdate.oldIndex];
+ [deleteIndexPaths addObject:deleteIndexPath];
+ }];
+ }
+
+ }
+
+ WXRecyclerDiffResult *result = [[WXRecyclerDiffResult alloc] initWithInsertSections:sectionDiffResult.inserts
+ deleteSections:sectionDiffResult.deletes
+ reloadSections:reloadSections
+ insertIndexPaths:insertIndexPaths
+ deleteIndexPaths:deleteIndexPaths
+ reloadIndexPaths:reloadIndexPaths];
+
+ return result;
+}
+
+- (void)applyUpdate:(WXRecyclerDiffResult *)diffResult toCollectionView:(UICollectionView *)collectionView
+{
+ if (!collectionView) {
+ return;
+ }
+
+ [collectionView deleteItemsAtIndexPaths:[diffResult.deleteIndexPaths allObjects]];
+ [collectionView insertItemsAtIndexPaths:[diffResult.insertIndexPaths allObjects]];
+
+ NSSet *reloadIndexPaths = self.reloadIndexPaths ? [diffResult.reloadIndexPaths setByAddingObjectsFromSet:self.reloadIndexPaths] : diffResult.reloadIndexPaths;
+
+ [collectionView reloadItemsAtIndexPaths:[reloadIndexPaths allObjects]];
+
+ [collectionView deleteSections:diffResult.deleteSections];
+ [collectionView insertSections:diffResult.insertSections];
+ [collectionView reloadSections:diffResult.reloadSections];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.h b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.h
new file mode 100644
index 0000000..ae257b3
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.h
@@ -0,0 +1,33 @@
+/**
+ * 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 "WXDiffUtil.h"
+@class WXComponent;
+@class WXCellComponent;
+@class WXHeaderComponent;
+
+@interface WXSectionDataController : NSObject <WXDiffable>
+
+@property (nonatomic, strong) NSArray<WXCellComponent *> *cellComponents;
+@property (nonatomic, strong) WXHeaderComponent *headerComponent;
+@property (nonatomic, strong) WXComponent *footerComponent;
+
+- (NSInteger)numberOfItems;
+
+- (UIView *)cellForItemAtIndex:(NSInteger)index;
+
+- (CGSize)sizeForItemAtIndex:(NSInteger)index;
+
+- (UIView *)viewForHeaderAtIndex:(NSInteger)index;
+
+- (CGSize)sizeForHeaderAtIndex:(NSInteger)index;
+
+- (BOOL)isStickyForHeaderAtIndex:(NSInteger)index;
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/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..8aaa031
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXSectionDataController.m
@@ -0,0 +1,81 @@
+/**
+ * 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"
+
+@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)isEqualToWXObject:(id<WXDiffable>)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/c6fddbea/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 2c40cd8..aff5ab1 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
@@ -7,15 +7,31 @@
*/
#import "WXComponent.h"
-@class WXListComponent;
+#import "WXDiffUtil.h"
+@class WXCellComponent;
-@interface WXCellComponent : WXComponent
+@protocol WXCellRenderDelegate <NSObject>
+
+- (float)containerWidthForLayout:(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 <WXDiffable>
@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, assign) BOOL keepScrollPosition;
+@property (nonatomic, weak) id<WXCellRenderDelegate> delegate;
@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/c6fddbea/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 3c35a0b..24a5a1d 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
@@ -11,10 +11,16 @@
#import "WXCellComponent.h"
#import "WXListComponent.h"
#import "WXComponent_internal.h"
+#import "WXDiffUtil.h"
+
+@interface WXCellComponent ()
+
+@end
@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
@@ -39,12 +45,17 @@
}
+- (BOOL)isEqualToWXObject:(id<WXDiffable>)object
+{
+ return self == object;
+}
+
- (void)_frameDidCalculated:(BOOL)isChanged
{
[super _frameDidCalculated:isChanged];
if (isChanged) {
- [self.list cellDidLayout:self];
+ [self.delegate cellDidLayout:self];
}
}
@@ -55,7 +66,7 @@
[super displayCompletionBlock](layer, finished);
}
- [self.list cellDidRendered:self];
+ [self.delegate cellDidRendered:self];
};
}
@@ -84,8 +95,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 {
@@ -97,7 +108,7 @@
{
[super _removeFromSupercomponent];
- [self.list cellDidRemove:self];
+ [self.delegate cellDidRemove:self];
}
- (void)removeFromSuperview
@@ -107,8 +118,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 containerWidthForLayout: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/c6fddbea/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/c6fddbea/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/c6fddbea/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/c6fddbea/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/c6fddbea/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..1ac21cc
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.h
@@ -0,0 +1,27 @@
+/**
+ * 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 *)header;
+
+- (void)headerDidLayout:(WXHeaderComponent *)header;
+
+- (void)headerDidRemove:(WXHeaderComponent *)header;
+
+@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/c6fddbea/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..33b3040
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
@@ -0,0 +1,68 @@
+/**
+ * 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;
+}
+
+- (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)_removeFromSupercomponent
+{
+ [super _removeFromSupercomponent];
+
+ [self.delegate headerDidRemove: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/c6fddbea/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