Keeping the contentOffset in a UICollectionView while rotating Interface Orientation

后端 未结 24 1363
野性不改
野性不改 2020-12-04 07:22

I\'m trying to handle interface orientation changes in a UICollectionViewController. What I\'m trying to achieve is, that I want to have the same contentOffset afte

24条回答
  •  温柔的废话
    2020-12-04 08:21

    Solution 1, "just snap"

    If what you need is only to ensure that the contentOffset ends in a right position, you can create a subclass of UICollectionViewLayout and implement targetContentOffsetForProposedContentOffset: method. For example you could do something like this to calculate the page:

    - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
    {
        NSInteger page = ceil(proposedContentOffset.x / [self.collectionView frame].size.width);
        return CGPointMake(page * [self.collectionView frame].size.width, 0);
    }
    

    But the problem that you'll face is that the animation for that transition is extremely weird. What I'm doing on my case (which is almost the same as yours) is:

    Solution 2, "smooth animation"

    1) First I set the cell size, which can be managed by collectionView:layout:sizeForItemAtIndexPath: delegate method as follows:

    - (CGSize)collectionView:(UICollectionView *)collectionView
                      layout:(UICollectionViewLayout  *)collectionViewLayout
      sizeForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        return [self.view bounds].size;
    }
    

    Note that [self.view bounds] will change according to the device rotation.

    2) When the device is about to rotate, I'm adding an imageView on top of the collection view with all resizing masks. This view will actually hide the collectionView weirdness (because it is on top of it) and since the willRotatoToInterfaceOrientation: method is called inside an animation block it will rotate accordingly. I'm also keeping the next contentOffset according to the shown indexPath so I can fix the contentOffset once the rotation is done:

    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                    duration:(NSTimeInterval)duration
    {
        // Gets the first (and only) visible cell.
        NSIndexPath *indexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
        KSPhotoViewCell *cell = (id)[self.collectionView cellForItemAtIndexPath:indexPath];
    
        // Creates a temporary imageView that will occupy the full screen and rotate.
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[[cell imageView] image]];
        [imageView setFrame:[self.view bounds]];
        [imageView setTag:kTemporaryImageTag];
        [imageView setBackgroundColor:[UIColor blackColor]];
        [imageView setContentMode:[[cell imageView] contentMode]];
        [imageView setAutoresizingMask:0xff];
        [self.view insertSubview:imageView aboveSubview:self.collectionView];
    
        // Invalidate layout and calculate (next) contentOffset.
        contentOffsetAfterRotation = CGPointMake(indexPath.item * [self.view bounds].size.height, 0);
        [[self.collectionView collectionViewLayout] invalidateLayout];
    }
    

    Note that my subclass of UICollectionViewCell has a public imageView property.

    3) Finally, the last step is to "snap" the content offset to a valid page and remove the temporary imageview.

    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
    {
        [self.collectionView setContentOffset:contentOffsetAfterRotation];
        [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
    }
    

提交回复
热议问题