使用UITableViewAutomaticDimension时,在UITableView顶部插入行而不修改滚动位置

时间:2016-01-12 00:14:41

标签: ios uitableview uiscrollview

如何将行插入最顶部的UITableView而不会导致其他单元格被按下 - 因此滚动位置似乎根本没有变化?

就像Facebook和Twitter在发送新帖子时一样,它们会插在顶部,但滚动位置仍然是固定的。

我的问题类似于this question。是什么让我的问题与这个问题的独特之处在于我没有使用具有固定行高的表格 - 我使用UITableViewAutomaticDimensionestimatedRowHeight。因此,建议的答案将无效,因为我无法确定行高。

我尝试过这个不考虑行高的解决方案,但重新加载后contentSize仍然不正确,因为contentOffset集不是一样的相对位置 - 细胞仍然被推到插入前的位置。这是因为单元格没有在屏幕上呈现,因此iOS不会为它计算适当的高度,直到它即将出现,因此contentSize不准确。 / p>

CGSize beforeContentSize = tableView.contentSize;
[tableView reloadData];
CGSize afterContentSize = tableView.contentSize;
CGPoint afterContentOffset = tableView.contentOffset;
tableView.contentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + afterContentSize.height - beforeContentSize.height);

Alain指出rectForCellAtIndexPath迫使iOS计算合适的高度。我现在可以确定插入单元格的正确高度,但滚动视图的contentSize仍然不正确,正如我遍历所有单元格并加起来大于{的高度所证明的那样。 {1}}。因此,当我手动设置contentSize.height时,它不会滚动到正确的位置。

原始代码:

contentOffset

在这种情况下,可以采取哪些措施来实现所需的行为?

5 个答案:

答案 0 :(得分:9)

派对晚了,但即使单元格具有动态高度(又名UITableViewAutomaticDimension),也无需迭代单元格来计算其大小,但只有在tableView的最开头添加项目时才有效并且没有标题,只需要一点点数学,就可以适应每种情况:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row == 0 {
            self.getMoreMessages()
        }
}

private func getMoreMessages(){
        var initialOffset = self.tableView.contentOffset.y
        self.tableView.reloadData()
        //@numberOfCellsAdded: number of items added at top of the table 
        self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: numberOfCellsAdded, inSection: 0), atScrollPosition: .Top, animated: false)
        self.tableView.contentOffset.y += initialOffset
}

答案 1 :(得分:1)

此外,单行部分可以实现所需的效果

  

如果某个部分已存在于指定的索引位置,则会将其向下移动到一个索引位置。   https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instm/UITableView/insertSections:withRowAnimation

let idxSet = NSIndexSet(index: 0)
self.verseTable.insertSections(idxSet, withRowAnimation: .Fade)

答案 2 :(得分:1)

遇到同样的问题但仍然没有找到一种优雅的方法来解决它。这就是我的方式,假设我想在当前单元格上方插入moreData.count个单元格。

// insert the new data to self.data
for (NSInteger i = moreData.count-1; i >= 0; i--) {
    [self.data insertObject:moreData[i] atIndex:0];
}

//reload data or insert row at indexPaths
[self.tableView reloadData];

//after 0.1s invoke scrollToRowAtIndexPath:atScrollPosition:animated:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:moreData.count inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
});

优点是在插入新数据之前和之后固定位置。缺点是我们可以看到屏幕闪烁(最上面的单元格显示并向上移动)

答案 3 :(得分:0)

我认为您的解决方案几乎就在那里。您应该将新的Y偏移基于“之前”Y偏移而不是“之后”。

...,beforeContentOffset.y + afterContentSize.height - beforeContentSize.height

(您需要在重新加载数据之前保存beforeContentOffset点)

答案 4 :(得分:0)

我针对 iOS 13和Swift 5 的工作解决方案:

注意:仅在动态像元高度(UITableViewAutomaticDimension / UITableView. automaticDimension)下进行测试。

P.S。这里发布的所有解决方案都没有对我有用。

extension UITableView {
    /**
     Method to use whenever new items should be inserted at the top of the table view.
     The table view maintains its scroll position using this method.
     - warning: Make sure your data model contains the correct count of items before invoking this method.
     - parameter itemCount: The count of items that should be added at the top of the table view.
     - note: Works with `UITableViewAutomaticDimension`.
     - links: https://bluelemonbits.com/2018/08/26/inserting-cells-at-the-top-of-a-uitableview-with-no-scrolling/
     */
    func insertItemsAtTopWithFixedPosition(_ itemCount: Int) {
        layoutIfNeeded() // makes sure layout is set correctly.
        var initialContentOffSet = contentOffset.y

        // If offset is less than 0 due to refresh up gesture, assume 0.
        if initialContentOffSet < 0 {
            initialContentOffSet = 0
        }

        // Reload, scroll and set offset:
        reloadData()
        scrollToRow(
            at: IndexPath(row: itemCount, section: 0),
            at: .top,
            animated: false)
        contentOffset.y += initialContentOffSet
    }
}