自动无限collectionView - 闪烁

Posted

技术标签:

【中文标题】自动无限collectionView - 闪烁【英文标题】:Automatic infinite collectionView - blinking 【发布时间】:2013-11-25 10:33:33 【问题描述】:

我已经检查了UICollectionView 中无限滚动问题的几个解决方案,但没有一个能完美运行(1、2、3 甚至教程 - 4)。 主要问题在于UICollectionView 包含图像这一事实。当我滚动到第一个或最后一个项目(使其无限)时,在它们加载之前我可以看到一个非常短的“闪烁”,必须删除。我不知道如何解决它,欢迎任何帮助。

我应该提到,我正在制作的画廊不应允许任何用户交互,它应该自行滚动(可以说是演示文稿)。

我尝试过的两种最有趣的方法:

方法1:collectionView中的14个item,顺序为:@[8, 9, @[0-9], 0, 1](所以8,9,1,2是重复的)

// a timer to move everything
_timer = [NSTimer scheduledTimerWithTimeInterval:0.02f target:self selector:@selector(scroll) userInfo:nil repeats:YES];
// timer selector
- (void)scroll 
    self.collectionView.contentOffset = CGPointMake(self.collectionView.contentOffset.x+1, self.collectionView.contentOffset.y);


// check if we need to change offset
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"something" forIndexPath:indexPath];
    if ([self scrollProperly]) 
        //        return cell;
    


// method to check & set proper offset (author: D33pN16h7)
- (BOOL)scrollProperly 
    // check if near the end or beginning
    BOOL test = NO;
    CGFloat currentOffsetX = self.collectionView.contentOffset.x;
    CGFloat currentOffSetY = self.collectionView.contentOffset.y;
    CGFloat contentWidth = self.collectionView.contentSize.width;

    if (currentOffsetX < (contentWidth / 6.0f)) 
        self.collectionView.contentOffset = CGPointMake((currentOffsetX + (contentWidth/2)), currentOffSetY);
        test = YES;
    
    if (currentOffsetX > ((contentWidth * 4)/ 6.0f)) 
        self.collectionView.contentOffset = CGPointMake((currentOffsetX - (contentWidth/2)), currentOffSetY);
        test = YES;
    
    return test;

第二种方法:collectionView 中的 20 个项目,顺序为:@[@[0-9]、@[0-9]](因此整个数组是重复的)。还有定时器,和以前的方法一样。我们在这里使用:

,而不是在 collectionView:cellForItemAtIndexPath: 中运行 [self scrollProperly]
if (indexPath.row == 2) 
    [collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:([collectionView numberOfItemsInSection:indexPath.section] - 3) inSection:indexPath.section] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
 else if (indexPath.row == ([collectionView numberOfItemsInSection:indexPath.section] - 1)) 
    [collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:indexPath.section] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];

【问题讨论】:

我也看过 Apple 的“高级 ScrollView 技术”,但这无济于事,因为它改变了框架,并且在 UICollectionView 中不能乱用单元格框架。 【参考方案1】:

好的,我的最终解决方案是让滚动条真的很长 - 在collectionView:cellForItemAtIndexPath: 我正在从我的数据库中读取它的大小模数。在collectionView:numberOfItemsInSection: 中有一个恒定的大小。当我达到恒定大小时,我滚动到第一行。因为我已将大小设为恒定,所以大量闪烁不会对用户造成刺激,并且滚动实际上是无限的。解决方案并不完美,但我不知道如何解决它。

【讨论】:

【参考方案2】:

我知道这有点晚了,但我想出了如何实现一个无限滚动的 UICollectionView;

根据您的单元格的布局方式,这部分可能会有所不同,但基本上分为以下步骤。

1) 修改您的数据源并在数据阵列的正面和背面复制所需数量的单元格。

例如,如果您的数据源数组如下所示:

NSArray* array = @[@1, @2, @3, @4, @5];

假设它们各占视图高度的 1/5,您可能希望将数组修改为如下所示:

NSArray* array = @[@4, @5, @1, @2, @3, @4, @5, @1, @2, @3];

这样,您的 collectionview 本质上是在“重复”这些单元格,以给人一种它继续运行的错觉。

这部分是最难的,因为修改数据数组的方式取决于屏幕上一次可以容纳多少个单元格。如果一次只能在屏幕上显示一个单元格并且您的数据数组是@[@1, @2, @3],则可以轻松将其修改为@[@3, @1, @2, @3, @1];(这是this awesome tutorial 中的示例。

那么我们为什么要这样做呢?这是因为collectionview 需要滚动到某些东西,即collection view 的正面和背面有额外的填充。

2) 使用KeyValue Observing来观察collectionView的contentOffset。

[self.collectionView addObserver:self forKeyPath:@"contentOffset" options:0 context:NULL];

3) 检查 contentOffset 是否达到边缘。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    CGFloat offset = self.collectionView.contentOffset.y;
    if(offset <= 1)
        [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.height-2)];
        [self.collectionView reloadData];
else if(offset >= self.collectionView.height-1)
        [self.collectionView setContentOffset:CGPointMake(0, 2)];
        [self.collectionView reloadData]; //call to reload the now visible cells, ensure delegate gets called
    

如果 contentOffset 在顶部边缘的几个像素内,则设置 contentOffset 以便集合视图在用户不知情的情况下静默地向下滚动页面。当 contentOffset 到达另一边时执行相同的操作。

两件非常重要的事情。

1) 首次初始化 collectionView 时,将 contentOffset 设置为 (0, 3)。这样,它不会触发 KVO 方法中的 if 语句。 2)在KVO方法中设置contentOffset时,还要确保你设置的点在方法被调用时不会触发if语句。

请记住,由于您正在观察 contentOffset,因此设置 contentOffset 将立即触发该方法!只要它没有第二次设置 contentOffset 从而导致更改通知的无限循环,你应该没问题。

别忘了将collectionView.bounces 设置为YES

【讨论】:

我认为更改内容偏移量也会导致此闪烁。出列单元将触发重新加载,因此图像将闪烁很短的时间。你的回答仍然很有趣,因为我没有尝试这种方法:) 我会推荐,只要你的表很短,加载每个索引。这种方法肯定存在一些加载问题,甚至一些我还没有弄清楚。

以上是关于自动无限collectionView - 闪烁的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Collection View 中给 UILabel 一个闪烁效果

如何使用自动布局动态调整具有固定宽度的collectionview单元格高度?

在无限滚动中延迟加载时锁定滚动位置(向上滚动)

collectionView iOS 13.2 中的 UILongPressGestureRecognizer 位置功能问题

iOS UICollectionView无限轮播

iOS UICollectionView无限轮播