UITableView:reloadRows(at:) 需要两次点击才能更新表并每次滚动
Posted
技术标签:
【中文标题】UITableView:reloadRows(at:) 需要两次点击才能更新表并每次滚动【英文标题】:UITableView: reloadRows(at:) takes two hits for table to be updated and scroll every time 【发布时间】:2018-02-06 05:41:07 【问题描述】:我有一个从 CoreData 数据库更新的表视图(控制器:MetricsViewController)。我使用了根据我的需要定制的原型单元 (MetricsViewCell)。它包含一个分段控件、一个UIView(metricsChart,用于显示图表-animatedCircle),以及一些UILabel。
MetricsViewCell:
class MetricsViewCell: UITableViewCell
var delegate: SelectSegmentedControl?
var animatedCircle: AnimatedCircle?
@IBOutlet weak var percentageCorrect: UILabel!
@IBOutlet weak var totalPlay: UILabel!
@IBOutlet weak var metricsChart: UIView!
didSet
animatedCircle = AnimatedCircle(frame: metricsChart.bounds)
@IBOutlet weak var recommendationLabel: UILabel!
@IBOutlet weak var objectType: UISegmentedControl!
@IBAction func displayObjectType(_ sender: UISegmentedControl)
delegate?.tapped(cell: self)
protocol SelectSegmentedControl
func tapped(cell: MetricsViewCell)
MetricsViewController:
class MetricsViewController: FetchedResultsTableViewController, SelectSegmentedControl
func tapped(cell: MetricsViewCell)
if let indexPath = tableView.indexPath(for: cell)
tableView.reloadRows(at: [indexPath], with: .none)
var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer didSet updateUI()
private var fetchedResultsController: NSFetchedResultsController<Object>?
private func updateUI()
if let context = container?.viewContext
let request: NSFetchRequest<Object> = Object.fetchRequest()
request.sortDescriptors = []
fetchedResultsController = NSFetchedResultsController<Object>(
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: "game.gameIndex",
cacheName: nil)
try? fetchedResultsController?.performFetch()
tableView.reloadData()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "Object Cell", for: indexPath)
if let object = fetchedResultsController?.object(at: indexPath)
if let objectCell = cell as? MetricsViewCell
objectCell.delegate = self
let request: NSFetchRequest<Object> = Object.fetchRequest()
...
...
return cell
当用户选择某个部分的分段控件中的一个分段时,MetricsViewController 应重新加载该特定行中的数据。 (有两个部分,每行一排)。因此,我在 MetricsViewCell 中定义了一个协议来通知我的控制器用户操作。
正在使用FetchedResultsTableViewController
更新数据 - 它基本上充当 CoreData 和 TableView 之间的委托。一切都很好,这意味着我将正确的数据输入到我的 TableView 中。
有两个问题:
我必须点击分段控件的段两次才能重新加载分段控件被点击的行中的数据。
每次从分段控件中选择一个段时,表格都会向上滚动,然后向下滚动。
非常感谢您的帮助。我在开发过程中遇到的很多问题都依赖于这个社区,我已经很感激了:)
例如,在动物识别部分,我必须点击“中级”两次才能重新加载它的行(如果你仔细观察,我第一次点击中级时,它会被选中几分之一秒,然后它回到“基本”或首先选择的任何部分。第二次当我达到中级时,它进入中级)。另外,表格会上下滚动,这是我不想要的。
编辑:添加了更多关于我使用 CoreData 和持久容器的上下文。
【问题讨论】:
2.为了避免滚动 - 尝试重新加载没有动画的行。目前您正在指定自动动画。 我。您如何确定点击了哪个段控制?每个单元格都有自己的段控制 不起作用 - 不幸的是使用 .none 动画。这是默认动画。 您是否在代码中的任何位置滚动? 在你的函数tapped(cell:)
然后你创建indexPath
的方式似乎是错误的。这里有几个错误。您将行硬编码为零。
【参考方案1】:
您可以直接使用indexPath(for: <#T##UITableViewCell#>)
而不是使用indexPathForRow(at: <#T##CGPoint#>)
函数来获取单元格的indexPath 对象,因为您将单元格对象接收到func tapped(cell: MetricsViewCell)
并尝试始终在主线程中更新UI 上的数据,如下所示.
func tapped(cell: MetricsViewCell)
if let lIndexPath = table.indexPath(for: <#T##UITableViewCell#>)
DispatchQueue.main.async(execute:
table.reloadRows(at: lIndexPath, with: .none)
)
【讨论】:
我正在使用我的应用代理容器的 viewContext 来更新数据。 viewContext 在主线程上运行,因此这不是问题。我仍然尝试了显式主线程,但不起作用:( @Manganese 如何获取单元格的 indexpath 对象?试过了吗? 是的,这与@user1046037 的建议相同:func tapped(cell: MetricsViewCell) if let indexPath = tableView.indexPath(for: cell) DispatchQueue.main.async self.tableView .reloadRows(at: [indexPath], with: .none) 【参考方案2】:您的UISegmentedControl
正在重用[UITableView 的默认行为]。
为避免这种情况,请保留 dictionary
以获取和存储值。
另一件事,在UIViewController
本身中尝试outlet connection as Action
UISegmentedControl
,而不是您的UITableViewCell
当您点击 UISegmentedControl
时,以下代码不会重新加载您的表格视图。你可以避免,代表也打电话。
以下代码是UISegmentedControl
的基本演示。根据您的需要进行定制。
var segmentDict = [Int : Int]()
override func viewDidLoad()
super.viewDidLoad()
for i in 0...29 // number of rows count
segmentDict[i] = 0 //DEFAULT SELECTED SEGMENTS
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SOTableViewCell
cell.mySegment.selectedSegmentIndex = segmentDict[indexPath.row]!
cell.selectionStyle = .none
return cell
@IBAction func mySegmentAcn(_ sender: UISegmentedControl)
let cellPosition = sender.convert(CGPoint.zero, to: tblVw)
let indPath = tblVw.indexPathForRow(at: cellPosition)
segmentDict[(indPath?.row)!] = sender.selectedSegmentIndex
print("Sender.tag ", indPath)
【讨论】:
你也可以放弃投票吗?对 SO 追随者有用吗?以上是关于UITableView:reloadRows(at:) 需要两次点击才能更新表并每次滚动的主要内容,如果未能解决你的问题,请参考以下文章
tableView.reloadRows(at:[indexPath],带有:.none)会像这样崩溃吗?
UITableView - reloadRows 方法创建奇怪的滚动错误
什么 tableView.reloadRows(at: [indexPath], with: .none) 会像这样崩溃?
iphone reloadRows At IndexPath 奇怪的动画
自定义 UITableViewCell 中的操作按钮调用的 UITableView reloadRows() 总是落后一步