在后台线程上访问 UIView 的属性

Access a UIView's properties on a background thread

我试图让我的 CollectionView 在视图出现后滚动它的第一个单元格,然后在按下按钮时再次滚动。问题是 collectionView 没有在任何视图生命周期函数中生成它的所有单元格。

我的解决方案是在后台线程上开始一个 while 循环,检查是否 collectionView.visibleCells.count > 0,如果是,return 到主线程并滚动到第一个单元格。但是,我收到一个错误,告诉我我不应该从主线程访问 visibleCells,并且当我访问时应用程序会卡顿。

如何在主线程实现这个功能,或者在后台线程查看单元格数量?

private func scrollToFirst() {
    DispatchQueue.global(qos: .background).async { [weak self] in
        if (self != nil) {
            while(self!.collectionView.visibleCells.count != 0) {
                DispatchQueue.main.async { [weak self] in
                    self!.collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: true)
                }
            }
        }
    }
}

是的,不要那样做。 UIKit 不是线程安全的,因此当您尝试从后台线程查看视图对象时,视图对象的数据结构可能会发生变化。

似乎应该有比等待细胞出现更好的方法来处理这个问题。

如果您找不到更简洁的方法,可以使用在主线程上运行的 Timer 对象。该代码可能如下所示:

private func scrollToFirst(afterDelay delay: Double = 0.2) {
    _ = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { 
        timer, [weak self] in
        guard strongSelf = self else {
          return
        }
        if strongSelf.collectionView.visibleCells.count != 0 {
          self!.collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: true)
        }
}

该代码将触发一次,如果计时器触发一次,则滚动到集合视图的开头。

有一个委托方法 willDisplay 在显示 collectionViewCell 之前被调用。如果您之前没有单元格并且调用了它,那么您就知道您将要从零个单元格变为多个零个单元格。