页面加载时滚动后如何访问 UICollectionView 中的特定单元格?

Posted

技术标签:

【中文标题】页面加载时滚动后如何访问 UICollectionView 中的特定单元格?【英文标题】:How to access specific cell in UICollectionView after scrolling when page loads? 【发布时间】:2018-09-06 20:27:15 【问题描述】:

我正在尝试像这样构建一个时间线:

时间轴中的每个单元格代表一个月,因此 May 是水平 collectionView 中的最后一个单元格。我想要的是当 collectionView 加载时,如果它不可见,它会自动滚动到可能,然后调用单元格上的特定函数来绘制箭头。我用来尝试执行此操作的代码如下。

    override func viewDidLoad() 
    super.viewDidLoad()
    self.timeline.register(UINib(nibName: "TimelineMonthViewCell", bundle: nil), forCellWithReuseIdentifier: "TimelineMonthViewCell")
    self.collectionView.register(UINib(nibName: "PostCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "PostCollectionViewCell")
    pageControl.hidesForSinglePage = true
    self.navigationItem.largeTitleDisplayMode = .never

    UserService.posts(for: User.current,tagString: self.selectedTagTitles.first!)  (postStuff) in

        self.posts = postStuff.0
        self.postIds = postStuff.1
        self.timeline.dataSource = self
        self.collectionView.dataSource = self
        self.timeline.delegate = self
        self.collectionView.delegate = self
        self.firstPostDate = self.posts.first?.timeStamp
        self.currentPostDate = self.posts.last?.timeStamp
    


override open func viewDidAppear(_ animated: Bool) 

    super.viewDidAppear(animated)

    let indexToScrollTo = IndexPath(row: self.posts.count - 1, section: 0)
    self.collectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
    let firstPostDate = self.posts.first?.timeStamp
    let diff = self.posts.last?.timeStamp.months(from: (firstPostDate?.startOfMonth())!)
    //self.currentPostMonth = diff
    let monthCellIndexPath = IndexPath(row: diff!, section: 0)
    self.timeline.scrollToItem(at: monthCellIndexPath, at: .centeredHorizontally, animated: false)
    let months = self.posts.last?.timeStamp.months(from: (self.posts.first?.timeStamp.startOfMonth())!)
    print("cells are",self.timeline.indexPathsForVisibleItems)
    print(months,"cell months")
    if let optionalCell = self.timeline.cellForItem(at: IndexPath(row: months!, section: 0)) 
        let cell = optionalCell as! TimelineMonthViewCell
        let day = Calendar.current.component(.day, from: self.currentPostDate!)
        cell.drawArrow(day: day)
     else 
        print("cell out of range")
    

这几乎可以工作,除了两个问题。

首先,初始单元格会在自动滚动到最后一个单元格之前闪烁。我认为这是因为我使用的是 viewDidAppear 而不是 viewWillAppear。但是,当我使用 viewWillLoad 时,应用程序崩溃并出现错误:Thread 1: EXC_BAD_ACCESS (code=1, address=0xfffffffffffffff8)

其次,打印出来的self.timeline.indexPathsForVisibleItems为:[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]]。因此,它查看的是前 5 个而不是它刚刚滚动到的最后 5 个。然后它找不到单元格:“单元格超出范围”,因为索引约为 28(最后一个单元格)。

那么我怎样才能让它自动滚动到最后一个单元格并正确找到它呢?

【问题讨论】:

【参考方案1】:

不要将这些行设置在当前位置

self.timeline.dataSource = self
self.collectionView.dataSource = self
self.timeline.delegate = self
self.collectionView.delegate = self

viewDidLoad之上可能会更好,以利用加载vc时的第一次自动重新加载,也在他们的位置插入

self.timeline.reloadData()
self.collectionView.reloadData()

你可以在里面插入滚动代码

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)    

如果您当前的 for 循环不是异步的,则在 ViewDidAppear

【讨论】:

你能显示你正在考虑的代码吗?我无法将 dataSource 和委托移动到 viewDidLoad 的顶部,因为我的帖子对象尚未创建。如果您想查看更新的代码,我还删除了异步调度以简化问题...【参考方案2】:

我建议将滚动代码移至viewWillAppear。这样,它在视图显示之前完成,但在所有内容都准备好查看之后完成。

override open func viewWillAppear(_ animated: Bool) 

    super.viewWillAppear(animated)

    let indexToScrollTo = IndexPath(row: self.posts.count - 1, section: 0)
    self.collectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
    let firstPost = self.posts.first?.timeStamp
    let firstOfFirstMonth = firstPost?.startOfMonth()
    let diff = self.posts.last?.timeStamp.months(from: firstOfFirstMonth!)
    //self.currentPostMonth = diff
    let monthCellIndexPath = IndexPath(row: diff!, section: 0)
    self.timeline.scrollToItem(at: monthCellIndexPath, at: .centeredHorizontally, animated: false)

【讨论】:

这使我的应用程序崩溃,并出现错误代码:线程 1:EXC_BAD_ACCESS(代码=1,地址=0xfffffffffffffff8)在此行:self.collectionView.scrollToItem(at:indexToScrollTo,at:.left,动画:假) 嗯。尝试移动它做 viewDidAppear。运气好的话,你不会看到它更新。 确实有效!但是我仍然遇到相同的问题,即找不到单元格,因此无法绘制箭头 如果您查看更新后的问题,您会看到我采纳了您的建议,但仍然存在在 viewDidAppear 中找不到单元格索引的问题。实际上,indexesForVisibleCells 似乎打印了原始索引(滚动发生之前的索引) 您需要处理cellForItemAt 中的绘图,这确实是第二个问题。将其作为一个不同的问题发布,我会尽力回答。

以上是关于页面加载时滚动后如何访问 UICollectionView 中的特定单元格?的主要内容,如果未能解决你的问题,请参考以下文章

加载数据后如何防止滚动视图滚动到网络视图?

仅在滚动(分页)后显示的数据是不是是加载页面时 dom 的一部分?

页面加载后如何激活倒计时?

Swift - 如果项目滚动一点,UICollection 快速恢复

滚动到表格底部时如何添加“加载更多”页面

滚动时如何下载图像?