簡體   English   中英

UITableView 刷新而不滾動

[英]UITableView Refresh without scrolling

我有一個帶有項目的_TableView ,我想設置自動刷新,並且我不希望它在刷新時滾動,假設用戶向下滾動了 2 頁,並且觸發了刷新 -> 所以我想將刷新的內容放到不中斷用戶滾動的表格頂部

假設用戶在第 18 行,現在_dataSource被刷新,所以它提取了 4 個項目,所以我希望用戶留在他所在的項目上。

實現它的最佳方法是什么?

對於Swift 3+

您需要保存當前的偏移量UITableView ,然后重新再設置偏移回到了UITableView

我為此目的創建了這個函數:

func reload(tableView: UITableView) {

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

}

簡單地調用它: reload(tableView: self.tableView)

斯威夫特 3

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

這是使用 UITableViewAutomatic Dimension 時 iOS8 的錯誤。 我們需要存儲表的內容偏移量,重新加載表,強制布局並將 contenOffset 設置回來。

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

我正在顯示是否只添加了一行。 您可以將其擴展到多行。

    // 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];

但是為了這個工作你需要定義- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath方法。 否則,如果它是恆定的,你可以直接給你的 tableview 行高。

只需將estimatedRowHeight設置為最大可能值。

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

就是這樣!!

筆記:

請不要使用FLT_MAX, DBL_MAX值。 可能會使您的應用程序崩潰。

我是這樣做的:

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)
}

...此外,由於我使用動態單元格高度,為了避免一些奇怪的情況,估計被緩存:

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
}

使用擴展

創建UITableViewExtensions.swift並添加以下內容:

extension UITableView {

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

當你想重新加載時,你必須

self.tableView.reloadData()

self.tableView.layoutIfNeeded()

並使用此UITableViewDelegate

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

並且您的 tableView 將保留在上一個滾動位置而不滾動

這段代碼將防止不必要的動畫並保持滾動視圖的內容偏移,對我來說效果很好。

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

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()
}

iOS 12.x ,使用Xcode 10.2.1 ,一個更簡單的選擇是。

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

這比以下更有效; 當行不完全可見時,它有時會震動。

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

嘗試更換

reloadData

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

但是你應該關心no cells ,如果no cells ,這種方法應該會導致崩潰。

我寫了一些非常適合我的東西:

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)
    }
  }
}

像這樣使用:

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

請嘗試以下操作。

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

來源: https : //developer.apple.com/forums/thread/86703

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM