如何在用户可见之前将 UITableView 滚动到所需的行?

How to scroll UITableView to desired row even before visible to user?

我想知道,是否可以将 UITableView 滚动到所需的行,甚至在它对用户可见之前,这样用户就不知道这种滚动操作?

目前,这是我执行滚动到所需行的代码。

class ThemeTableViewController: UITableViewController {
    private var firstTime = true
 
    private func scrollRectToVisible() {
        let theme = WeNoteOptions.theme
        if let index = Theme.allCases.firstIndex(of: theme) {
            let indexPath = IndexPath(row: index, section: 0)
            let rect = tableView.rectForRow(at: indexPath)
            tableView.scrollRectToVisible(rect, animated: true)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if firstTime {
            firstTime.toggle()
            scrollRectToVisible()
        }
    }

请注意,在以下函数中放置scrollRectToVisible将不起作用。到目前为止,viewDidAppearscrollRectToVisible 唯一可以工作的地方。

这是目前的结果。

用户会注意到自动滚动操作。

我们希望用户不知道滚动动作。

是否有可能当用户第一次看到 UITableView 时,最后一行(绿色鳄梨)已经可见?

里面viewDidLayoutSubviews

if tableView.numberOfRows(inSection: 0) != 0 {
  scrollRectToVisible()   
}

animated: false)

tableView.scrollRectToVisible(rect, animated: false)

我用 animated 作为 falseviewDidAppear 方法调用了 tableView.scrollRectToVisible(rect, animated: false),结果符合预期。

我已经根据您上面的代码进行了复制和更改。在这里,我在您的方法中添加了 animated:bool 作为参数 scrollRectToVisible 以根据需要控制动画部分。

class ThemeTableViewController: UITableViewController {
    private var firstTime = true

    // added new parameter `animated`
    private func scrollRectToVisible(_ animated: Bool = true) {
        let theme = WeNoteOptions.theme
        if let index = Theme.allCases.firstIndex(of: theme) {
            let indexPath = IndexPath(row: index, section: 0)
            let rect = tableView.rectForRow(at: indexPath)

            // replaced true with `animated`
            tableView.scrollRectToVisible(rect, animated: animated)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if firstTime {
            firstTime.toggle()
            // animation = false.
            scrollRectToVisible(false)
        }
    }
}

如果您的单元格可能具有动态高度,您可能会发现这更可靠:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if firstTime {
        if tableView.numberOfRows(inSection: 0) != 0 {
            firstTime = false
            tableView.scrollToRow(at: IndexPath(row: tableView.numberOfRows(inSection: 0) - 1, section: 0), at: .bottom, animated: false)
        }
    }
}

使用以下示例对其进行测试...标签中的单元格将包含 2 到 6 行文本。注释/取消注释 viewDidLayoutSubviews():

中的不同方法
class MyTableVC: UITableViewController {
    
    private var firstTime = true

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(MyCell.self, forCellReuseIdentifier: "myCell")
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        if firstTime {
            if tableView.numberOfRows(inSection: 0) != 0 {
                firstTime = false
                
                // note the difference between this
                tableView.scrollToRow(at: IndexPath(row: tableView.numberOfRows(inSection: 0) - 1, section: 0), at: .bottom, animated: false)
                
                // and this
                //scrollRectToVisible()
            }
        }
    }
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 30
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCell
        let n = indexPath.row % 5
        var s = "Row \(indexPath.row)"
        for i in 0...n {
            s += "\n\(i)"
        }
        c.theLabel.text = s
        return c
    }

    private func scrollRectToVisible() {
        let index = tableView.numberOfRows(inSection: 0) - 1
        let indexPath = IndexPath(row: index, section: 0)
        let rect = tableView.rectForRow(at: indexPath)
        tableView.scrollRectToVisible(rect, animated: false)
    }

}

class MyCell: UITableViewCell {
    
    var theLabel = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        theLabel.translatesAutoresizingMaskIntoConstraints = false
        theLabel.numberOfLines = 0
        theLabel.backgroundColor = .yellow
        contentView.addSubview(theLabel)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: g.topAnchor),
            theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
        ])
    }
    
}
Here consider itemCount = last item of your array

Add code in viewDidload method after your array contains data.

tableView.reloadData()
let indexPath = IndexPath(item: itemCount, section: 0)
print("indexPath",indexPath)
tableView.scrollToRow(at: indexPath, at: .none, animated: false)