美文网首页
iOS UITableView reloadData数据窜行问题

iOS UITableView reloadData数据窜行问题

作者: AaronYin | 来源:发表于2018-12-21 21:59 被阅读4次

    今天遇到uitableview上拉加载,reloadData后,数据窜行问题。

    1、原因

    因为iOS 11后系统默认开启Self-Sizing,首先要知道Self-Sizing是个什么东东。官方文档是这样解释的:大概就是说我们不用再自己去计算cell的高度了,只要设置好这两个属性,约束好布局,系统会自动计算好cell的高度。
    IOS11以后,Self-Sizing默认开启,包括Headers, footers。如果项目中没使用estimatedRowHeight属性,在IOS11下会有奇奇怪怪的现象,因为IOS11之前,estimatedRowHeight默认为0,Self-Sizing自动打开后,contentSize和contentOffset都可能发生改变。
    所以可以通过以下方式禁用:

    在tableView初始化的地方加入下面代码

        self.tableView.estimatedRowHeight = 0
        self.tableView.estimatedSectionHeaderHeight = 0
        self.tableView.estimatedSectionFooterHeight = 0
    

    2、另一种是缓存cell高度,使用estimatedHeightForRowAt代理方法

    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSNumber *key = @(indexPath.row);
        NSNumber *height = @(cell.frame.size.height);
    
        [self.cellHeightsDictionary setObject:height forKey:key];
    }
    
    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSNumber *key = @(indexPath.row);
        NSNumber *height = [self.cellHeightsDictionary objectForKey:key];
    
        if (height)
        {
            return height.doubleValue;
        }
    
        return UITableViewAutomaticDimension;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return UITableViewAutomaticDimension;
    }
    

    3、但是问题又来了,这个方法虽然能屏蔽掉窜行问题,但是却把系统优化功能屏蔽掉了

    换了一个思路,就是在下拉刷新前记录一下contentOffSet,然后刷新之后在给tableview设置回去。但是遇到两个问题。

    1、 第一个问题

    如果在reloadData后需要立即获取tableview的cell、高度,或者需要滚动tableview,那么,直接在reloadData后执行代码是有可能出问题的。因为reloadDate并不会等待tableview更新结束后才返回,而是立即返回,然后去计算表高度,获取cell等。如果表中的数据非常大,在一个run loop周期没执行完,这时,需要tableview视图数据的操作就会出问题了。

    找到几种解决办法

    //方法一:验证过可以
    [self.tableView reloadData];
    [self.tableView layoutIfNeeded];
    
    //方法二:没验证
    [self.tableView reloadData];
    dispatch_async(dispatch_get_main_queue(), ^{
    });
    
    //方法三:验证过可以
    [UIView animateWithDuration:0 animations:^{
        [self.tableView reloadData];
        
    } completion:^(BOOL finished) {
        NSLog(@"------滚动-----");
    }];
    

    2、第二个问题是,tableview的contentSize是一直变化的,设置contentOffSet也没有用。但是不知道为什么帖子上好像说上面的方法可以,但是我试了不行。

    3、最后试了一种方法,终于完美解决

    就是不用setContentOffset方法,用scrollToRow(at: indexPath, at: .bottom, animated: false)方法

                reloadData()
                layoutIfNeeded()
                scrollToRow(at: indexPath, at: .bottom, animated: false)
    

    4、最终方法

    extension UITableView {
        
        public func reloadData(to indexPath: IndexPath) -> Bool {
            if indexPath.row < 0 || indexPath.section < 0 {
                return false
            }
            if let dataSource = dataSource {
                if dataSource.responds(to: #selector(UITableViewDataSource.numberOfSections(in:))) {
                    let sections = dataSource.numberOfSections!(in: self)
                    if sections <= indexPath.section {
                        return false
                    }
                }
                if dataSource.responds(to: #selector(UITableViewDataSource.tableView(_:numberOfRowsInSection:))) {
                    let rows = dataSource.tableView(self, numberOfRowsInSection: indexPath.section)
                    if rows <= indexPath.row {
                        return false
                    }
                }
                reloadData()
                layoutIfNeeded()
                scrollToRow(at: indexPath, at: .bottom, animated: false)
                return true
            }
            return false
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS UITableView reloadData数据窜行问题

          本文链接:https://www.haomeiwen.com/subject/kizvkqtx.html