TableView w。重新加载新数据时,动态高度会滚动

时间:2018-12-18 10:07:33

标签: swift uitableview

摘要 当在顶部插入新单元格时,具有动态单元格高度的UITableView会随机滚动。

详细说明 作为UITableViewController的一部分,为避免触发reloadData()和滚动时滚动不正确,我基于此堆栈溢出answer进行了以下工作:

var cellHeightsDictionary: [IndexPath: CGFloat] = [:]   

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {  
      cellHeightsDictionary[indexPath] = cell.frame.size.height  
}  

override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {  
  if let height =  cellHeightsDictionary[indexPath] {  
      return height  
  }  
  return UITableView.automaticDimension  
}  

这很好,除了在数据源顶部添加新数据并触发reloadData()时。这样做时,我找不到合适的实现方式来防止表格随机滚动...这是我要解决的问题。

我尝试过的事情

  1. 我尝试使用更新前后contentSize之间的差异,然后更新滚动位置。不幸的是,基于我在Apple文档中所读的内容,使用动态单元格似乎无法信任contentSize且对我不起作用。尽管如此,这还是我的实现之一。

    UIView.performWithoutAnimation { //Detect exact position of the cell in relation to navBar let navBar = navigationController?.navigationBar let cellRectInTable = tableView.rectForRow(at: IndexPath(row: topArticle + countOfAddedItems, section: 0)) let positionVsNavBar = tableView.convert(cellRectInTable.origin, to: navBar) let positionVsTop = tableView.convert(cellRectInTable.origin, to: tableView.superview) let offsetOfCell = positionVsTop.y - positionVsNavBar.y tableView.reloadData() tableView.scrollToRow(at: IndexPath(row: topArticle + countOfAddedItems, section: 0), at: .top, animated: false) tableView.contentOffset.y = tableView.contentOffset.y + offsetOfCell }

  2. 我尝试在添加之前设置tableView.estimatedRowHeight = 0,tableView.estimatedSectionHeaderHeight = 0和tableView.estimatedSectionFooterHeight = 0,但似乎没有任何影响。

  3. 似乎最有效的方法是当我从cellHeightsDictionary获取单元格的高度并根据加法数对其求和时。这有时会完美无瑕,但有时却没有。另外,当添加许多行时,由于未在cellHeightsDictionary中找到值,它可能会崩溃。我尝试了多种变体,但没有使其起作用。同样重要的是,我不确定这为什么能给我最好的结果,所以我可能会遗漏一些明显的东西。我觉得在最后一个实现中的某个地方就是答案,但是我一直找不到它。

    var iteratorCounter = 0
        var heightCum = CGFloat(integerLiteral:0)
        而iteratorCounter       heightCum = heightCum + cellHeightsDictionary [IndexPath(row:iteratorCounter,section:0)]!
          iteratorCounter = iteratorCounter + 1
        }

    UIView.performWithoutAnimation {  
       tableView.reloadData()  
       tableView.layoutIfNeeded()  
       tableView.contentOffset.y = tableView.contentOffset.y + heightCum   
    }
    

我尝试了所有可以想到的事情,并在没有运气的情况下进行了搜索。任何帮助表示赞赏

感谢一百万!

3 个答案:

答案 0 :(得分:1)

作为最后的选择,您可以将表格视图嵌入到新的滚动视图中,然后关闭表格视图的滚动。您可以完全控制滚动行为(尽管全部以编程方式进行)。

在这种情况下,我们必须添加一个与表视图高度相对应的布局约束,并为此约束添加一个出口。

如果单元格视图具有适当的布局,则可以使用systemLayoutSizeFittingSize方法来计算确切的单元格高度。如果单元格具有多个标签(例如动态内容),请尝试使用固定宽度进行计算(在单元格中添加显式宽度约束)。

有了这个,我们可以重新加载表格视图,如下所示:
1)将数据插入数据源
2)重新计算内容高度
3)修改高度约束常量
4)致电layoutIfNeeded
5)重新加载表格视图
6)适当移动scrollview内容偏移=> setContentOffset(_ contentOffset: CGPoint, animated: Bool)

答案 1 :(得分:0)

定义一个变量:

describe('Protractor Login checing ', function() {
    it('should add one and two', function() {
    browser.get('http://localhost:4041/login');
    element(by.model('username')).sendKeys('admin');
    element(by.model('password')).sendKeys('admin');

    element(by.id('login')).click();

    browser.wait(waitForUrlChange("http://localhost:4041/dashboard", 8000, function(){
      browser.getCurrentUrl().then(function (currentUrl) {
          expect(currentUrl.toEqual("http://localhost:4041/dashboard"));
      });
  }));
 });

function waitForUrlChange(url) {
    return function () {
        return browser.getCurrentUrl().then(function (currentUrl) {
            console.log(currentUrl);
            return url === currentUrl;
        });
    }
}

数量可以根据您的要求进行更改

并更新tableView的方法

 var itemHeights = [CGFloat](repeating: UITableViewAutomaticDimension, count: 1000)

答案 2 :(得分:0)

最终成功运行的方法是:

步骤1。将UITableViewCell存储在导航栏下方的变量中,并存储该单元格相对于导航栏的偏移量。

第2步。插入单元格/ reloadData。

第3步:滚动到插入/重新加载之前保存的单元格,然后添加偏移量。

这是代码:

UIView.performWithoutAnimation {
    //Step 1 Detect & Store
    let topWillBeAt = getTopVisibleRow() + countOfAddedItems
    let oldHeightDifferenceBetweenTopRowAndNavBar = heightDifferenceBetweenTopRowAndNavBar()

    //Step 2 Insert
    self.tableView.insertRows(at: arrayOfIndexPaths, with: .none)

    //Step 3 Restore Scrolling
    tableView.scrollToRow(at: IndexPath(row: topWillBeAt, section: 0), at: .top, animated: false)
    tableView.contentOffset.y = tableView.contentOffset.y - oldHeightDifferenceBetweenTopRowAndNavBar


}

及支持功能:

  func getTopVisibleRow () -> Int {
        //We need this to accounts for the translucency below the nav bar
        let navBar = navigationController?.navigationBar
        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)
        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height + 1)
        let accurateIndexPath = tableView.indexPathForRow(at: pointWhereNavBarEnds)
        return accurateIndexPath?.row ?? 0
    }

    func heightDifferenceBetweenTopRowAndNavBar()-> CGFloat{
        let rectForTopRow = tableView.rectForRow(at:IndexPath(row:  getTopVisibleRow(), section: 0))
        let navBar = navigationController?.navigationBar
        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)
        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height)
        let differenceBetweenTopRowAndNavBar = rectForTopRow.origin.y - pointWhereNavBarEnds.y
        return differenceBetweenTopRowAndNavBar


    }