简体   繁体   English

UITableView 刷新而不滚动

[英]UITableView Refresh without scrolling

I have a _TableView with items , and I want to set automatic refresh,and I don't want it to scroll on refresh , lets say user scrolled 2 pages down , and the refresh trigered -> so I want to put the refreshed content to the top of the table without interupting user's scrolling我有一个带有项目的_TableView ,我想设置自动刷新,并且我不希望它在刷新时滚动,假设用户向下滚动了 2 页,并且触发了刷新 -> 所以我想将刷新的内容放到不中断用户滚动的表格顶部

Assume user was on row 18 and now the _dataSource is refreshed so it fetched lets say 4 items , so I want user to stay on the item he was.假设用户在第 18 行,现在_dataSource被刷新,所以它提取了 4 个项目,所以我希望用户留在他所在的项目上。

What would be the best approach to achieve it ??实现它的最佳方法是什么?

For Swift 3+ :对于Swift 3+

You need to save the current offset of the UITableView , then reload and then set the offset back on the UITableView .您需要保存当前的偏移量UITableView ,然后重新再设置偏移回到了UITableView

I have created this function for this purpose:我为此目的创建了这个函数:

func reload(tableView: UITableView) {

    let contentOffset = tableView.contentOffset
    tableView.reloadData()
    tableView.layoutIfNeeded()
    tableView.setContentOffset(contentOffset, animated: false)

}

Simply call it with: reload(tableView: self.tableView)简单地调用它: reload(tableView: self.tableView)

SWIFT 3斯威夫特 3

let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)

This is error of iOS8 when using UITableViewAutomatic Dimension.这是使用 UITableViewAutomatic Dimension 时 iOS8 的错误。 We need store the content offset of table, reload table, force layout and set contenOffset back.我们需要存储表的内容偏移量,重新加载表,强制布局并将 contenOffset 设置回来。

CGPoint contentOffset = self.tableView.contentOffset;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
[self.tableView setContentOffset:contentOffset];

I am showing if only one row is being added.我正在显示是否只添加了一行。 You can extend it to multiple rows.您可以将其扩展到多行。

    // dataArray is your data Source object
    [dataArray insertObject:name atIndex:0];
    CGPoint contentOffset = self.tableView.contentOffset;
    contentOffset.y += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
    [self.tableView reloadData];
    [self.tableView setContentOffset:contentOffset];

But for this to work you need to have defined - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath the method.但是为了这个工作你需要定义- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath方法。 Or else, you can directly give your tableview row height if it is constant.否则,如果它是恒定的,你可以直接给你的 tableview 行高。

Just set estimatedRowHeight to maximum possible value.只需将estimatedRowHeight设置为最大可能值。

 self.tableView.estimatedRowHeight = 1000
 self.tableView.estimatedSectionFooterHeight = 100.0
 self.tableView.estimatedSectionHeaderHeight = 500.0

That's it!!就是这样!!

Note:笔记:

Please do not use FLT_MAX, DBL_MAX value.请不要使用FLT_MAX, DBL_MAX值。 May be it will crash your app.可能会使您的应用程序崩溃。

I'm doing it this way:我是这样做的:

messages.insertContentsOf(incomingMsgs.reverse(), at: 0)
table.reloadData()

// This is for the first load, first 20 messages, scroll to bottom
if (messages.count <= 20) {
      let indexToScroll = NSIndexPath(forRow: saferSelf.messages.count - 1, inSection: 0)
      table.scrollToRowAtIndexPath(indexToScroll, atScrollPosition: .Top , animated: false)
}
// This is to reload older messages on top of tableview
else {
      let indexToScroll = NSIndexPath(forRow: incomingMsgs.count, inSection: 0)
      table.scrollToRowAtIndexPath(indexToScroll, atScrollPosition: .Top , animated: false)
      // Remove the refreshControl.height + tableHeader.height from the offset so the content remain where it was before reload
      let theRightOffset = CGPointMake(0, table.contentOffset.y - refreshControl.frame.height - table.headeView.frame.height)
      table.setContentOffset(theRightOffset, animated: false)
}

...also, since I use dynamic cell height, to avoid some weirdness, the estimation is cached: ...此外,由于我使用动态单元格高度,为了避免一些奇怪的情况,估计被缓存:

var heightAtIndexPath = [NSIndexPath: CGFloat]()
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    heightAtIndexPath[indexPath] = cell.frame.height
}

Use Extension使用扩展

create UITableViewExtensions.swift and add following:创建UITableViewExtensions.swift并添加以下内容:

extension UITableView {

    func reloadDataWithoutScroll() {
        let offset = contentOffset
        reloadData()
        layoutIfNeeded()
        setContentOffset(offset, animated: false)
    }
}

When you want to reload you have to当你想重新加载时,你必须

self.tableView.reloadData()

self.tableView.layoutIfNeeded()

and also use this UITableViewDelegate并使用此UITableViewDelegate

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 'your maximum cell's height'
}

and your tableView will remain on the previous scroll position without scrolling并且您的 tableView 将保留在上一个滚动位置而不滚动

This code will prevent unnecessary animation and maintain the scroll view's content offset, it worked fine for me.这段代码将防止不必要的动画并保持滚动视图的内容偏移,对我来说效果很好。

let lastScrollOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadData()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastScrollOffset, animated: false)

Swift 4.2 : Simple Solution Swift 4.2:简单的解决方案

override func viewDidLoad() {
 super.viewDidLoad()

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

//And then simply update(insert, reloadSections, delete etc) your tableView or reload

tableView.reloadData()

//or

UIView.performWithoutAnimation {

  tableView.beginUpdates()
  .....
  tableView.endUpdates()
}

In iOS 12.x , using Xcode 10.2.1 , an easier option is.iOS 12.x ,使用Xcode 10.2.1 ,一个更简单的选择是。

UIView.performWithoutAnimation { 
    let loc = tableView.contentOffset
    tableView.reloadRows(at: [indexPath], with: .none)
    tableView.contentOffset = loc
}

This works better than following;这比以下更有效; it shakes at times when the row is not fully visible.当行不完全可见时,它有时会震动。

let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)

try to replace尝试更换

reloadData with reloadData

tableView.reloadRows ( at: tableView!.indexPathsForVisibleRows!, with: .none ), tableView.reloadRows ( at: tableView!.indexPathsForVisibleRows!, with: .none ),

but you should be care about no cells , if no cells , this method should cause crash.但是你应该关心no cells ,如果no cells ,这种方法应该会导致崩溃。

i wrote something that works perfect for me:我写了一些非常适合我的东西:

extension UIScrollView {
    func reloadDataAndKeepContentOffsetInPlace(reloadData:(() -> Void)) {
    let currentContentHeight = contentSize.height
    if currentContentHeight == .zero {
       reloadData()
       return
    }
    reloadData()
    layoutIfNeeded()
    let newContentHeight = self.contentSize.height
    DispatchQueue.main.async {
        var contentOffset = self.contentOffset
        contentOffset.y += newContentHeight - currentContentHeight
        self.setContentOffset(contentOffset, animated: false)
    }
  }
}

use like this:像这样使用:

   self.reloadSomeData()
   collectionView.reloadDataAndKeepContentOffsetInPlace { [weak self] in
                                guard let self = self else { return }
                                self.collectionView.reloadData()
                            }

Try the following.请尝试以下操作。

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

Source: https://developer.apple.com/forums/thread/86703来源: https : //developer.apple.com/forums/thread/86703

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM