Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

628
Views
UICollectionView sticky header disappears for while after inserting section when collection is overscrolled (bounce effect)

I'm using UICollectionReusableView as a header of UICollectionView section. I enabled "sticky headers" with:

let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true

I'm inserting new sections to collection with:

collectionView.performBatchUpdates({
    self.collectionView.insertSections(IndexSet(integersIn: collectionView.numberOfSections...viewModel.numberOfSections - 1))
}, completion: nil)

If the insertion happens when collection is overscrolled (bounce is enabled), header will disappear for while (see GIF below). How can I avoid this behaviour?

I am using iOS 12.1.4 but same issue happens also on iOS 11.x and 12.x simulators.

The problem doesn't happen if bounce effect is turned off but I want to keep it on for smoother scroll feeling. I tried invalidate layout before/after update with no result. Thanks for advices.

enter image description here

EDIT (02/26/2019)
Workaround: Wrapping the insertion to performWithoutAnimation block solve header disappearing but obviously disable reload animation.

UIView.performWithoutAnimation {
    collectionView.performBatchUpdates({
        self.collectionView.insertSections(IndexSet(integersIn: collectionView.numberOfSections...viewModel.numberOfSections - 1))
    }, completion: nil)
}
over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Unfortunately, by calling performBatchUpdates, the layout automatically animates all items itself. Even until now, there's no way to explicitly tell which items to animate and which not to.

However, I came up with a resolution which is kind of an anti-pattern.

For your header class, override these methods:

       -(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];
        }

Now if shouldDisableAnimations is true, the collectionView's automatic animations won't be applied to our header.

However, disabling the animations for the header may cause other glitches (e.g. when you scroll down a lot, and then remove all the cells. the header will jump to top instantly and the collectionView will scroll to top with animation resulting in a glitch!)

To handle this we need to set shouldDisableAnimations for the header in the correct time which is when prepareForCollectionViewUpdates gets called. Unfortunately, we cannot do this through the header's attribute since the attributes are applied to the header after animation gets finished. So we need to make direct access to the header's instance directly from within prepareForCollectionViewUpdates method. (there are several ways to do this. I did this by keeping a weak reference of the header on a class property of itself)

-(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.
}

over 4 years ago · Santiago Trujillo Report

0

The above solution I provided earlier seems not to work on iOS 13.

During performBatchUpdates, UICollectionView doesn't apply layoutAttributes for elements. The only way to fix this is to make set the frame (or any other parameters) on the header explicitly from within prepare() method of the layout. Because there's an ongoing animation being performed, UICollectionView won't layout during the animation.

I think Apple has to add a feature to explicitly mark which elements participate in the animation and which not. This can be done through the layoutAttributes of elements or a separate method in UICollectionViewLayout.

Accessing the element instances from layout is a no-no! But there's no other workarounds at the moment.

over 4 years ago · Santiago Trujillo Report

0

I was also having the same problem, but the work around I have done is reload the collectionView(the sticky header didn't disappears) it self instead of insert.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!