如何滚动到UITableView的确切结尾?

时间:2015-11-14 05:17:43

标签: ios swift uitableview

我有一个UITableView,其中填充了具有动态高度的单元格。当从视图控制器推送视图控制器时,我希望表格滚动到底部。

我尝试过使用contentOffsettableView scrollToRowAtIndexPath,但我仍然无法获得完全正确的解决方案。

任何人都可以帮我解决这个问题吗?

这是我要滚动的代码:

let indexPath = NSIndexPath(forRow: commentArray.count-1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)

18 个答案:

答案 0 :(得分:71)

Swift 3.0

写一个函数:

func scrollToBottom(){
    DispatchQueue.main.async {
        let indexPath = IndexPath(row: self.dataArray.count-1, section: 0)
        self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
    }
}

并在重新加载tableview数据后调用它

tableView.reloadData()
scrollToBottom()

答案 1 :(得分:19)

我会使用更通用的方法:

Swift4

extension UITableView {

    func scrollToBottom(){

        DispatchQueue.main.async {
            let indexPath = IndexPath(
                row: self.numberOfRows(inSection:  self.numberOfSections -1) - 1, 
                section: self.numberOfSections - 1)
            self.scrollToRow(at: indexPath, at: .bottom, animated: true)
        }
    }

    func scrollToTop() {

        DispatchQueue.main.async {
            let indexPath = IndexPath(row: 0, section: 0)
            self.scrollToRow(at: indexPath, at: .top, animated: false)
        }
    }
}

答案 2 :(得分:13)

我尝试了Umair的方法,但是在UITableView s中,有时可以有一个包含0行的节;在这种情况下,代码将指向无效的索引路径(空节的行0不是行)。

从行/节的数量中减去1可能是另一个痛点,因为行/节可能包含0个元素。

这是我滚动到最底部单元格的解决方案,请确保索引路径有效:

extension UITableView {
    func scrollToBottomRow() {
        DispatchQueue.main.async {
            guard self.numberOfSections > 0 else { return }

            // Make an attempt to use the bottom-most section with at least one row
            var section = max(self.numberOfSections - 1, 0)
            var row = max(self.numberOfRows(inSection: section) - 1, 0)
            var indexPath = IndexPath(row: row, section: section)

            // Ensure the index path is valid, otherwise use the section above (sections can
            // contain 0 rows which leads to an invalid index path)
            while !self.indexPathIsValid(indexPath) {
                section = max(section - 1, 0)
                row = max(self.numberOfRows(inSection: section) - 1, 0)
                indexPath = IndexPath(row: row, section: section)

                // If we're down to the last section, attempt to use the first row
                if indexPath.section == 0 {
                    indexPath = IndexPath(row: 0, section: 0)
                    break
                }
            }

            // In the case that [0, 0] is valid (perhaps no data source?), ensure we don't encounter an
            // exception here
            guard self.indexPathIsValid(indexPath) else { return }

            self.scrollToRow(at: indexPath, at: .bottom, animated: true)
        }
    }

    func indexPathIsValid(_ indexPath: IndexPath) -> Bool {
        let section = indexPath.section
        let row = indexPath.row
        return section < self.numberOfSections && row < self.numberOfRows(inSection: section)
    }
}

答案 3 :(得分:7)

当您推送具有tableview的viewcontroller时,只有在Tableview完成重新加载后才应滚动指定的indexPath。

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let indexPath = NSIndexPath(forRow: commentArray.count-1, inSection: 0)
  tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)

})

将方法放入dispatch_async的原因是,一旦执行reloadData,下一行将立即执行,然后在主线程中重新加载。因此要知道tableview何时完成(在完成所有cellforrowindex之后),我们在这里使用GCD。基本上tableview中没有委托会告诉tableview已经完成重新加载。

答案 4 :(得分:4)

要完美滚动到底部解决方案,请使用tableView contentOffset

func scrollToBottom()  {
        let point = CGPoint(x: 0, y: self.tableView.contentSize.height + self.tableView.contentInset.bottom - self.tableView.frame.height)
        if point.y >= 0{
            self.tableView.setContentOffset(point, animated: animate)
        }
    }
在主队列中执行滚动到底部的工作是bcoz,它延迟了执行并导致工作,因为加载viewController并延迟了主队列后,tableView现在知道其内容大小。

我宁愿在将数据填充到tableView之后使用self.view.layoutIfNeeded(),然后调用我的方法scrollToBottom()。这对我来说很好。

答案 5 :(得分:2)

这适用于Swift 3.0

let pointsFromTop = CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude)
tableView.setContentOffset(pointsFromTop, animated: true)

答案 6 :(得分:2)

适用于 Swift 5 或更高版本

import UIKit

extension UITableView {
    
    func scrollToBottom(animated: Bool) {
        
        DispatchQueue.main.async {
            let point = CGPoint(x: 0, y: self.contentSize.height + self.contentInset.bottom - self.frame.height)
            if point.y >= 0 {
                self.setContentOffset(point, animated: animated)
            }
        }
    }
}

答案 7 :(得分:1)

使用 Swift 5 ,您也可以在每次重新加载后执行此操作:

DispatchQueue.main.async {
    let index = IndexPath(row: self.itens.count-1, section: 0)
    self.tableView.scrollToRow(at: index, at: .bottom, animated: true)                        
}           
        

答案 8 :(得分:1)

您也可以使用此功能:-

tableView.scrollRectToVisible(CGRect(x: 0, y: tableView.contentSize.height, width: 1, height: 1), animated: true)

答案 9 :(得分:0)

Swift 5 解决方案

extension UITableView {
   func scrollToBottom(){

    DispatchQueue.main.async {
        let indexPath = IndexPath(
            row: self.numberOfRows(inSection:  self.numberOfSections-1) - 1,
            section: self.numberOfSections - 1)
        if self.hasRowAtIndexPath(indexPath: indexPath) {
            self.scrollToRow(at: indexPath, at: .bottom, animated: true)
        }
    }
}

func scrollToTop() {
    DispatchQueue.main.async { 
        let indexPath = IndexPath(row: 0, section: 0)
        if self.hasRowAtIndexPath(indexPath: indexPath) {
            self.scrollToRow(at: indexPath, at: .top, animated: false)
       }
    }
}

func hasRowAtIndexPath(indexPath: IndexPath) -> Bool {
    return indexPath.section < self.numberOfSections && indexPath.row < self.numberOfRows(inSection: indexPath.section)
}
}

答案 10 :(得分:0)

要滚动到TableView的末尾,可以使用以下功能,该功能也适用于ScrollViews。

它还会计算iPhone X和更高版本iPhone底部的安全区域。该呼叫是从主队列进行的,以正确计算高度。

app.post(
  '/upload_receipt_img',
  upload.single("image"),
  function(req, res) {
    console.log("File uploaded")
    console.log(req);
    var file = __dirname + '/images/' + req.file.filename + ".jpeg";
    fs.rename(req.file.path, file, function(err) {
      if (err) {
        console.log(err);
        res.send(500);
      } else {
        res.json({
          message: 'File uploaded successfully',
          filename: req.file.filename
        });
      }
    });
  }
)

答案 11 :(得分:0)

滚动scrollToBottom的最佳方法:

在调用scrollToBottom方法之前 调用以下方法

self.view.layoutIfNeeded()
extension UITableView {
    func scrollToBottom(animated:Bool)  {
        let numberOfRows = self.numberOfRows(inSection: self.numberOfSections - 1) - 1
        if numberOfRows >= 0{
            let indexPath = IndexPath(
                row: numberOfRows,
                section: self.numberOfSections - 1)
            self.scrollToRow(at: indexPath, at: .bottom, animated: animated)
        } else {
            let point = CGPoint(x: 0, y: self.contentSize.height + self.contentInset.bottom - self.frame.height)
            if point.y >= 0{
                self.setContentOffset(point, animated: animated)
            }
        }
    }
}

答案 12 :(得分:0)

这是一个包含补全结束符的函数:

func reloadAndScrollToTop(completion: @escaping () -> Void) {
    self.tableView.reload()
    completion()
}

并使用:

self.reloadAndScrollToTop(completion: {
     tableView.scrollToRow(at: indexPath, at: .top, animated: true))
})

在安全地加载了所有表格单元之后,将执行tableView.scrollTo ...行。

答案 13 :(得分:0)

在Swift 4+中工作:

   self.tableView.reloadData()
    let indexPath = NSIndexPath(row: self.yourDataArray.count-1, section: 0)
    self.tableView.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: true)

答案 14 :(得分:0)

@Umair答案的一些更新,以防您的tableView为空

func scrollToBottom(animated: Bool = true, delay: Double = 0.0) {
    let numberOfRows = tableView.numberOfRows(inSection: tableView.numberOfSections - 1) - 1
    guard numberOfRows > 0 else { return }

    DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [unowned self] in

        let indexPath = IndexPath(
            row: numberOfRows,
            section: self.tableView.numberOfSections - 1)
        self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: animated)
    }
}

答案 15 :(得分:0)

[Swift 3,iOS 10]

我最终使用了类似hacky的解决方案,但它不依赖于行indexpaths(有时会导致崩溃),单元格动态高度或表重新加载事件,因此它看起来非常普遍并且在实践中有效比我发现的其他人更可靠。

  • 使用KVO跟踪表格的contentOffset

  • KVO观察员内的火焰滚动事件

  • 使用延迟计时器计划滚动调用以过滤多个
    观察者触发器

一些ViewController中的代码:

query = "SELECT ..."
df = pd.DataFrame(list(session.execute(query)))

答案 16 :(得分:0)

如果您使用UINavigationBar高度UITableView UIView,请尝试从UINavigationBar的帧高减去UITableView高度。导致UITableView的{​​{1}}点与top的{​​{1}}点相同,这会影响您的UINavigationBar bottom项滚动。

  

Swift 3

UITableView

答案 17 :(得分:-1)

适用于Swift 3+:

        self.tableView.setContentOffset(CGPoint(x: 0, y: self.tableView.contentSize.height - UIScreen.main.bounds.height), animated: true)