[英]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. 使用
beginUpdates
和endUpdates
这在对表视图执行批操作时很有用
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:
我的样本是这样的:
tableView.reloadRows(at:
tableView.reloadRows(at:
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 块来执行
reloadRows
和scrollToRow
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:现在似乎每一行的动画都正确排序了:
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.