UICollectionView 粘性标题在集合过度滚动时插入部分后消失一段时间(反弹效果)

Jak*_*rek 5 ios uicollectionview uicollectionreusableview swift uicollectionviewflowlayout

UICollectionReusableView用作UICollectionView部分的标题。我启用了“粘性标题”:

let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true
Run Code Online (Sandbox Code Playgroud)

我正在插入新的部分来收集:

collectionView.performBatchUpdates({
    self.collectionView.insertSections(IndexSet(integersIn: collectionView.numberOfSections...viewModel.numberOfSections - 1))
}, completion: nil)
Run Code Online (Sandbox Code Playgroud)

如果在集合过度滚动(启用反弹)时发生插入,标题将消失一段时间(见下面的 GIF)。我怎样才能避免这种行为?

我使用的是 iOS 12.1.4,但同样的问题也发生在 iOS 11.x 和 12.x 模拟器上。

如果关闭反弹效果,问题就不会发生,但我想保持打开状态以获得更平滑的滚动感觉。我在更新之前/之后尝试使布局无效,但没有结果。谢谢你的建议。

在此处输入图片说明

编辑 (02/26/2019)
解决方法:包装插入以performWithoutAnimation阻止标题消失但显然禁用重新加载动画。

UIView.performWithoutAnimation {
    collectionView.performBatchUpdates({
        self.collectionView.insertSections(IndexSet(integersIn: collectionView.numberOfSections...viewModel.numberOfSections - 1))
    }, completion: nil)
}
Run Code Online (Sandbox Code Playgroud)

Ham*_*ian 1

不幸的是,通过调用performBatchUpdates,布局会自动为所有项目本身设置动画。即使到现在,也没有办法明确告诉哪些项目应该设置动画,哪些不应该设置。

然而,我提出了一个反模式的解决方案。

对于您的标头类,重写这些方法:

       -(void)setBounds:(CGRect)bounds
        {
            [CATransaction begin];
            [CATransaction setDisableActions:self.shouldDisableAnimations];
            [super setBounds:bounds];
            [CATransaction commit];
        }
    
        //-(void)setCenter:(CGPoint)center
    
    
        - (void)setCenter:(CGPoint)center
        {
            [CATransaction begin];
            [CATransaction setDisableActions:self.shouldDisableAnimations];
            [super setCenter:center];
            [CATransaction commit];
        }
Run Code Online (Sandbox Code Playgroud)

现在,如果 shouldDisableAnimations 为 true,则 collectionView 的自动动画将不会应用于我们的标题。

但是,禁用标题的动画可能会导致其他故障(例如,当您向下滚动很多,然后删除所有单元格时。标题将立即跳到顶部,而 CollectionView 将滚动到顶部并带有动画,从而导致故障!)

为了处理这个问题,我们需要在正确的时间(即调用prepareForCollectionViewUpdates时)为标头设置shouldDisableAnimations。不幸的是,我们无法通过标题的属性来做到这一点,因为属性在动画完成后应用于标题。因此,我们需要直接从prepareForCollectionViewUpdates方法中直接访问标头的实例。(有多种方法可以做到这一点。我通过在其自身的类属性上保留标头的弱引用来做到这一点)

-(void)prepareForCollectionViewUpdates:(NSArray<UICollectionViewUpdateItem *> *)updateItems
{
    // Keep track of insert and delete index paths
    [super prepareForCollectionViewUpdates:updateItems];
    
    [self.indexPaths2Delete removeAllObjects];
    [self.indexPaths2Insert removeAllObjects];
    
    for (UICollectionViewUpdateItem *update in updateItems)
    {
        if (update.updateAction == UICollectionUpdateActionDelete)
        {
            [self.indexPaths2Delete addObject:update.indexPathBeforeUpdate];
        }
        else if (update.updateAction == UICollectionUpdateActionInsert)
        {
            [self.indexPaths2Insert addObject:update.indexPathAfterUpdate];
        }
    }

    if (self.indexPaths2Insert.count > 0 || self.indexPaths2Delete.count > 0)
    {
        HomeHeaderView.liveInstance.shouldDisableAnimations = false; //since it may cause scrolling, we should enable the animations
    }
    else
        HomeHeaderView.liveInstance.shouldDisableAnimations = true; //there's nothing added or deleted, so keep the header sticked.
}
Run Code Online (Sandbox Code Playgroud)