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: &lt;#T##UITableViewCell#&gt;) 而不是使用indexPathForRow(at: &lt;#T##CGPoint#&gt;) 函数来获取单元格的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() 总是落后一步

Swift tableView.reloadRows 删除单元格