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

Posted

技术标签:

【中文标题】即使在用户可见之前,如何将 UITableView 滚动到所需的行?【英文标题】:How to scroll UITableView to desired row even before visible to user? 【发布时间】:2021-06-07 13:09:41 【问题描述】:

我想知道,是否可以将 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 唯一工作的地方。

viewDidLoad - scrollRectToVisible 在这里不起作用。 viewWillAppear - scrollRectToVisible 在这里不起作用。

这是目前的结果。

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

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

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

【问题讨论】:

【参考方案1】:

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

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),
        ])
    
    

【讨论】:

我知道必须检查firstTime 的原因。 (因为viewDidLayoutSubviews会在短时间内被多次调用)。但是,我可以知道必须检查tableView.numberOfRows(inSection: 0) != 0 的原因是什么吗? @CheokYanCheng - 这不是明确必要的...但是如果您在数据源有任何元素之前到达此控制器,它将避免崩溃。 @CheokYanCheng 由于某种原因我不明白上面的代码导致我的 uitableview 滚动到某种限制,当用户向上或向下滚动直到 uitableviewcell 项目到达屏幕的顶部或底部之后数据呈现给用户......我错过了什么?【参考方案2】:

内部viewDidLayoutSubviews

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

animated: false)

tableView.scrollRectToVisible(rect, animated: false)

【讨论】:

【参考方案3】:

我从viewDidAppear 方法调用tableView.scrollRectToVisible(rect, animated: false)animatedfalse,结果与预期一致。

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

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)
        
    

【讨论】:

【参考方案4】:
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)

【讨论】:

以上是关于即使在用户可见之前,如何将 UITableView 滚动到所需的行?的主要内容,如果未能解决你的问题,请参考以下文章

如何检测 UITableView 中有多少个单元格可见

UITableView 在点击屏幕之前不可见

如何在 VueJS DevTools 中使 Vuex “不可见”

当 UITableView 单元格中的进度条不可见时,如何更新它们

将单元格添加到可见的空 UITableView 以白色背景显示

在ios中为uitableview的所有单元格下添加不可见的detailview,并在用户点击单元格时使其可见/不可见