简体   繁体   English

iOS TableView ScrollToRow 空屏问题

[英]iOS TableView ScrollToRow Empty Screen Problem

i am trying to use scrollToRow but when call this method tableview does not show any data sometimes.我正在尝试使用 scrollToRow 但是当调用此方法时,tableview 有时不显示任何数据。 When i check UI inspector i can see table cells but does not seen on screen.当我检查 UI 检查器时,我可以看到表格单元格,但在屏幕上看不到。

I tried to DispactQueue didn't solve this problem我试过 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 is element of list like a,b,c,d... FilteredData is list of tableview elements char 是列表的元素,如 a、b、c、d... FilteredData 是 tableview 元素的列表

When debugging if scrollToRow method marked breakpoint it's working调试 scrollToRow 方法是否标记断点时它正在工作

Probably the reloadRows (at:) animation is still in progress when you kick of the next causing this strange behavior.可能是reloadRows (at:) animation 仍在进行中,当你踢下一个导致这种奇怪的行为时。

With the breakpoint, the reloadRows (at:) has a chance to finish and on continuing the scrollToRow kicks off after.使用断点, reloadRows (at:)有机会完成并continuing scrollToRow之后开始。

Try one of the following if this helps your situation assuming there is no issue with your data source:假设您的数据源没有问题,如果这对您的情况有帮助,请尝试以下操作之一:

1. Change 1.改变

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

to

tableView.reloadRows(at: [indexpath], with: .none) so that the UITableView applies a default animation tableView.reloadRows(at: [indexpath], with: .none)以便 UITableView 应用默认值 animation

OR或者

2. Increase your delay before kicking off the next animation 2.在开始下一个animation之前增加你的延迟

OR或者

3. Perform the reload without 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)

OR或者

4. Use beginUpdates and endUpdates which can be useful when performing batch operations on table views 4. 使用beginUpdatesendUpdates这在对表视图执行批操作时很有用

tableView.beginUpdates()

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

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

tableView.endUpdates()

Do any of these give you better results?这些是否会给您带来更好的结果?

Update更新

Looking more at the docs for reloadRows(at indexPaths: , here are some lines that stood out for me更多地查看reloadRows(at indexPaths:文档,这里有一些对我来说很突出的行

Call this method if you want to alert the user that the value of a cell is changing.如果您想提醒用户单元格的值正在更改,请调用此方法。 If, however, notifying the user is not important—that is, you just want to change the value that a cell is displaying—you can get the cell for a particular row and set its new value.但是,如果通知用户并不重要——也就是说,您只想更改单元格显示的值——您可以获取特定行的单元格并设置其新值。

So it seems that in some situations animation might not be needed as the cell could be off screen or not needed, so the simplest way to change data is get the cell at the index path and change the data without this function.因此,似乎在某些情况下可能不需要 animation,因为单元格可能不在屏幕上或不需要,因此更改数据的最简单方法是在索引路径处获取单元格并在没有此 function 的情况下更改数据。

Since you are running this in a loop, you most likely start the next indexPath's reloadRows before the previous indexPath's scrollToRow is complete and this can cause some unusual behavior.由于您在循环中运行它,您很可能在前一个 indexPath 的scrollToRow完成之前启动下一个 indexPath 的reloadRows ,这可能会导致一些异常行为。

Since UITableView does not have it's own completion handler, we can try using CoreAnimation with recursion which can be option 5.由于 UITableView 没有自己的完成处理程序,我们可以尝试使用带有递归的 CoreAnimation,这可以是选项 5。

Here is small example I prepared.这是我准备的小例子。 My sample is like this:我的样本是这样的:

  1. I have 15 rows that are grey in color我有 15 行是灰色的
  2. I decide that I want to change the color of 4 rows我决定要更改 4 行的颜色
  3. I store the index of 4 rows in a queue我将 4 行的索引存储在一个队列中
  4. I will change the color using tableView.reloadRows(at:我将使用tableView.reloadRows(at:
  5. After changing the color, I want to scroll to that row using tableView.scrollToRow(at:更改颜色后,我想使用tableView.scrollToRow(at:

Here is how I accomplish that这是我如何做到的

First I extend the UITableView to use CoreAnimation block to do reloadRows and scrollToRow首先,我扩展 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()
    }
}

Here is how I use this extension with my view controller set up以下是我如何在我的视图 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()
                    }
                })
            }
        }
    }
}

Finally, here is the data source and delegate but nothing unique is happening here, just adding it for completeness最后,这是数据源和委托,但这里没有发生任何独特的事情,只是为了完整性而添加它

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
    }
}

Now it seems like the animations of each of the row is sequenced properly:现在似乎每一行的动画都正确排序了:

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

And also if you see the print in the console, it is sequenced which did not happen with the loop method:而且,如果您在控制台中看到打印,它是按顺序排列的,而循环方法不会发生:

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