7

I'm seeing some mysterious behaviour from UICollectionView with a custom layout (subclass of UICollectionViewLayout) when rotating the device.

I have a simple horizontally scrolling row of cells. When rotating the device from portrait to landscape, additional cells become visible that weren't visible before, and the animation of those appearing cells is wrong (it uses the familiar ghosting effect, which I think is some kind of default animation effect for collection view layouts). Notice the cell appearing on the far left in this animation:

UICollectionViewRotationGhosting

Some details about the custom layout setup:

  • shouldInvalidateLayoutForBoundsChange: returns YES.
  • In layoutAttributesForItemAtIndexPath: caches the attributes in a dictionary if they haven't already been created.
  • In layoutAttributesForElementsInRect: I calculate which cells should be visible by hand, and tweak their centre property slightly each time, before returning their attributes.

I then have the following code to deal with the initial/final layout attributes:

- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds
{
    [super prepareForAnimatedBoundsChange:oldBounds];
    self.animatingBoundsChange = YES;
}

- (void)finalizeAnimatedBoundsChange
{
    [super finalizeAnimatedBoundsChange];
    self.animatingBoundsChange = NO;
}

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
    if (self.animatingBoundsChange) {
        // If the view is rotating, appearing items should animate from their current attributes (specify `nil`).
        // Both of these appear to do much the same thing:
        //return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
        return nil;
    }
    return [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];
}

- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
    if (self.animatingBoundsChange) {
        // If the view is rotating, disappearing items should animate to their new attributes.
        return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    }
    return [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath];
}

It seems to me that the initial layout attributes for the newly appearing cell are somehow not right (it ends in the correct location after all). But when I log the center property of the layout attributes returned from the initalLayout... method, everything looks correct - all evenly spaced along the horizontal axis.

The only distinguishing feature of the cell that isn't animating correctly is that its cell isn't returned in the [collectionView visibleCells] array when the initialLayout... method is called. The layoutAttributesForElementsInRect: that precedes it does correctly identify that its layout attributes are needed however.

So what's going on? Some insights into what's going on under the hood would be so helpful... UICollectionView seems like a massive black box.

Stuart
  • 36,683
  • 19
  • 101
  • 139
  • http://stackoverflow.com/questions/12649271/what-is-the-best-best-way-to-disable-cross-fades-on-uicollectionview-uicollectio?rq=1 – Arun_ Oct 31 '13 at 11:35
  • If iOS detects that a transition cannot occur smoothly, i.e., this scenario, it will use a cross fade animation on any cells that collide with existing cells. This is documented in the WWDC video on UICollectionViews in iOS7. – Tim Oct 31 '13 at 13:18
  • @Arun_k That question doesn't appear relevant to this one - they are asking for clarification on why `initial/finalLayout...` are called on rotation, and in fact they confirm that adding code as above __prevents__ the crossfade animation. This questions regards additional cells being presented following an animated bounds change. – Stuart Nov 03 '13 at 09:38
  • @Jeff Thanks, could you provide the name of the particular video please? I'm aware of the default crossfade animation, however my understanding is that this should be fully customisable using the `initial/finalLayout...` methods. I don't see why the appearing cell cannot be animated smoothly if an initial position is provided. – Stuart Nov 03 '13 at 09:42
  • Advanced Techniques with UIKit Dynamics and Custom Transitions Using View Controllers. https://developer.apple.com/wwdc/videos/ – Tim Nov 03 '13 at 13:23
  • @Jeff I never did find the details in those videos you referenced... I don't think they mention rotations/crossfades? – Stuart Jan 29 '14 at 06:29
  • did you manage to find a solution for this? I have a similar problem, where some of my cells are not appearing. http://stackoverflow.com/questions/22413621/disappearing-uicollectionviewcells-on-landscape-orientation – Frank Mar 16 '14 at 21:36

1 Answers1

0

Building on the answer that Arun_k linked to, it seems as if you might need to return something different from initialLayoutAttributesForAppearingItemAtIndexPath: besides nil for the new cell that is appearing. nil works for the cells that are already on-screen, but might not be the right thing to return for a new cell. Maybe you can play around with returning a fixed UICollectionViewLayoutAttributes value for all cells in this method to see if it changes the animating behavior of the newly inserted cell and then go on from there.

This question sheds even more light on this issue: initialLayoutAttributesForAppearingItemAtIndexPath fired for all visible cells, not just inserted cells

Community
  • 1
  • 1
Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
  • Thanks for reply. Unfortunately, as I said in my question, returning a non-nil value (`[self layoutAttributesForItem...]`) doesn't make any difference, even though logging the returned values shows the correct initial position for all cells, including the newly appearing cell. I'll play around as suggested and see if I can find out any more. – Stuart Nov 03 '13 at 09:32