创建方法来动态确定哪个 UICollectionViewCell 在屏幕的中心

Posted

技术标签:

【中文标题】创建方法来动态确定哪个 UICollectionViewCell 在屏幕的中心【英文标题】:Create method to dynamically determine which UICollectionViewCell is in the center of the screen 【发布时间】:2017-01-06 00:08:38 【问题描述】:

我目前正在开发一个 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


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

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

【问题讨论】:

scrollViewDidScroll怎么样? 【参考方案1】:

我完全同意我受过良好教育的同行dahn 的看法。

话虽如此,您可能希望根据滚动位置实际设置动画,而不仅仅是在单元格到达中心时“更改”单元格...

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

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

// 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 较弱,以避免保留循环

然后在你的单元格的 .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...
           
      
    
 

【讨论】:

【参考方案2】:

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

所以向数据源添加一个 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 ]];

在 cellForItemAtIndex 中进行“编辑”工作,将传递的 indePath 行与 indexOverCenter 进行比较。如果它们相等,则在此处进行特殊格式化。

【讨论】:

【参考方案3】:

在我的 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 代码

【讨论】:

以上是关于创建方法来动态确定哪个 UICollectionViewCell 在屏幕的中心的主要内容,如果未能解决你的问题,请参考以下文章

基于spring多数据源动态调用及其事务处理

委托和事件

原创设计模式之8:责任链模式

JVM学习笔记--方法调用之静态分配和动态分配

vba - 使用具有“动态创建名称”的变量

Swift的动态性?