Keeping the contentOffset in a UICollectionView while rotating Interface Orientation

后端 未结 24 1350
野性不改
野性不改 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:08

    My way is to use a UICollectionViewFlowlayout object.

    Set the ojbect line spacing if it scrolls horizontally.

    [flowLayout setMinimumLineSpacing:26.0f];
    

    Set its interitem spacing if it scrolls vertically.

    [flowLayout setMinimumInteritemSpacing:0.0f];
    

    Notice it behaves different when you rotate the screen. In my case, I have it scrolls horizontally so minimumlinespacing is 26.0f. Then it seems horrible when it rotates to landscape direction. I have to check rotation and set minimumlinespacing for that direction 0.0f to make it right.

    That's it! Simple.

    0 讨论(0)
  • 2020-12-04 08:10

    After rotate interface orientation the UICollectionViewCell usually move to another position, because we won't update contentSize and contentOffset.

    So the visible UICollectionViewCell always not locate at expected position.

    The visible UICollectionView which we expected image as follow

    Orientation which we expected

    UICollectionView must delegate the function [collectionView sizeForItemAtIndexPath] of『UICollectionViewDelegateFlowLayout』.

    And you should calculate the item Size in this function.

    The custom UICollectionViewFlowLayout must override the functions as follow.

    1. -(void)prepareLayout

      . Set itemSize, scrollDirection and others.

    2. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

      . Calculate page number or calculate visible content offset.

    3. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset

      . Return visual content offset.

    4. -(CGSize)collectionViewContentSize

      . Return the total content size of collectionView.

    Your viewController must override 『willRotateToInterfaceOrientation』and in this function you should call the function [XXXCollectionVew.collectionViewLayout invalidateLayout];

    But 『willRotateToInterfaceOrientation』 is deprecated in iOS 9, or you could call the function [XXXCollectionVew.collectionViewLayout invalidateLayout] in difference way.

    There's an example as follow : https://github.com/bcbod2002/CollectionViewRotationTest

    0 讨论(0)
  • 2020-12-04 08:12

    What does the job for me is this:

    1. Set the size of your my cells from your my UICollectionViewDelegateFlowLayout method

      func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
      {
          return collectionView.bounds.size
      }
      
    2. After that I implement willRotateToInterfaceOrientationToInterfaceOrientation:duration: like this

      override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) 
      {
          let currentPage = Int(collectionView.contentOffset.x / collectionView.bounds.size.width)
      
          var width = collectionView.bounds.size.height
          UIView.animateWithDuration(duration) {
              self.collectionView.setContentOffset(CGPointMake(width * CGFloat(currentPage), 0.0), animated: false)
              self.collectionView.collectionViewLayout.invalidateLayout()
          }
      }
      

    The above code is in Swift but you get the point and it's easy to "translate"

    0 讨论(0)
  • 2020-12-04 08:14

    Swift 4.2 subclass:

    class RotatableCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
        private var focusedIndexPath: IndexPath?
    
        override func prepare(forAnimatedBoundsChange oldBounds: CGRect) {
            super.prepare(forAnimatedBoundsChange: oldBounds)
            focusedIndexPath = collectionView?.indexPathsForVisibleItems.first
        }
    
        override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
            guard let indexPath = focusedIndexPath
                , let attributes = layoutAttributesForItem(at: indexPath)
                , let collectionView = collectionView else {
                    return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
            }
            return CGPoint(x: attributes.frame.origin.x - collectionView.contentInset.left,
                           y: attributes.frame.origin.y - collectionView.contentInset.top)
        }
    
        override func finalizeAnimatedBoundsChange() {
            super.finalizeAnimatedBoundsChange()
            focusedIndexPath = nil
        }
    }
    
    0 讨论(0)
  • 2020-12-04 08:16

    You might want to hide the collectionView during it's (incorrect) animation and show a placeholder view of the cell that rotates correctly instead.

    For a simple photo gallery I found a way to do it that looks quite good. See my answer here: How to rotate a UICollectionView similar to the photos app and keep the current view centered?

    0 讨论(0)
  • 2020-12-04 08:17

    To piggy back off troppoli's solution you can set the offset in your custom class without having to worry about remembering to implement the code in your view controller. prepareForAnimatedBoundsChange should get called when you rotate the device then finalizeAnimatedBoundsChange after its done rotating.

    @interface OrientationFlowLayout ()
    
    @property (strong)NSIndexPath* pathForFocusItem;
    
    @end
    
    @implementation OrientationFlowLayout
    
    - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset {
        if (self.pathForFocusItem) {
            UICollectionViewLayoutAttributes* layoutAttrs = [self layoutAttributesForItemAtIndexPath:
                                                             self.pathForFocusItem];
            return CGPointMake(layoutAttrs.frame.origin.x - self.collectionView.contentInset.left,
                               layoutAttrs.frame.origin.y - self.collectionView.contentInset.top);
        }
        else {
            return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
        }
    }
    
    - (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds {
        [super prepareForAnimatedBoundsChange:oldBounds];
        self.pathForFocusItem = [[self.collectionView indexPathsForVisibleItems] firstObject];
    }
    
    - (void)finalizeAnimatedBoundsChange {
        [super finalizeAnimatedBoundsChange];
        self.pathForFocusItem = nil;
    }
    
    @end
    
    0 讨论(0)
提交回复
热议问题