繁体   English   中英

iOS TableView ScrollToRow 空屏问题

[英]iOS TableView ScrollToRow Empty Screen Problem

我正在尝试使用 scrollToRow 但是当调用此方法时,tableview 有时不显示任何数据。 当我检查 UI 检查器时,我可以看到表格单元格,但在屏幕上看不到。

我试过 DispactQueue 没有解决这个问题

var forIndex = 0
    for item in filteredData {
        if let firstCharacter = item.Name?.prefix(1), firstCharacter == char {
           let indexpath = IndexPath(row: forIndex, section: 0)
           self.tableView.reloadRows(at: [indexpath], with: UITableView.RowAnimation.top)
           DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500), execute: { [weak self] in
           self?.tableView.scrollToRow(at: indexpath, at: UITableView.ScrollPosition.top, animated: true)
                    })
                    break
                }
                forIndex += 1
                }

char 是列表的元素,如 a、b、c、d... FilteredData 是 tableview 元素的列表

调试 scrollToRow 方法是否标记断点时它正在工作

可能是reloadRows (at:) animation 仍在进行中,当你踢下一个导致这种奇怪的行为时。

使用断点, reloadRows (at:)有机会完成并continuing scrollToRow之后开始。

假设您的数据源没有问题,如果这对您的情况有帮助,请尝试以下操作之一:

1.改变

tableView.reloadRows(at: [indexpath], with: .top)

tableView.reloadRows(at: [indexpath], with: .none)以便 UITableView 应用默认值 animation

或者

2.在开始下一个animation之前增加你的延迟

或者

3.在没有animation的情况下执行重新加载

UIView.performWithoutAnimation { [weak self] in
    self?.tableView.reloadRows(at: [indexpath], with: .none)
}


tableView.scrollToRow(at: indexpath,
                            at: UITableView.ScrollPosition.top,
                            animated: true)

或者

4. 使用beginUpdatesendUpdates这在对表视图执行批操作时很有用

tableView.beginUpdates()

tableView.reloadRows(at: [indexpath], with: .top)

tableView.scrollToRow(at: indexpath,
                            at: UITableView.ScrollPosition.top,
                            animated: true)

tableView.endUpdates()

这些是否会给您带来更好的结果?

更新

更多地查看reloadRows(at indexPaths:文档,这里有一些对我来说很突出的行

如果您想提醒用户单元格的值正在更改,请调用此方法。 但是,如果通知用户并不重要——也就是说,您只想更改单元格显示的值——您可以获取特定行的单元格并设置其新值。

因此,似乎在某些情况下可能不需要 animation,因为单元格可能不在屏幕上或不需要,因此更改数据的最简单方法是在索引路径处获取单元格并在没有此 function 的情况下更改数据。

由于您在循环中运行它,您很可能在前一个 indexPath 的scrollToRow完成之前启动下一个 indexPath 的reloadRows ,这可能会导致一些异常行为。

由于 UITableView 没有自己的完成处理程序,我们可以尝试使用带有递归的 CoreAnimation,这可以是选项 5。

这是我准备的小例子。 我的样本是这样的:

  1. 我有 15 行是灰色的
  2. 我决定要更改 4 行的颜色
  3. 我将 4 行的索引存储在一个队列中
  4. 我将使用tableView.reloadRows(at:
  5. 更改颜色后,我想使用tableView.scrollToRow(at:

这是我如何做到的

首先,我扩展 UITableView 以使用 CoreAnimation 块来执行reloadRowsscrollToRow

extension UITableView
{
    func reloadRows(at indexPaths: [IndexPath],
                    with animation: UITableView.RowAnimation,
                    completion: (() -> Void)?)
    {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        reloadRows(at: indexPaths, with: animation)
        CATransaction.commit()
    }
    
    func scrollToRow(at indexPath: IndexPath,
                     at scrollPosition: UITableView.ScrollPosition,
                     animated: Bool,
                     completion: (() -> Void)?)
    {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        CATransaction.setAnimationDuration(2) // set what you want
        scrollToRow(at: indexPath, at: scrollPosition, animated: animated)
        CATransaction.commit()
    }
}

以下是我如何在我的视图 controller 设置中使用此扩展

class TableViewAnimation: UITableViewController
{
    let numberOfRows = 15
    
    // Change the color of these rows in tableview
    var colorChangeArray: [Int] = []
    
    // Copy of colorChangeArray used in recursion
    var colorChangeQueue: [Int] = []
    
    // Change the color of row to this
    private let colorToChange = UIColor.systemBlue
    
    // Normal cell color
    private let colorNormal = UIColor.gray
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        setUpNavigationBar()
        setUpTableView()
    }
    
    private func setUpNavigationBar()
    {
        title = "Table View Animate"
        
        let barButton = UIBarButtonItem(title: "Animate",
                                        style: .plain,
                                        target: self,
                                        action: #selector(didTapAnimateButton))
        
        navigationItem.rightBarButtonItem = barButton
    }
    
    private func setUpTableView()
    {
        tableView.register(CustomCell.self,
                           forCellReuseIdentifier: CustomCell.identifier)
    }
    
    @objc
    func didTapAnimateButton()
    {
        // Queue all the rows that should change
        // We will dequeue these in the animation function
        // and the recursive function stops when the queue
        // is empty
        colorChangeQueue = [1, 3, 6, 12]
        
        resumeAnimation()
    }
    
    // Recursion function rather than loops using our
    // custom functions from extension
    private func resumeAnimation()
    {
        if !colorChangeQueue.isEmpty
        {
            let rowToChange = colorChangeQueue.removeFirst()
            
            print("starting \(rowToChange) animation")
            
            let indexPath = IndexPath(row: rowToChange,
                                      section: 0)
            
            colorChangeArray.append(rowToChange)
            
            tableView.reloadRows(at: [indexPath],
                                 with: .top) { [weak self] in
                
                self?.tableView.scrollToRow(at: indexPath,
                                            at: .top,
                                            animated: true,
                                            completion: {
                    
                    // recursively call the function again with a small delay
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1)
                    {
                        print("complete \(rowToChange) animation")
                        self?.resumeAnimation()
                    }
                })
            }
        }
    }
}

最后,这是数据源和委托,但这里没有发生任何独特的事情,只是为了完整性而添加它

extension TableViewAnimation
{
    override func tableView(_ tableView: UITableView,
                            numberOfRowsInSection section: Int) -> Int
    {
        return numberOfRows
    }
    
    override func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell
            = tableView.dequeueReusableCell(withIdentifier: CustomCell.identifier) as! CustomCell
        
        if colorChangeArray.contains(indexPath.row)
        {
            cell.mainView.backgroundColor = colorToChange
        }
        else
        {
            cell.mainView.backgroundColor = colorNormal
        }
        
        cell.textLabel?.textAlignment = .center
        cell.textLabel?.text = "Row \(indexPath.row)"
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView,
                   heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return 150
    }
}

现在似乎每一行的动画都正确排序了:

UITableView 动画完成滚动以重新加载行 iOS Swift

而且,如果您在控制台中看到打印,它是按顺序排列的,而循环方法不会发生:

starting 1 animation
complete 1 animation
starting 3 animation
complete 3 animation
starting 6 animation
complete 6 animation
starting 12 animation
complete 12 animation

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM