You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ac...@apache.org on 2018/04/26 12:40:03 UTC

[11/16] incubator-weex git commit: [WEEX-311] [iOS] use new layoutEngin to replace yoga

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/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
deleted file mode 100644
index d73531c..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#import "WXListComponent.h"
-#import "WXCellComponent.h"
-#import "WXHeaderComponent.h"
-#import "WXComponent.h"
-#import "WXComponent_internal.h"
-#import "NSArray+Weex.h"
-#import "WXAssert.h"
-#import "WXMonitor.h"
-#import "WXUtility.h"
-#import "NSObject+WXSwizzle.h"
-#import "WXSDKInstance_private.h"
-#import "WXRefreshComponent.h"
-#import "WXLoadingComponent.h"
-
-@interface WXTableView : UITableView
-
-@end
-
-@implementation WXTableView
-
-+ (BOOL)requiresConstraintBasedLayout
-{
-    return NO;
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
-{
-    if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
-        return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
-    }
-    else{
-        return YES;
-    }
-}
-
-- (void)layoutSubviews
-{
-    [super layoutSubviews];
-    [self.wx_component layoutDidFinish];
-}
-
-- (void)setContentOffset:(CGPoint)contentOffset
-{
-    // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary.
-    // When UITableView is pulled down and finger releases,contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary.
-    // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator.
-    // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f
-    // Have to reset contentOffset to zero manually here.
-    if (fabs(contentOffset.y) < 0.5) {
-        contentOffset.y = 0;
-    }
-    if (isnan(contentOffset.x)) {
-        contentOffset.x = 0;
-    }
-    if(isnan(contentOffset.y)) {
-        contentOffset.y = 0;
-    }
-    
-    [super setContentOffset:contentOffset];
-}
-
-@end
-
-// WXText is a non-public is not permitted
-@interface WXSectionComponent : NSObject<NSCopying>
-
-@property (nonatomic, strong) WXHeaderComponent *header;
-@property (nonatomic, strong) NSMutableArray<WXCellComponent *> *rows;
-
-@end
-
-@implementation WXSectionComponent
-
-- (instancetype)init
-{
-    if (self = [super init]) {
-        _rows = [NSMutableArray array];
-    }
-    
-    return self;
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
-    WXSectionComponent *newSection = [[[self class] allocWithZone:zone] init];
-    newSection.header = _header;
-    newSection.rows = [_rows mutableCopyWithZone:zone];
-    
-    return newSection;
-}
-
-- (NSString *)description
-{
-    return [NSString stringWithFormat:@"%@\n%@", [_header description], [_rows description]];
-}
-@end
-
-@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate>
-
-@property (nonatomic, assign) NSUInteger currentTopVisibleSection;
-
-@end
-
-@implementation WXListComponent
-{
-    __weak UITableView * _tableView;
-
-    // Only accessed on component thread
-    NSMutableArray<WXSectionComponent *> *_sections;
-    // Only accessed on main thread
-    NSMutableArray<WXSectionComponent *> *_completedSections;
-    NSUInteger _previousLoadMoreRowNumber;
-    // insert & reload & batch
-    NSString *_updataType;
-    
-    BOOL _isUpdating;
-    NSMutableArray<void(^)(void)> *_updates;
-    NSTimeInterval _reloadInterval;
-}
-
-- (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]) {
-        _sections = [NSMutableArray array];
-        _completedSections = [NSMutableArray array];
-        _reloadInterval = attributes[@"reloadInterval"] ? [WXConvert CGFloat:attributes[@"reloadInterval"]]/1000 : 0;
-        _updataType = [WXConvert NSString:attributes[@"updataType"]]?:@"insert";
-        [self fixFlicker];
-    }
-    
-    return self;
-}
-
-- (void)dealloc
-{
-    if (_tableView) {
-        _tableView.delegate = nil;
-        _tableView.dataSource = nil;
-    }
-}
-
-- (UIView *)loadView
-{
-    return [[WXTableView alloc] init];
-}
-
-- (void)viewDidLoad
-{
-    [super viewDidLoad];
-    
-    _tableView = (UITableView *)self.view;
-    _tableView.allowsSelection = NO;
-    _tableView.allowsMultipleSelection = NO;
-    _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
-    _tableView.delegate = self;
-    _tableView.dataSource = self;
-    _tableView.userInteractionEnabled = YES;
-    
-    _tableView.estimatedRowHeight = 0;
-    _tableView.estimatedSectionFooterHeight = 0;
-    _tableView.estimatedSectionHeaderHeight = 0;
-}
-
-- (void)viewWillUnload
-{
-    [super viewWillUnload];
-    
-    _tableView.delegate = nil;
-    _tableView.dataSource = nil;
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
-    [super updateAttributes:attributes];
-    
-    if (attributes[@"reloadInterval"]) {
-        _reloadInterval = [WXConvert CGFloat:attributes[@"reloadInterval"]] / 1000;
-    }
-    if (attributes[@"updataType"]) {
-        _updataType = [WXConvert NSString:attributes[@"updataType"]];
-    }
-}
-
-- (void)setContentSize:(CGSize)contentSize
-{
-    // Do Nothing
-}
-
-- (void)_handleFirstScreenTime
-{
-    // Do Nothing, firstScreenTime is set by cellDidRendered:
-}
-
-- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
-{
-    CGPoint contentOffset = _tableView.contentOffset;
-    CGFloat contentOffsetY = 0;
-    
-    WXComponent *cellComponent = component;
-    CGRect cellRect;
-    while (cellComponent) {
-        if ([cellComponent isKindOfClass:[WXCellComponent class]]) {
-            NSIndexPath *toIndexPath = [self indexPathForCell:(WXCellComponent*)cellComponent sections:_completedSections];
-            cellRect = [_tableView rectForRowAtIndexPath:toIndexPath];
-            break;
-        }
-        if ([cellComponent isKindOfClass:[WXHeaderComponent class]]) {
-            NSUInteger toIndex = [self indexForHeader:(WXHeaderComponent *)cellComponent sections:_completedSections];
-            cellRect = [_tableView rectForSection:toIndex];
-            break;
-        }
-        contentOffsetY += cellComponent.calculatedFrame.origin.y;
-        cellComponent = cellComponent.supercomponent;
-    }
-    
-    contentOffsetY += cellRect.origin.y;
-    contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
-    
-    if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) {
-        contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height;
-    } else {
-        contentOffset.y = contentOffsetY;
-    }
-    
-    [_tableView setContentOffset:contentOffset animated:animated];
-}
-
-
-#pragma mark - Inheritance
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
-    if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
-        ((WXCellComponent *)subcomponent).delegate = self;
-    } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
-        ((WXHeaderComponent *)subcomponent).delegate = self;
-    } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]]
-               && ![subcomponent isKindOfClass:[WXLoadingComponent class]]
-               && subcomponent->_positionType != WXPositionTypeFixed) {
-        WXLogError(@"list only support cell/header/refresh/loading/fixed-component as child.");
-        return;
-    }
-    
-    [super _insertSubcomponent:subcomponent atIndex:index];
-    
-    if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
-        && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
-        // Don't insert section if subcomponent is not header or cell
-        return;
-    }
-    
-    NSIndexPath *indexPath = [self indexPathForSubIndex:index];
-    
-    if ([subcomponent isKindOfClass:[WXHeaderComponent class]] || _sections.count <= indexPath.section) {
-        // conditions to insert section: insert a header or insert first cell of table view
-        // this will be updated by recycler's update controller in the future
-        WXSectionComponent *insertSection = [WXSectionComponent new];
-        BOOL keepScrollPosition = NO;
-        if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
-            WXHeaderComponent *header = (WXHeaderComponent*)subcomponent;
-            insertSection.header = header;
-            keepScrollPosition = header.keepScrollPosition;
-        }
-        
-        NSUInteger insertIndex = indexPath.section;
-        WXSectionComponent *reloadSection;
-        if (insertIndex > 0 && insertIndex <= _sections.count
-            && [subcomponent isKindOfClass:[WXHeaderComponent class]]) {
-            // insert a header in the middle, one section may divide into two
-            // so the original section need to be reloaded
-            NSIndexPath *indexPathBeforeHeader = [self indexPathForSubIndex:index - 1];
-            if (_sections[insertIndex - 1].rows.count != 0 && indexPathBeforeHeader.row < _sections[insertIndex - 1].rows.count - 1) {
-                reloadSection = _sections[insertIndex - 1];
-                NSArray *rowsToSeparate = reloadSection.rows;
-                insertSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(indexPathBeforeHeader.row + 1, rowsToSeparate.count - indexPathBeforeHeader.row - 1)] mutableCopy];
-                reloadSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(0, indexPathBeforeHeader.row + 1)]  mutableCopy];
-            }
-        }
-    
-        [_sections insertObject:insertSection atIndex:insertIndex];
-        WXSectionComponent *completedInsertSection = [insertSection copy];
-        WXSectionComponent *completedReloadSection;
-        if (reloadSection) {
-            completedReloadSection = [reloadSection copy];
-        }
-        
-        [self.weexInstance.componentManager _addUITask:^{
-            WXLogDebug(@"Insert section:%ld", (unsigned long)insertIndex);
-            
-            [UIView performWithoutAnimation:^{
-                
-                @try {
-                    [_tableView beginUpdates];
-                    
-                    [_completedSections insertObject:completedInsertSection atIndex:insertIndex];
-                    if (completedReloadSection) {
-                        WXLogDebug(@"Reload section:%lu", (unsigned long)(insertIndex - 1));
-                        _completedSections[insertIndex - 1] = completedReloadSection;
-                    }
-                    
-                    [self _insertTableViewSectionAtIndex:insertIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
-                    
-                    if (completedReloadSection) {
-                        [_tableView reloadSections:[NSIndexSet indexSetWithIndex:insertIndex - 1] withRowAnimation:UITableViewRowAnimationNone];
-                    }
-                    
-                    [_tableView endUpdates];
-                } @catch (NSException *exception) {
-                    WXLogError(@"list insert component occurs exception %@", exception);
-                } @finally {
-                     // nothing
-                }
-                
-            }];
-            
-        }];
-        
-    }
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
-    //Here will not insert cell or header's view again
-    if (![subcomponent isKindOfClass:[WXCellComponent class]]
-        && ![subcomponent isKindOfClass:[WXHeaderComponent class]]) {
-        [super insertSubview:subcomponent atIndex:index];
-    }
-}
-
-#pragma mark - WXHeaderRenderDelegate
-
-- (float)headerWidthForLayout:(WXHeaderComponent *)cell
-{
-    return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
-}
-
-- (void)headerDidLayout:(WXHeaderComponent *)header
-{
-    [self.weexInstance.componentManager _addUITask:^{
-        // trigger section header update
-        [UIView performWithoutAnimation:^{
-            [_tableView beginUpdates];
-            
-            NSUInteger reloadIndex = [self indexForHeader:header sections:_completedSections];
-            [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
-            
-            [_tableView endUpdates];
-        }];
-    }];
-}
-
-- (void)headerDidRemove:(WXHeaderComponent *)header
-{
-    NSUInteger headerIndex = [self indexForHeader:header sections:_sections];
-    // this will be updated by recycler's update controller in the future
-    WXSectionComponent *headerSection = _sections[headerIndex];
-    WXSectionComponent *reloadSection;
-    NSUInteger reloadIndex = -1;
-    BOOL isDeleteSection = NO;
-    if (headerIndex == 0 && headerSection.rows.count > 0) {
-        // delete a header in the first section and the section still has cells
-        // reload the first section
-        reloadIndex = 0;
-        reloadSection = _sections[reloadIndex];
-        _sections[reloadIndex].header = nil;
-    } else if (headerIndex > 0 && headerSection.rows.count > 0) {
-        // delete a header in the middle, two sections merge into one
-        // so one section need to be deleted and the other should be relo
-        isDeleteSection = YES;
-        reloadIndex = headerIndex - 1;
-        reloadSection = _sections[reloadIndex];
-        reloadSection.rows = [[reloadSection.rows arrayByAddingObjectsFromArray:headerSection.rows] mutableCopy];
-        [_sections removeObjectAtIndex:headerIndex];
-    } else {
-        // delete a header with no cell in that section
-        // just delete the section
-        isDeleteSection = YES;
-        [_sections removeObjectAtIndex:headerIndex];
-    }
-    
-    WXSectionComponent *completedReloadSection;
-    if (reloadSection) {
-        completedReloadSection = [reloadSection copy];
-    }
-    BOOL keepScrollPosition = header.keepScrollPosition;
-    
-    [self.weexInstance.componentManager _addUITask:^{
-        if (isDeleteSection) {
-            WXLogDebug(@"delete section:%lu", (unsigned long)headerIndex);
-            [_completedSections removeObjectAtIndex:headerIndex];
-        }
-        
-        if (reloadIndex == 0 && !isDeleteSection) {
-            _completedSections[reloadIndex].header = nil;
-        }
-        
-        if (completedReloadSection) {
-            WXLogDebug(@"Reload section:%lu", (unsigned long)reloadIndex);
-            _completedSections[reloadIndex] = completedReloadSection;
-        }
-        
-        [UIView performWithoutAnimation:^{
-            [_tableView beginUpdates];
-            if (isDeleteSection) {
-                [self _deleteTableViewSectionAtIndex:headerIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
-            }
-            
-            if (completedReloadSection) {
-                [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
-            }
-            
-            [_tableView endUpdates];
-        }];
-        
-    }];
-}
-
-#pragma mark - WXCellRenderDelegate
-
-- (float)containerWidthForLayout:(WXCellComponent *)cell
-{
-    return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
-}
-
-- (void)cellDidRemove:(WXCellComponent *)cell
-{
-    WXAssertComponentThread();
-    
-    NSIndexPath *indexPath = [self indexPathForCell:cell sections:_sections];
-    [self removeCellForIndexPath:indexPath withSections:_sections];
-    
-    [self.weexInstance.componentManager _addUITask:^{
-        [self removeCellForIndexPath:indexPath withSections:_completedSections];
-        
-        WXLogDebug(@"Delete cell:%@ at indexPath:%@", cell.ref, indexPath);
-        if (cell.deleteAnimation == UITableViewRowAnimationNone) {
-            [UIView performWithoutAnimation:^{
-                [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
-            }];
-        } else {
-            [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.deleteAnimation];
-        }
-    }];
-}
-
-- (void)cellDidLayout:(WXCellComponent *)cell
-{
-    WXAssertComponentThread() ;
-    
-    NSUInteger index = [self.subcomponents indexOfObject:cell];
-    NSIndexPath *indexPath = [self indexPathForSubIndex:index];
-
-    NSInteger sectionNum = indexPath.section;
-    if (sectionNum >= [_sections count] || sectionNum < 0) {
-        // try to protect sectionNum out of range.
-        return;
-    }
-    NSInteger row = indexPath.row;
-    NSMutableArray *sections = _sections;
-    WXSectionComponent *section = sections[sectionNum];
-    WXAssert(section, @"no section found for section number:%ld", sectionNum);
-    NSMutableArray *completedSections;
-    BOOL isReload = [section.rows containsObject:cell];
-    if (!isReload && row > [section.rows count]) {
-        // protect crash when row out of bounds
-        return ;
-    }
-    if (!isReload) {
-        [section.rows insertObject:cell atIndex:row];
-        // deep copy
-        completedSections = [[NSMutableArray alloc] initWithArray:sections copyItems:YES];;
-    }
-    
-    [self.weexInstance.componentManager _addUITask:^{
-        if (!isReload) {
-            WXLogDebug(@"Insert cell:%@ at indexPath:%@", cell.ref, indexPath);
-            _completedSections = completedSections;
-            if (cell.insertAnimation == UITableViewRowAnimationNone) {
-                [UIView performWithoutAnimation:^{
-                    [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
-                }];
-            } else {
-                [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.insertAnimation];
-            }
-        } else {
-            WXLogInfo(@"Reload cell:%@ at indexPath:%@", cell.ref, indexPath);
-            [UIView performWithoutAnimation:^{
-                [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
-                [self handleAppear];
-            }];
-        }
-    }];
-}
-
-- (void)cellDidRendered:(WXCellComponent *)cell
-{
-    WXAssertMainThread();
-    
-    if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) {
-        // improve performance
-        return;
-    }
-    
-    NSIndexPath *indexPath = [self indexPathForCell:cell sections:_completedSections];
-    if (!indexPath || indexPath.section >= [_tableView numberOfSections] ||
-        indexPath.row < 0 || indexPath.row >= [_tableView numberOfRowsInSection:indexPath.section]) {
-        WXLogWarning(@"Rendered cell:%@ out of range, sections:%@", cell, _completedSections);
-        return;
-    }
-    
-    CGRect cellRect = [_tableView rectForRowAtIndexPath:indexPath];
-    if (cellRect.origin.y + cellRect.size.height >= _tableView.frame.size.height) {
-        WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
-    }
-    
-    if (self.weexInstance.onRenderProgress) {
-        CGRect renderRect = [_tableView convertRect:cellRect toView:self.weexInstance.rootView];
-        self.weexInstance.onRenderProgress(renderRect);
-    }
-}
-
-- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
-{
-    WXAssertComponentThread();
-    
-    NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections];
-    NSIndexPath *toIndexPath = [self indexPathForSubIndex:index];
-    if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) {
-        WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]);
-        return;
-    }
-    [self removeCellForIndexPath:fromIndexPath withSections:_sections];
-    [self insertCell:cell forIndexPath:toIndexPath withSections:_sections];
-    
-    [self.weexInstance.componentManager _addUITask:^{
-        if (_reloadInterval > 0) {
-            // use [UITableView reloadData] to do batch updates, will move to recycler's update controller
-            __weak typeof(self) weakSelf = self;
-            if (!_updates) {
-                _updates = [NSMutableArray array];
-            }
-            [_updates addObject:^{
-                __strong typeof(weakSelf) strongSelf = weakSelf;
-                [strongSelf removeCellForIndexPath:fromIndexPath withSections:strongSelf->_completedSections];
-                [strongSelf insertCell:cell forIndexPath:toIndexPath withSections:strongSelf->_completedSections];
-            }];
-            
-            [self checkReloadData];
-        } else {
-            [self removeCellForIndexPath:fromIndexPath withSections:_completedSections];
-            [self insertCell:cell forIndexPath:toIndexPath withSections:_completedSections];
-            [UIView performWithoutAnimation:^{
-                @try {
-                    [_tableView beginUpdates];
-                    [_tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
-                    [self handleAppear];
-                    [_tableView endUpdates];
-                }@catch(NSException * exception){
-                    WXLogDebug(@"move cell exception: %@", [exception description]);
-                }@finally {
-                    // do nothing
-                }
-            }];
-        }
-    }];
-}
-
-- (void)checkReloadData
-{
-    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reloadInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
-        if (_isUpdating || _updates.count == 0) {
-            return ;
-        }
-        
-        _isUpdating = YES;
-        NSArray *updates = [_updates copy];
-        [_updates removeAllObjects];
-        for (void(^update)(void) in updates) {
-            update();
-        }
-        [_tableView reloadData];
-        _isUpdating = NO;
-        
-        [self checkReloadData];
-    });
-}
-
-- (void)addStickyComponent:(WXComponent *)sticky
-{
-    
-}
-
-- (void)removeStickyComponent:(WXComponent *)sticky
-{
-
-}
-#pragma mark - TableView delegate
-
-- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
-{
-    
-}
-
-- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
-{
-    WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
-    NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
-    if (![visibleIndexPaths containsObject:indexPath]) {
-        if (cell.contentView.subviews.count > 0) {
-            UIView *wxCellView = [cell.contentView.subviews firstObject];
-            // Must invoke synchronously otherwise it will remove the view just added.
-            WXCellComponent *cellComponent = (WXCellComponent *)wxCellView.wx_component;
-            if (cellComponent.isRecycle) {
-                [wxCellView.wx_component _unloadViewWithReusing:YES];
-            }
-        }
-    }
-}
-
-- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
-{
-    WXCellComponent *cell = [self cellForIndexPath:indexPath];
-    return cell.calculatedFrame.size.height;
-}
-
-- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
-{
-    WXHeaderComponent *header = ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).header;
-    if (header) {
-        return header.calculatedFrame.size.height;
-    } else {
-        return 0.0;
-    }
-}
-
-- (void)scrollViewDidScroll:(UIScrollView *)scrollView
-{
-    [super scrollViewDidScroll:scrollView];
-    if ([[_tableView indexPathsForVisibleRows] count] > 0) {
-        NSIndexPath *topCellPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
-        if (self.currentTopVisibleSection != topCellPath.section) {
-            if (self.currentTopVisibleSection) {
-                WXSectionComponent *removeSection = [_sections wx_safeObjectAtIndex:self.currentTopVisibleSection];
-                if (removeSection.header && [removeSection.header.events containsObject:@"unsticky"]) {
-                    [removeSection.header fireEvent:@"unsticky" params:nil];
-                }
-            }
-            self.currentTopVisibleSection = topCellPath.section;
-            WXSectionComponent *showSection = [_sections wx_safeObjectAtIndex:topCellPath.section];
-            if (showSection.header && [showSection.header.events containsObject:@"sticky"]) {
-                [showSection.header fireEvent:@"sticky" params:nil];
-            }
-        }
-    }
-}
-
-- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
-{
-    WXHeaderComponent *header = ((WXSectionComponent *)_completedSections[section]).header;
-    WXLogDebug(@"header view for section %ld:%@", (long)section, header.view);
-    return header.view;
-}
-
-#pragma mark - TableView Data Source
-
-- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
-{
-    return _completedSections.count;
-}
-
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-{
-    return ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).rows.count;
-}
-
-- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
-{
-    WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
-    static NSString *reuseIdentifier = @"WXTableViewCell";
-    
-    UITableViewCell *cellView = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
-    if (!cellView) {
-        cellView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
-        cellView.backgroundColor = [UIColor clearColor];
-    }
-    
-    WXCellComponent *cell = [self cellForIndexPath:indexPath];
-    
-    if (cell.zIndex) {
-        cellView.layer.zPosition = [WXConvert CGFloat:cell.zIndex];
-    }
-    
-    if (!cell) {
-        return cellView;
-    }
-    
-    if (cell.view.superview == cellView.contentView) {
-        return cellView;
-    }
-    
-    for (UIView *view in cellView.contentView.subviews) {
-        [view removeFromSuperview];
-    }
-    
-    [cellView.contentView addSubview:cell.view];
-    
-    [cellView setAccessibilityIdentifier:cell.view.accessibilityIdentifier];
-    
-    WXLogDebug(@"Created cell:%@ view:%@ cellView:%@ at indexPath:%@", cell.ref, cell.view, cellView, indexPath);
-    return cellView;
-}
-
-#pragma mark - Load More Event
-
-- (void)setLoadmoreretry:(NSUInteger)loadmoreretry
-{
-    if (loadmoreretry != self.loadmoreretry) {
-        _previousLoadMoreRowNumber = 0;
-    }
-    
-    [super setLoadmoreretry:loadmoreretry];
-}
-
-- (void)loadMore
-{
-    [super loadMore];
-    
-    _previousLoadMoreRowNumber = [self totalNumberOfRows];
-}
-
-- (BOOL)isNeedLoadMore
-{
-    BOOL superNeedLoadMore = [super isNeedLoadMore];
-    return superNeedLoadMore && _previousLoadMoreRowNumber != [self totalNumberOfRows];
-}
-
-- (NSUInteger)totalNumberOfRows
-{
-    NSUInteger rowNumber = 0;
-    NSUInteger sectionCount = [_tableView numberOfSections];
-    for (int section = 0; section < sectionCount; section ++) {
-        rowNumber += [_tableView numberOfRowsInSection:section];
-    }
-    
-    return rowNumber;
-}
-
-- (void)resetLoadmore{
-    [super resetLoadmore];
-    _previousLoadMoreRowNumber=0;
-}
-
-#pragma mark Private
-
-- (WXCellComponent *)cellForIndexPath:(NSIndexPath *)indexPath
-{
-    WXSectionComponent *section = [_completedSections wx_safeObjectAtIndex:indexPath.section];
-    if (!section) {
-        WXLogError(@"No section found for num:%ld, completed sections:%ld", (long)indexPath.section, (unsigned long)_completedSections.count);
-        return nil;
-    }
-    
-    WXCellComponent *cell = [section.rows wx_safeObjectAtIndex:indexPath.row];
-    if (!cell) {
-        WXLogError(@"No cell found for num:%ld, completed rows:%ld", (long)indexPath.row, (unsigned long)section.rows.count);
-        return nil;
-    }
-    
-    return cell;
-}
-
-- (void)insertCell:(WXCellComponent *)cell forIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
-{
-    WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
-    if (indexPath.row > [section.rows count] || indexPath.row < 0) {
-        WXLogError(@"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
-        return;
-    }
-    WXAssert(section, @"inserting cell at indexPath:%@ section has not been inserted to list before, sections:%@", indexPath, sections);
-    WXAssert(indexPath.row <= section.rows.count, @"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
-    [section.rows insertObject:cell atIndex:indexPath.row];
-}
-
-- (void)removeCellForIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
-{
-    WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
-    if (0 == [section.rows count]) {
-        return;
-    }
-    WXAssert(section, @"Removing cell at indexPath:%@ has not been inserted to cell list before, sections:%@", indexPath, sections);
-    WXAssert(indexPath.row < section.rows.count, @"Removing cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
-    [section.rows removeObjectAtIndex:indexPath.row];
-}
-
-- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell sections:(NSMutableArray<WXSectionComponent *> *)sections
-{
-    __block NSIndexPath *indexPath;
-    [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull sectionStop) {
-        [section.rows enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull row, NSUInteger rowIndex, BOOL * _Nonnull stop) {
-            if (row == cell) {
-                indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
-                *stop = YES;
-                *sectionStop = YES;
-            }
-        }];
-    }];
-    
-    return indexPath;
-}
-
-- (NSUInteger)indexForHeader:(WXHeaderComponent *)header sections:(NSMutableArray<WXSectionComponent *> *)sections
-{
-    __block NSUInteger index;
-    [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull stop) {
-        if (section.header == header) {
-            index = sectionIndex;
-            *stop = YES;
-        }
-    }];
-    
-    return index;
-}
-
-- (NSIndexPath *)indexPathForSubIndex:(NSUInteger)index
-{
-    NSInteger section = 0;
-    NSInteger row = -1;
-    WXComponent *firstComponent;
-    for (int i = 0; i <= index; i++) {
-        WXComponent* component = [self.subcomponents wx_safeObjectAtIndex:i];
-        if (!component) {
-            continue;
-        }
-        if (([component isKindOfClass:[WXHeaderComponent class]]
-            || [component isKindOfClass:[WXCellComponent class]])
-            && !firstComponent) {
-            firstComponent = component;
-        }
-        
-        if (component != firstComponent && [component isKindOfClass:[WXHeaderComponent class]]) {
-            section ++;
-            row = -1;
-        }
-        
-        if ([component isKindOfClass:[WXCellComponent class]]) {
-            row ++;
-        }
-    }
-
-    return [NSIndexPath indexPathForRow:row inSection:section];
-}
-
-- (void)_performUpdates:(void(^)(void))updates withKeepScrollPosition:(BOOL)keepScrollPosition adjustmentBlock:(CGFloat(^)(NSIndexPath *topVisibleCell))adjustmentBlock
-{
-    CGFloat adjustment = 0;
-    
-    // keep the scroll position when inserting or deleting sections/rows by adjusting the content offset
-    if (keepScrollPosition) {
-        NSIndexPath *top = _tableView.indexPathsForVisibleRows.firstObject;
-        adjustment = adjustmentBlock(top);
-    }
-    
-    updates();
-    
-    if (keepScrollPosition) {
-        CGPoint afterContentOffset = _tableView.contentOffset;
-        CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + adjustment);
-        _tableView.contentOffset = newContentOffset;
-    }
-    
-    [self handleAppear];
-}
-
-- (void)_insertTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
-    [self _performUpdates:^{
-        [_tableView insertSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
-    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
-        if (section <= top.section) {
-            return [self tableView:_tableView heightForHeaderInSection:section];
-        } else {
-            return 0.0;
-        }
-    }];
-}
-
-- (void)_deleteTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
-    [self _performUpdates:^{
-        [_tableView deleteSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
-    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
-        if (section <= top.section) {
-            return [self tableView:_tableView heightForHeaderInSection:section];
-        } else {
-            return 0.0;
-        }
-    }];
-}
-
-- (void)_insertTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
-    [self _performUpdates:^{
-        if ([_updataType  isEqual: @"reload"]) {
-            [_tableView reloadData];
-        } else {
-            [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
-        }
-    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
-        if (([indexPath compare:top] <= 0) || [_updataType  isEqual: @"reload"]) {
-            return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
-        } else {
-            return 0.0;
-        }
-    }];
-}
-
-- (void)_deleteTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
-    if (!indexPath) {
-        return ;
-    }
-    [self _performUpdates:^{
-        [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
-    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
-        if ([indexPath compare:top] <= 0) {
-            return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
-        } else {
-            return 0.0;
-        }
-    }];
-}
-
-- (void)fixFlicker
-{
-    static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^{
-        // FIXME:(ง •̀_•́)ง┻━┻ Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells.
-        // So if you pull down list while list is rendering, the list will be flickering.
-        // Demo:    
-        // Have to hook _adjustContentOffsetIfNecessary here.
-        // Any other more elegant way?
-        NSString *a = @"ntOffsetIfNe";
-        NSString *b = @"adjustConte";
-        
-        NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a];
-        [[self class] weex_swizzle:[WXTableView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)];
-    });
-}
-
-- (void)fixedFlickerSelector
-{
-    // DO NOT delete this method.
-}
-
-
-@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
new file mode 100644
index 0000000..bd4eb64
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
@@ -0,0 +1,997 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "WXListComponent.h"
+#import "WXCellComponent.h"
+#import "WXHeaderComponent.h"
+#import "WXComponent.h"
+#import "WXComponent_internal.h"
+#import "NSArray+Weex.h"
+#import "WXAssert.h"
+#import "WXMonitor.h"
+#import "WXUtility.h"
+#import "NSObject+WXSwizzle.h"
+#import "WXSDKInstance_private.h"
+#import "WXRefreshComponent.h"
+#import "WXLoadingComponent.h"
+#import "WXScrollerComponent+Layout.h"
+
+@interface WXTableView : UITableView
+
+@end
+
+@implementation WXTableView
+
++ (BOOL)requiresConstraintBasedLayout
+{
+    return NO;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+    if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
+        return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
+    }
+    else{
+        return YES;
+    }
+}
+
+- (void)layoutSubviews
+{
+    [super layoutSubviews];
+    [self.wx_component layoutDidFinish];
+}
+
+- (void)setContentOffset:(CGPoint)contentOffset
+{
+    // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary.
+    // When UITableView is pulled down and finger releases,contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary.
+    // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator.
+    // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f
+    // Have to reset contentOffset to zero manually here.
+    if (fabs(contentOffset.y) < 0.5) {
+        contentOffset.y = 0;
+    }
+    if (isnan(contentOffset.x)) {
+        contentOffset.x = 0;
+    }
+    if(isnan(contentOffset.y)) {
+        contentOffset.y = 0;
+    }
+    
+    [super setContentOffset:contentOffset];
+}
+
+@end
+
+// WXText is a non-public is not permitted
+@interface WXSectionComponent : NSObject<NSCopying>
+
+@property (nonatomic, strong) WXHeaderComponent *header;
+@property (nonatomic, strong) NSMutableArray<WXCellComponent *> *rows;
+
+@end
+
+@implementation WXSectionComponent
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _rows = [NSMutableArray array];
+    }
+    
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    WXSectionComponent *newSection = [[[self class] allocWithZone:zone] init];
+    newSection.header = _header;
+    newSection.rows = [_rows mutableCopyWithZone:zone];
+    
+    return newSection;
+}
+
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"%@\n%@", [_header description], [_rows description]];
+}
+@end
+
+@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate>
+
+@property (nonatomic, assign) NSUInteger currentTopVisibleSection;
+
+@end
+
+@implementation WXListComponent
+{
+    __weak UITableView * _tableView;
+
+    // Only accessed on component thread
+    NSMutableArray<WXSectionComponent *> *_sections;
+    // Only accessed on main thread
+    NSMutableArray<WXSectionComponent *> *_completedSections;
+    NSUInteger _previousLoadMoreRowNumber;
+    // insert & reload & batch
+    NSString *_updataType;
+    
+    BOOL _isUpdating;
+    NSMutableArray<void(^)(void)> *_updates;
+    NSTimeInterval _reloadInterval;
+}
+
+- (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]) {
+        _sections = [NSMutableArray array];
+        _completedSections = [NSMutableArray array];
+        _reloadInterval = attributes[@"reloadInterval"] ? [WXConvert CGFloat:attributes[@"reloadInterval"]]/1000 : 0;
+        _updataType = [WXConvert NSString:attributes[@"updataType"]]?:@"insert";
+        [self fixFlicker];
+    }
+    
+    return self;
+}
+
+- (void)dealloc
+{
+    if (_tableView) {
+        _tableView.delegate = nil;
+        _tableView.dataSource = nil;
+    }
+}
+
+- (UIView *)loadView
+{
+    return [[WXTableView alloc] init];
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+    
+    _tableView = (UITableView *)self.view;
+    _tableView.allowsSelection = NO;
+    _tableView.allowsMultipleSelection = NO;
+    _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+    _tableView.delegate = self;
+    _tableView.dataSource = self;
+    _tableView.userInteractionEnabled = YES;
+    
+    _tableView.estimatedRowHeight = 0;
+    _tableView.estimatedSectionFooterHeight = 0;
+    _tableView.estimatedSectionHeaderHeight = 0;
+}
+
+- (void)viewWillUnload
+{
+    [super viewWillUnload];
+    
+    _tableView.delegate = nil;
+    _tableView.dataSource = nil;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+    [super updateAttributes:attributes];
+    
+    if (attributes[@"reloadInterval"]) {
+        _reloadInterval = [WXConvert CGFloat:attributes[@"reloadInterval"]] / 1000;
+    }
+    if (attributes[@"updataType"]) {
+        _updataType = [WXConvert NSString:attributes[@"updataType"]];
+    }
+}
+
+- (void)setContentSize:(CGSize)contentSize
+{
+    // Do Nothing
+}
+
+- (void)_handleFirstScreenTime
+{
+    // Do Nothing, firstScreenTime is set by cellDidRendered:
+}
+
+- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
+{
+    CGPoint contentOffset = _tableView.contentOffset;
+    CGFloat contentOffsetY = 0;
+    
+    WXComponent *cellComponent = component;
+    CGRect cellRect;
+    while (cellComponent) {
+        if ([cellComponent isKindOfClass:[WXCellComponent class]]) {
+            NSIndexPath *toIndexPath = [self indexPathForCell:(WXCellComponent*)cellComponent sections:_completedSections];
+            cellRect = [_tableView rectForRowAtIndexPath:toIndexPath];
+            break;
+        }
+        if ([cellComponent isKindOfClass:[WXHeaderComponent class]]) {
+            NSUInteger toIndex = [self indexForHeader:(WXHeaderComponent *)cellComponent sections:_completedSections];
+            cellRect = [_tableView rectForSection:toIndex];
+            break;
+        }
+        contentOffsetY += cellComponent.calculatedFrame.origin.y;
+        cellComponent = cellComponent.supercomponent;
+    }
+    
+    contentOffsetY += cellRect.origin.y;
+    contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
+    
+    if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) {
+        contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height;
+    } else {
+        contentOffset.y = contentOffsetY;
+    }
+    
+    [_tableView setContentOffset:contentOffset animated:animated];
+}
+
+
+#pragma mark - Inheritance
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+    if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
+        ((WXCellComponent *)subcomponent).delegate = self;
+    } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+        ((WXHeaderComponent *)subcomponent).delegate = self;
+    } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]]
+               && ![subcomponent isKindOfClass:[WXLoadingComponent class]]
+               && subcomponent->_positionType != WXPositionTypeFixed) {
+        WXLogError(@"list only support cell/header/refresh/loading/fixed-component as child.");
+        return;
+    }
+    
+    [super _insertSubcomponent:subcomponent atIndex:index];
+    
+    if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
+        && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
+        // Don't insert section if subcomponent is not header or cell
+        return;
+    }
+    
+    NSIndexPath *indexPath = [self indexPathForSubIndex:index];
+    
+    if ([subcomponent isKindOfClass:[WXHeaderComponent class]] || _sections.count <= indexPath.section) {
+        // conditions to insert section: insert a header or insert first cell of table view
+        // this will be updated by recycler's update controller in the future
+        WXSectionComponent *insertSection = [WXSectionComponent new];
+        BOOL keepScrollPosition = NO;
+        if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+            WXHeaderComponent *header = (WXHeaderComponent*)subcomponent;
+            insertSection.header = header;
+            keepScrollPosition = header.keepScrollPosition;
+        }
+        
+        NSUInteger insertIndex = indexPath.section;
+        WXSectionComponent *reloadSection;
+        if (insertIndex > 0 && insertIndex <= _sections.count
+            && [subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+            // insert a header in the middle, one section may divide into two
+            // so the original section need to be reloaded
+            NSIndexPath *indexPathBeforeHeader = [self indexPathForSubIndex:index - 1];
+            if (_sections[insertIndex - 1].rows.count != 0 && indexPathBeforeHeader.row < _sections[insertIndex - 1].rows.count - 1) {
+                reloadSection = _sections[insertIndex - 1];
+                NSArray *rowsToSeparate = reloadSection.rows;
+                insertSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(indexPathBeforeHeader.row + 1, rowsToSeparate.count - indexPathBeforeHeader.row - 1)] mutableCopy];
+                reloadSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(0, indexPathBeforeHeader.row + 1)]  mutableCopy];
+            }
+        }
+    
+        [_sections insertObject:insertSection atIndex:insertIndex];
+        WXSectionComponent *completedInsertSection = [insertSection copy];
+        WXSectionComponent *completedReloadSection;
+        if (reloadSection) {
+            completedReloadSection = [reloadSection copy];
+        }
+        
+        [self.weexInstance.componentManager _addUITask:^{
+            WXLogDebug(@"Insert section:%ld", (unsigned long)insertIndex);
+            
+            [UIView performWithoutAnimation:^{
+                
+                @try {
+                    [_tableView beginUpdates];
+                    
+                    [_completedSections insertObject:completedInsertSection atIndex:insertIndex];
+                    if (completedReloadSection) {
+                        WXLogDebug(@"Reload section:%lu", (unsigned long)(insertIndex - 1));
+                        _completedSections[insertIndex - 1] = completedReloadSection;
+                    }
+                    
+                    [self _insertTableViewSectionAtIndex:insertIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
+                    
+                    if (completedReloadSection) {
+                        [_tableView reloadSections:[NSIndexSet indexSetWithIndex:insertIndex - 1] withRowAnimation:UITableViewRowAnimationNone];
+                    }
+                    
+                    [_tableView endUpdates];
+                } @catch (NSException *exception) {
+                    WXLogError(@"list insert component occurs exception %@", exception);
+                } @finally {
+                     // nothing
+                }
+                
+            }];
+            
+        }];
+        
+    }
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+    //Here will not insert cell or header's view again
+    if (![subcomponent isKindOfClass:[WXCellComponent class]]
+        && ![subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+        [super insertSubview:subcomponent atIndex:index];
+    }
+}
+
+#pragma mark - WXHeaderRenderDelegate
+
+- (float)headerWidthForLayout:(WXHeaderComponent *)cell
+{
+//#ifndef USE_FLEX
+    if(![WXComponent isUseFlex]){
+        return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+    }
+//#else
+    else
+    {
+        return self.flexScrollerCSSNode->getStyleWidth();
+    }
+//#endif
+}
+
+- (void)headerDidLayout:(WXHeaderComponent *)header
+{
+    [self.weexInstance.componentManager _addUITask:^{
+        // trigger section header update
+        [UIView performWithoutAnimation:^{
+            [_tableView beginUpdates];
+            
+            NSUInteger reloadIndex = [self indexForHeader:header sections:_completedSections];
+            [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
+            
+            [_tableView endUpdates];
+        }];
+    }];
+}
+
+- (void)headerDidRemove:(WXHeaderComponent *)header
+{
+    NSUInteger headerIndex = [self indexForHeader:header sections:_sections];
+    // this will be updated by recycler's update controller in the future
+    WXSectionComponent *headerSection = _sections[headerIndex];
+    WXSectionComponent *reloadSection;
+    NSUInteger reloadIndex = -1;
+    BOOL isDeleteSection = NO;
+    if (headerIndex == 0 && headerSection.rows.count > 0) {
+        // delete a header in the first section and the section still has cells
+        // reload the first section
+        reloadIndex = 0;
+        reloadSection = _sections[reloadIndex];
+        _sections[reloadIndex].header = nil;
+    } else if (headerIndex > 0 && headerSection.rows.count > 0) {
+        // delete a header in the middle, two sections merge into one
+        // so one section need to be deleted and the other should be relo
+        isDeleteSection = YES;
+        reloadIndex = headerIndex - 1;
+        reloadSection = _sections[reloadIndex];
+        reloadSection.rows = [[reloadSection.rows arrayByAddingObjectsFromArray:headerSection.rows] mutableCopy];
+        [_sections removeObjectAtIndex:headerIndex];
+    } else {
+        // delete a header with no cell in that section
+        // just delete the section
+        isDeleteSection = YES;
+        [_sections removeObjectAtIndex:headerIndex];
+    }
+    
+    WXSectionComponent *completedReloadSection;
+    if (reloadSection) {
+        completedReloadSection = [reloadSection copy];
+    }
+    BOOL keepScrollPosition = header.keepScrollPosition;
+    
+    [self.weexInstance.componentManager _addUITask:^{
+        if (isDeleteSection) {
+            WXLogDebug(@"delete section:%lu", (unsigned long)headerIndex);
+            [_completedSections removeObjectAtIndex:headerIndex];
+        }
+        
+        if (reloadIndex == 0 && !isDeleteSection) {
+            _completedSections[reloadIndex].header = nil;
+        }
+        
+        if (completedReloadSection) {
+            WXLogDebug(@"Reload section:%lu", (unsigned long)reloadIndex);
+            _completedSections[reloadIndex] = completedReloadSection;
+        }
+        
+        [UIView performWithoutAnimation:^{
+            [_tableView beginUpdates];
+            if (isDeleteSection) {
+                [self _deleteTableViewSectionAtIndex:headerIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
+            }
+            
+            if (completedReloadSection) {
+                [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
+            }
+            
+            [_tableView endUpdates];
+        }];
+        
+    }];
+}
+
+#pragma mark - WXCellRenderDelegate
+
+- (float)containerWidthForLayout:(WXCellComponent *)cell
+{
+//#ifndef USE_FLEX
+    if (![WXComponent isUseFlex]) {
+         return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+    }
+//#else
+    else{
+        return self.flexScrollerCSSNode->getStyleWidth();
+    }
+//#endif
+}
+
+- (void)cellDidRemove:(WXCellComponent *)cell
+{
+    WXAssertComponentThread();
+    
+    NSIndexPath *indexPath = [self indexPathForCell:cell sections:_sections];
+    if(!indexPath){
+        //protect when cell not exist in sections
+        return;
+    }
+    [self removeCellForIndexPath:indexPath withSections:_sections];
+    
+    [self.weexInstance.componentManager _addUITask:^{
+        [self removeCellForIndexPath:indexPath withSections:_completedSections];
+        
+        WXLogDebug(@"Delete cell:%@ at indexPath:%@", cell.ref, indexPath);
+        if (cell.deleteAnimation == UITableViewRowAnimationNone) {
+            [UIView performWithoutAnimation:^{
+                [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
+            }];
+        } else {
+            [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.deleteAnimation];
+        }
+    }];
+}
+
+- (void)cellDidLayout:(WXCellComponent *)cell
+{
+    WXAssertComponentThread() ;
+    
+    NSUInteger index = [self.subcomponents indexOfObject:cell];
+    NSIndexPath *indexPath = [self indexPathForSubIndex:index];
+
+    NSInteger sectionNum = indexPath.section;
+    if (sectionNum >= [_sections count] || sectionNum < 0) {
+        // try to protect sectionNum out of range.
+        return;
+    }
+    NSInteger row = indexPath.row;
+    NSMutableArray *sections = _sections;
+    WXSectionComponent *section = sections[sectionNum];
+    WXAssert(section, @"no section found for section number:%ld", sectionNum);
+    NSMutableArray *completedSections;
+    BOOL isReload = [section.rows containsObject:cell];
+    if (!isReload && row > [section.rows count]) {
+        // protect crash when row out of bounds
+        return ;
+    }
+    if (!isReload) {
+        [section.rows insertObject:cell atIndex:row];
+        // deep copy
+        completedSections = [[NSMutableArray alloc] initWithArray:sections copyItems:YES];;
+    }
+    
+    [self.weexInstance.componentManager _addUITask:^{
+        if (!isReload) {
+            WXLogDebug(@"Insert cell:%@ at indexPath:%@", cell.ref, indexPath);
+            _completedSections = completedSections;
+            if (cell.insertAnimation == UITableViewRowAnimationNone) {
+                [UIView performWithoutAnimation:^{
+                    [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
+                }];
+            } else {
+                [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.insertAnimation];
+            }
+        } else {
+            WXLogInfo(@"Reload cell:%@ at indexPath:%@", cell.ref, indexPath);
+            [UIView performWithoutAnimation:^{
+                [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
+                [self handleAppear];
+            }];
+        }
+    }];
+}
+
+- (void)cellDidRendered:(WXCellComponent *)cell
+{
+    WXAssertMainThread();
+    
+    if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) {
+        // improve performance
+        return;
+    }
+    
+    NSIndexPath *indexPath = [self indexPathForCell:cell sections:_completedSections];
+    if (!indexPath || indexPath.section >= [_tableView numberOfSections] ||
+        indexPath.row < 0 || indexPath.row >= [_tableView numberOfRowsInSection:indexPath.section]) {
+        WXLogWarning(@"Rendered cell:%@ out of range, sections:%@", cell, _completedSections);
+        return;
+    }
+    
+    CGRect cellRect = [_tableView rectForRowAtIndexPath:indexPath];
+    if (cellRect.origin.y + cellRect.size.height >= _tableView.frame.size.height) {
+        WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
+    }
+    
+    if (self.weexInstance.onRenderProgress) {
+        CGRect renderRect = [_tableView convertRect:cellRect toView:self.weexInstance.rootView];
+        self.weexInstance.onRenderProgress(renderRect);
+    }
+}
+
+- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
+{
+    WXAssertComponentThread();
+    
+    NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections];
+    NSIndexPath *toIndexPath = [self indexPathForSubIndex:index];
+    if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) {
+        WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]);
+        return;
+    }
+    [self removeCellForIndexPath:fromIndexPath withSections:_sections];
+    [self insertCell:cell forIndexPath:toIndexPath withSections:_sections];
+    
+    [self.weexInstance.componentManager _addUITask:^{
+        if (_reloadInterval > 0) {
+            // use [UITableView reloadData] to do batch updates, will move to recycler's update controller
+            __weak typeof(self) weakSelf = self;
+            if (!_updates) {
+                _updates = [NSMutableArray array];
+            }
+            [_updates addObject:^{
+                __strong typeof(weakSelf) strongSelf = weakSelf;
+                [strongSelf removeCellForIndexPath:fromIndexPath withSections:strongSelf->_completedSections];
+                [strongSelf insertCell:cell forIndexPath:toIndexPath withSections:strongSelf->_completedSections];
+            }];
+            
+            [self checkReloadData];
+        } else {
+            [self removeCellForIndexPath:fromIndexPath withSections:_completedSections];
+            [self insertCell:cell forIndexPath:toIndexPath withSections:_completedSections];
+            [UIView performWithoutAnimation:^{
+                @try {
+                    [_tableView beginUpdates];
+                    [_tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
+                    [self handleAppear];
+                    [_tableView endUpdates];
+                }@catch(NSException * exception){
+                    WXLogDebug(@"move cell exception: %@", [exception description]);
+                }@finally {
+                    // do nothing
+                }
+            }];
+        }
+    }];
+}
+
+- (void)checkReloadData
+{
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reloadInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        if (_isUpdating || _updates.count == 0) {
+            return ;
+        }
+        
+        _isUpdating = YES;
+        NSArray *updates = [_updates copy];
+        [_updates removeAllObjects];
+        for (void(^update)(void) in updates) {
+            update();
+        }
+        [_tableView reloadData];
+        _isUpdating = NO;
+        
+        [self checkReloadData];
+    });
+}
+
+- (void)addStickyComponent:(WXComponent *)sticky
+{
+    
+}
+
+- (void)removeStickyComponent:(WXComponent *)sticky
+{
+
+}
+#pragma mark - TableView delegate
+
+- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    
+}
+
+- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
+    NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
+    if (![visibleIndexPaths containsObject:indexPath]) {
+        if (cell.contentView.subviews.count > 0) {
+            UIView *wxCellView = [cell.contentView.subviews firstObject];
+            // Must invoke synchronously otherwise it will remove the view just added.
+            WXCellComponent *cellComponent = (WXCellComponent *)wxCellView.wx_component;
+            if (cellComponent.isRecycle) {
+                [wxCellView.wx_component _unloadViewWithReusing:YES];
+            }
+        }
+    }
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    WXCellComponent *cell = [self cellForIndexPath:indexPath];
+    return cell.calculatedFrame.size.height;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
+{
+    WXHeaderComponent *header = ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).header;
+    if (header) {
+        return header.calculatedFrame.size.height;
+    } else {
+        return 0.0;
+    }
+}
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView
+{
+    [super scrollViewDidScroll:scrollView];
+    if ([[_tableView indexPathsForVisibleRows] count] > 0) {
+        NSIndexPath *topCellPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
+        if (self.currentTopVisibleSection != topCellPath.section) {
+            if (self.currentTopVisibleSection) {
+                WXSectionComponent *removeSection = [_sections wx_safeObjectAtIndex:self.currentTopVisibleSection];
+                if (removeSection.header && [removeSection.header.events containsObject:@"unsticky"]) {
+                    [removeSection.header fireEvent:@"unsticky" params:nil];
+                }
+            }
+            self.currentTopVisibleSection = topCellPath.section;
+            WXSectionComponent *showSection = [_sections wx_safeObjectAtIndex:topCellPath.section];
+            if (showSection.header && [showSection.header.events containsObject:@"sticky"]) {
+                [showSection.header fireEvent:@"sticky" params:nil];
+            }
+        }
+    }
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
+{
+    WXHeaderComponent *header = ((WXSectionComponent *)_completedSections[section]).header;
+    WXLogDebug(@"header view for section %ld:%@", (long)section, header.view);
+    return header.view;
+}
+
+#pragma mark - TableView Data Source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+    return _completedSections.count;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+    return ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).rows.count;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
+    static NSString *reuseIdentifier = @"WXTableViewCell";
+    
+    UITableViewCell *cellView = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+    if (!cellView) {
+        cellView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
+        cellView.backgroundColor = [UIColor clearColor];
+    }
+    
+    WXCellComponent *cell = [self cellForIndexPath:indexPath];
+    
+    if (cell.zIndex) {
+        cellView.layer.zPosition = [WXConvert CGFloat:cell.zIndex];
+    }
+    
+    if (!cell) {
+        return cellView;
+    }
+    
+    if (cell.view.superview == cellView.contentView) {
+        return cellView;
+    }
+    
+    for (UIView *view in cellView.contentView.subviews) {
+        [view removeFromSuperview];
+    }
+    
+    [cellView.contentView addSubview:cell.view];
+    
+    [cellView setAccessibilityIdentifier:cell.view.accessibilityIdentifier];
+    
+    WXLogDebug(@"Created cell:%@ view:%@ cellView:%@ at indexPath:%@", cell.ref, cell.view, cellView, indexPath);
+    return cellView;
+}
+
+#pragma mark - Load More Event
+
+- (void)setLoadmoreretry:(NSUInteger)loadmoreretry
+{
+    if (loadmoreretry != self.loadmoreretry) {
+        _previousLoadMoreRowNumber = 0;
+    }
+    
+    [super setLoadmoreretry:loadmoreretry];
+}
+
+- (void)loadMore
+{
+    [super loadMore];
+    
+    _previousLoadMoreRowNumber = [self totalNumberOfRows];
+}
+
+- (BOOL)isNeedLoadMore
+{
+    BOOL superNeedLoadMore = [super isNeedLoadMore];
+    return superNeedLoadMore && _previousLoadMoreRowNumber != [self totalNumberOfRows];
+}
+
+- (NSUInteger)totalNumberOfRows
+{
+    NSUInteger rowNumber = 0;
+    NSUInteger sectionCount = [_tableView numberOfSections];
+    for (int section = 0; section < sectionCount; section ++) {
+        rowNumber += [_tableView numberOfRowsInSection:section];
+    }
+    
+    return rowNumber;
+}
+
+- (void)resetLoadmore{
+    [super resetLoadmore];
+    _previousLoadMoreRowNumber=0;
+}
+
+#pragma mark Private
+
+- (WXCellComponent *)cellForIndexPath:(NSIndexPath *)indexPath
+{
+    WXSectionComponent *section = [_completedSections wx_safeObjectAtIndex:indexPath.section];
+    if (!section) {
+        WXLogError(@"No section found for num:%ld, completed sections:%ld", (long)indexPath.section, (unsigned long)_completedSections.count);
+        return nil;
+    }
+    
+    WXCellComponent *cell = [section.rows wx_safeObjectAtIndex:indexPath.row];
+    if (!cell) {
+        WXLogError(@"No cell found for num:%ld, completed rows:%ld", (long)indexPath.row, (unsigned long)section.rows.count);
+        return nil;
+    }
+    
+    return cell;
+}
+
+- (void)insertCell:(WXCellComponent *)cell forIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
+{
+    WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
+    if (indexPath.row > [section.rows count] || indexPath.row < 0) {
+        WXLogError(@"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
+        return;
+    }
+    WXAssert(section, @"inserting cell at indexPath:%@ section has not been inserted to list before, sections:%@", indexPath, sections);
+    WXAssert(indexPath.row <= section.rows.count, @"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
+    [section.rows insertObject:cell atIndex:indexPath.row];
+}
+
+- (void)removeCellForIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
+{
+    WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
+    if (0 == [section.rows count]) {
+        return;
+    }
+    WXAssert(section, @"Removing cell at indexPath:%@ has not been inserted to cell list before, sections:%@", indexPath, sections);
+    WXAssert(indexPath.row < section.rows.count, @"Removing cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
+    [section.rows removeObjectAtIndex:indexPath.row];
+}
+
+- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell sections:(NSMutableArray<WXSectionComponent *> *)sections
+{
+    __block NSIndexPath *indexPath;
+    [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull sectionStop) {
+        [section.rows enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull row, NSUInteger rowIndex, BOOL * _Nonnull stop) {
+            if (row == cell) {
+                indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
+                *stop = YES;
+                *sectionStop = YES;
+            }
+        }];
+    }];
+    
+    return indexPath;
+}
+
+- (NSUInteger)indexForHeader:(WXHeaderComponent *)header sections:(NSMutableArray<WXSectionComponent *> *)sections
+{
+    __block NSUInteger index;
+    [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull stop) {
+        if (section.header == header) {
+            index = sectionIndex;
+            *stop = YES;
+        }
+    }];
+    
+    return index;
+}
+
+- (NSIndexPath *)indexPathForSubIndex:(NSUInteger)index
+{
+    NSInteger section = 0;
+    NSInteger row = -1;
+    WXComponent *firstComponent;
+    for (int i = 0; i <= index; i++) {
+        WXComponent* component = [self.subcomponents wx_safeObjectAtIndex:i];
+        if (!component) {
+            continue;
+        }
+        if (([component isKindOfClass:[WXHeaderComponent class]]
+            || [component isKindOfClass:[WXCellComponent class]])
+            && !firstComponent) {
+            firstComponent = component;
+        }
+        
+        if (component != firstComponent && [component isKindOfClass:[WXHeaderComponent class]]) {
+            section ++;
+            row = -1;
+        }
+        
+        if ([component isKindOfClass:[WXCellComponent class]]) {
+            row ++;
+        }
+    }
+
+    return [NSIndexPath indexPathForRow:row inSection:section];
+}
+
+- (void)_performUpdates:(void(^)(void))updates withKeepScrollPosition:(BOOL)keepScrollPosition adjustmentBlock:(CGFloat(^)(NSIndexPath *topVisibleCell))adjustmentBlock
+{
+    CGFloat adjustment = 0;
+    
+    // keep the scroll position when inserting or deleting sections/rows by adjusting the content offset
+    if (keepScrollPosition) {
+        NSIndexPath *top = _tableView.indexPathsForVisibleRows.firstObject;
+        adjustment = adjustmentBlock(top);
+    }
+    
+    updates();
+    
+    if (keepScrollPosition) {
+        CGPoint afterContentOffset = _tableView.contentOffset;
+        CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + adjustment);
+        _tableView.contentOffset = newContentOffset;
+    }
+    
+    [self handleAppear];
+}
+
+- (void)_insertTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+    [self _performUpdates:^{
+        [_tableView insertSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
+    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+        if (section <= top.section) {
+            return [self tableView:_tableView heightForHeaderInSection:section];
+        } else {
+            return 0.0;
+        }
+    }];
+}
+
+- (void)_deleteTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+    [self _performUpdates:^{
+        [_tableView deleteSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
+    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+        if (section <= top.section) {
+            return [self tableView:_tableView heightForHeaderInSection:section];
+        } else {
+            return 0.0;
+        }
+    }];
+}
+
+- (void)_insertTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+    [self _performUpdates:^{
+        if ([_updataType  isEqual: @"reload"]) {
+            [_tableView reloadData];
+        } else {
+            [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
+        }
+    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+        if (([indexPath compare:top] <= 0) || [_updataType  isEqual: @"reload"]) {
+            return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
+        } else {
+            return 0.0;
+        }
+    }];
+}
+
+- (void)_deleteTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+    if (!indexPath) {
+        return ;
+    }
+    [self _performUpdates:^{
+        [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
+    } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+        if ([indexPath compare:top] <= 0) {
+            return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
+        } else {
+            return 0.0;
+        }
+    }];
+}
+
+- (void)fixFlicker
+{
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        // FIXME:(ง •̀_•́)ง┻━┻ Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells.
+        // So if you pull down list while list is rendering, the list will be flickering.
+        // Demo:    
+        // Have to hook _adjustContentOffsetIfNecessary here.
+        // Any other more elegant way?
+        NSString *a = @"ntOffsetIfNe";
+        NSString *b = @"adjustConte";
+        
+        NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a];
+        [[self class] weex_swizzle:[WXTableView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)];
+    });
+}
+
+- (void)fixedFlickerSelector
+{
+    // DO NOT delete this method.
+}
+
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m
deleted file mode 100644
index d57fa9a..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#import "WXLoadingComponent.h"
-#import "WXScrollerComponent.h"
-#import "WXLoadingIndicator.h"
-#import "WXComponent_internal.h"
-#import "WXLog.h"
-
-@interface WXLoadingComponent()
-
-@property (nonatomic) BOOL initFinished;
-@property (nonatomic) BOOL loadingEvent;
-@property (nonatomic) BOOL displayState;
-
-@property (nonatomic, weak) WXLoadingIndicator *indicator;
-
-@end
-
-@implementation WXLoadingComponent
-
-- (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) {
-        if (attributes[@"display"]) {
-            if ([attributes[@"display"] isEqualToString:@"show"]) {
-                _displayState = YES;
-            } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
-                _displayState = NO;
-            } else {
-                WXLogError(@"");
-            }
-        }
-        self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
-    }
-    return self;
-}
-
-- (void)viewWillUnload
-{
-    _displayState = NO;
-    _loadingEvent = NO;
-    _initFinished = NO;
-}
-
--(void)updateAttributes:(NSDictionary *)attributes
-{
-    if (attributes[@"display"]) {
-        if ([attributes[@"display"] isEqualToString:@"show"]) {
-            _displayState = YES;
-        } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
-            _displayState = NO;
-        } else {
-            WXLogError(@"");
-        }
-        [self setDisplay];
-    }
-}
-
-- (void)viewDidLoad
-{
-    _initFinished = YES;
-    
-    if (!_displayState) {
-        [_indicator.view setHidden:YES];
-    }
-}
-
-- (void)addEvent:(NSString *)eventName
-{
-    if ([eventName isEqualToString:@"loading"]) {
-        _loadingEvent = YES;
-    }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
-    if ([eventName isEqualToString:@"loading"]) {
-        _loadingEvent = NO;
-    }
-}
-
-- (void)loading
-{
-    if (!_loadingEvent || _displayState)
-        return;
-    
-    [self fireEvent:@"loading" params:nil];
-}
-
-- (void)setDisplay
-{
-    id<WXScrollerProtocol> scrollerProtocol = [self ancestorScroller];
-    if (scrollerProtocol == nil || !_initFinished)
-        return;
-    WXComponent *scroller = (WXComponent*)scrollerProtocol;
-    CGPoint contentOffset = [scrollerProtocol contentOffset];
-    if (_displayState) {
-        contentOffset.y = [scrollerProtocol contentSize].height - scroller.calculatedFrame.size.height + self.calculatedFrame.size.height;
-        [_indicator start];
-    } else {
-        contentOffset.y = contentOffset.y - self.calculatedFrame.size.height;
-        [_indicator stop];
-    }
-    [scrollerProtocol setContentOffset:contentOffset animated:YES];
-}
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
-    if (subcomponent) {
-        [super _insertSubcomponent:subcomponent atIndex:index];
-        if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
-            _indicator = (WXLoadingIndicator*)subcomponent;
-        }
-    }
-}
-
-- (BOOL)displayState
-{
-    return _displayState;
-}
-
-- (void)resizeFrame
-{
-    CGRect rect = self.calculatedFrame;
-    
-    id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
-    if (scrollerProtocol) {
-        rect.origin.y = [scrollerProtocol contentSize].height;
-    }
-    
-    [self.view setFrame:rect];
-}
-
-@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm
new file mode 100644
index 0000000..bb5024f
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "WXLoadingComponent.h"
+#import "WXScrollerComponent.h"
+#import "WXLoadingIndicator.h"
+#import "WXComponent_internal.h"
+#import "WXLog.h"
+#import "WXComponent+Layout.h"
+
+@interface WXLoadingComponent()
+
+@property (nonatomic) BOOL initFinished;
+@property (nonatomic) BOOL loadingEvent;
+@property (nonatomic) BOOL displayState;
+
+@property (nonatomic, weak) WXLoadingIndicator *indicator;
+
+@end
+
+@implementation WXLoadingComponent
+
+- (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) {
+        if (attributes[@"display"]) {
+            if ([attributes[@"display"] isEqualToString:@"show"]) {
+                _displayState = YES;
+            } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
+                _displayState = NO;
+            } else {
+                WXLogError(@"");
+            }
+        }
+//#ifndef USE_FLEX
+        if(![WXComponent isUseFlex])
+        {
+             self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
+        }
+//#else
+        else
+        {
+             self.flexCssNode->setStylePositionType(WeexCore::kAbsolute);
+        }
+       
+//#endif
+    }
+    return self;
+}
+
+- (void)viewWillUnload
+{
+    _displayState = NO;
+    _loadingEvent = NO;
+    _initFinished = NO;
+}
+
+-(void)updateAttributes:(NSDictionary *)attributes
+{
+    if (attributes[@"display"]) {
+        if ([attributes[@"display"] isEqualToString:@"show"]) {
+            _displayState = YES;
+        } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
+            _displayState = NO;
+        } else {
+            WXLogError(@"");
+        }
+        [self setDisplay];
+    }
+}
+
+- (void)viewDidLoad
+{
+    _initFinished = YES;
+    
+    if (!_displayState) {
+        [_indicator.view setHidden:YES];
+    }
+}
+
+- (void)addEvent:(NSString *)eventName
+{
+    if ([eventName isEqualToString:@"loading"]) {
+        _loadingEvent = YES;
+    }
+}
+
+- (void)removeEvent:(NSString *)eventName
+{
+    if ([eventName isEqualToString:@"loading"]) {
+        _loadingEvent = NO;
+    }
+}
+
+- (void)loading
+{
+    if (!_loadingEvent || _displayState)
+        return;
+    
+    [self fireEvent:@"loading" params:nil];
+}
+
+- (void)setDisplay
+{
+    id<WXScrollerProtocol> scrollerProtocol = [self ancestorScroller];
+    if (scrollerProtocol == nil || !_initFinished)
+        return;
+    WXComponent *scroller = (WXComponent*)scrollerProtocol;
+    CGPoint contentOffset = [scrollerProtocol contentOffset];
+    if (_displayState) {
+        contentOffset.y = [scrollerProtocol contentSize].height - scroller.calculatedFrame.size.height + self.calculatedFrame.size.height;
+        [_indicator start];
+    } else {
+        contentOffset.y = contentOffset.y - self.calculatedFrame.size.height;
+        [_indicator stop];
+    }
+    [scrollerProtocol setContentOffset:contentOffset animated:YES];
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+    if (subcomponent) {
+        [super _insertSubcomponent:subcomponent atIndex:index];
+        if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
+            _indicator = (WXLoadingIndicator*)subcomponent;
+        }
+    }
+}
+
+- (BOOL)displayState
+{
+    return _displayState;
+}
+
+- (void)resizeFrame
+{
+    CGRect rect = self.calculatedFrame;
+    
+    id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
+    if (scrollerProtocol) {
+        rect.origin.y = [scrollerProtocol contentSize].height;
+    }
+    
+    [self.view setFrame:rect];
+}
+
+@end