创建动态判断哪个UICollectionViewCell在屏幕中央的方法

Create method to dynamically determine which UICollectionViewCell is in the center of the screen

我目前正在开发一个 Objective-C iOS 应用程序,该应用程序具有带全屏集合视图的场景,其中单元格的大小几乎与整个屏幕一样大,可以垂直向下滚动场景。

我希望能够以编程方式更改此集合视图中位于可见屏幕中心的单元格,我目前实现此功能的方式是等待滚动在集合视图上停止,然后编辑位于屏幕正中央的任何单元格,如下面的方法所示:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

    NSIndexPath *centerCellIndexPath =
    [self.collectionViewFollwersFeed indexPathForItemAtPoint:
     [self.view convertPoint:[self.view center] toView:self.collectionViewFollwersFeed]];

    UICollectionViewCell *cell;
    NSString *CellIdentifier;

    CellIdentifier = @"FollowersFeed";
    cell = [_collectionViewFollwersFeed cellForItemAtIndexPath:centerCellIndexPath];

    //edit cell here

}

虽然这种实现此功能的方法在一定程度上起作用,但只有在用户停止滚动时才会开始对单元格进行更改。

我希望能够在新单元格覆盖中心位置的那一刻开始对中心单元格进行更改,而不必停止滚动。我该如何编辑此方法,以便当且仅当新单元格覆盖屏幕的中心位置时,才应调用此方法?

正在编辑单元格 == 糟糕。更改模型 == 不错。

因此向数据源添加一个 NSInteger,如 indexOverCenter。将其初始化为无效值,例如 -1。然后,在委托中,检测条件:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{

    NSIndexPath *centerCellIndexPath =
    [self.collectionViewFollwersFeed indexPathForItemAtPoint:
     [self.view convertPoint:[self.view center] toView:self.collectionViewFollwersFeed]];

    self.indexOverCenter = centerCellIndexPath.row;
    // don't edit the cell here:  yuck
    // reload it:  nice
    [self.collectionViewFollwersFeed reloadItemsAtIndexPaths:@[ centerCellIndexPath ]];
}

您的 "editing" 在 cellForItemAtIndex 中工作,将传递的 indePath 行与 indexOverCenter 进行比较。如果它们相等,则在那里进行特殊格式化。

我完全同意我受过教育的同龄人 dahn

话虽如此,您可能想要根据滚动位置实际制作动画,而不仅仅是 "change" 单元格到达中心时...

为此,您可以让单元跟踪自己的位置:

在实例化时将滚动视图传递给单元格:

// MyCollectionView's DataSource

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"yourIdentifier" forIndexPath:indexPath];
    cell.observedScrollView = theScrollViewYouWantToTrack;
    [cell startObservingPosition];
    return cell;
}

确保单元格保持 observedScrollView 弱,以避免保留周期

然后在你cell的.m文件中注册一个KVO观察器:

// MyCollectionViewCell.m

- (void)startObservingPosition {
   if (self.observedScrollView) {
      [self.observedScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:NULL];
   }
}

确保在单元解除分配之前移除 KVO 观察器...否则会崩溃!你也可以考虑使用我构建的一个很好的 enhanced KVO observer 自动释放自己

每次 KVO Observer 触发时(当 scrollView 滚动时)——您可以找出位置并做出相应的反应。

此示例展示了如何检测单元格何时位于中心,还说明了如何可以做更多的事情:

// MyCollectionViewCell.m

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"contentOffset"]) {
      if (self.observedScrollView) {  
         CGPoint offset = self.observedScrollView.contentOffset;
         CGRect contentViewRect = [self.contentView convertRect:self.contentView.bounds toView:self.observedScrollView];
         CGFloat contentViewCenterY = contentViewRect.origin.y + contentView.size.height * 0.5 - offset.y;
         if (contentViewCenterY == self.observedScrollView.bounds.height * 0.5) {
            // the contentView is in the center of the viewable feed. do your animations...
         }  
      }
    }
} 

在我的 CollectionView 中,我是这样导出它的:

private func findCenterIndex() -> Int {
        let center = self.view.convert(numberCollectionView.center, to: self.numberCollectionView)
        let row = numberCollectionView!.indexPathForItem(at: center)?.row


        guard let index = row else {

            return 0
        }


        self.rating = index

        return index

    }

因此您可以轻松地将其转换为您的 objective-c 代码