How to scroll to the exact end of the UITableView?

前端 未结 16 2501
天涯浪人
天涯浪人 2020-12-07 18:48

I have a UITableView that is populated with cells with dynamic height. I would like the table to scroll to the bottom when the view controller is pushed from vi

相关标签:
16条回答
  • 2020-12-07 19:11

    [Swift 3, iOS 10]

    I've ended up using kind-of-hacky solution, but it doesn't depend on rows indexpaths (which leads to crashes sometimes), cells dynamic height or table reload event, so it seems pretty universal and in practice works more reliable than others I've found.

    • use KVO to track table's contentOffset

    • fire scroll event inside KVO observer

    • schedule scroll invocation using delayed Timer to filter multiple
      observer triggers

    The code inside some ViewController:

    private var scrollTimer: Timer?
    private var ObserveContext: Int = 0
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        table.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: &ObserveContext)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        table.removeObserver(self, forKeyPath: "contentSize")
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if (context == &ObserveContext) {
            self.scheduleScrollToBottom()
        }
    }
    
    func scheduleScrollToBottom() {
    
        if (self.scrollTimer == nil) {
            self.scrollTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] (timer) in
                let table = self!.table
    
                let bottomOffset = table.contentSize.height - table.bounds.size.height
                if !table.isDragging && bottomOffset > 0 {
                    let point: CGPoint = CGPoint(x: 0, y: bottomOffset)
                    table.setContentOffset(point, animated: true)
                }
    
                timer.invalidate()
                self?.scrollTimer = nil
            })
            self.scrollTimer?.fire()
        }
    }
    
    0 讨论(0)
  • 2020-12-07 19:14

    Best way to scroll scrollToBottom:

    before call scrollToBottom method call following method

    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)
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-07 19:16

    I would use more generic approach to this:

    Swift4

    extension UITableView {
    
        func scrollToBottom(){
    
            DispatchQueue.main.async {
                let indexPath = IndexPath(
                    row: self.numberOfRows(inSection:  self.numberOfSections-1) - 1, 
                    section: self.numberOfSections - 1)
                if hasRowAtIndexPath(indexPath) {
                    self.scrollToRow(at: indexPath, at: .bottom, animated: true)
                }
            }
        }
    
        func scrollToTop() {
    
            DispatchQueue.main.async {
                let indexPath = IndexPath(row: 0, section: 0)
                if hasRowAtIndexPath(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)
        }
    }
    
    0 讨论(0)
  • 2020-12-07 19:17

    For Swift 3.0

    Write a function :

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

    and call it after you reload the tableview data

    tableView.reloadData()
    scrollToBottom()
    
    0 讨论(0)
  • 2020-12-07 19:17

    For perfect scroll to bottom solution use 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)
            }
        }
    
    Performing scroll to bottom in main queue works bcoz it is delaying the execution and result in working since after loading of viewController and delaying through main queue tableView now knows its content size.

    I rather use self.view.layoutIfNeeded() after filling my data onto tableView and then call my method scrollToBottom(). This works fine for me.

    0 讨论(0)
  • 2020-12-07 19:17

    This works in Swift 3.0

    let pointsFromTop = CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude)
    tableView.setContentOffset(pointsFromTop, animated: true)
    
    0 讨论(0)
提交回复
热议问题