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
I use a variant of fz. answer (iOS 7 & 8) :
Before rotation :
After rotation :
Remove the image view.
@property (nonatomic) NSIndexPath *indexPath;
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
self.indexPathAfterRotation = [[self.collectionView indexPathsForVisibleItems] firstObject];
// Creates a temporary imageView that will occupy the full screen and rotate.
UIGraphicsBeginImageContextWithOptions(self.collectionView.bounds.size, YES, 0);
[self.collectionView drawViewHierarchyInRect:self.collectionView.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[imageView setFrame:[self.collectionView bounds]];
[imageView setTag:kTemporaryImageTag];
[imageView setBackgroundColor:[UIColor blackColor]];
[imageView setContentMode:UIViewContentModeCenter];
[imageView setAutoresizingMask:0xff];
[self.view insertSubview:imageView aboveSubview:self.collectionView];
[[self.collectionView collectionViewLayout] invalidateLayout];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[self.collectionView scrollToItemAtIndexPath:self.indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
[[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}
This problem bothered me for a bit as well. The highest voted answered seemed a bit too hacky for me so I just dumbed it down a bit and just change the alpha of the collection view respectively before and after rotation. I also don't animate the content offset update.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
self.collectionView.alpha = 0;
[self.collectionView.collectionViewLayout invalidateLayout];
self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);
[self.collectionView setContentOffset:newContentOffset animated:NO];
self.collectionView.alpha = 1;
}
Fairly smooth and less hacky.
The "just snap" answer above didn't work for me as it frequently didn't end on the item that was in view before the rotate. So I derived a flow layout that uses a focus item (if set) for calculating the content offset. I set the item in willAnimateRotationToInterfaceOrientation and clear it in didRotateFromInterfaceOrientation. The inset adjustment seems to be need on IOS7 because the Collection view can layout under the top bar.
@interface HintedFlowLayout : UICollectionViewFlowLayout
@property (strong)NSIndexPath* pathForFocusItem;
@end
@implementation HintedFlowLayout
-(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];
}
}
@end
For those using iOS 8+, willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are deprecated.
You should use the following now:
/*
This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized).
If you override this method, you should either call super to propagate the change to children or manually forward the change to children.
*/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Update scroll position during rotation animation
self.collectionView.contentOffset = (CGPoint){contentOffsetX, contentOffsetY};
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Whatever you want to do when the rotation animation is done
}];
}
Swift 3:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context:UIViewControllerTransitionCoordinatorContext) in
// Update scroll position during rotation animation
}) { (context:UIViewControllerTransitionCoordinatorContext) in
// Whatever you want to do when the rotation animation is done
}
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize pnt = CGSizeMake(70, 70);
return pnt; }
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
// UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)
return UIEdgeInsetsMake(3, 0, 3, 0); }
This way you can adjust the content offset and the size of your cell.
This work like a charm:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.view.bounds.size;
}
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
int currentPage = collectionMedia.contentOffset.x / collectionMedia.bounds.size.width;
float width = collectionMedia.bounds.size.height;
[UIView animateWithDuration:duration animations:^{
[self.collectionMedia setContentOffset:CGPointMake(width * currentPage, 0.0) animated:NO];
[[self.collectionMedia collectionViewLayout] invalidateLayout];
}];
}