如何旋转UICollectionView类似的照片应用程序,并保持当前视图居中?

我有一个使用UICollectionViewUICollectionViewFlowLayout的照片库视图,它具有pagingEnabled和水平滚动一次只显示一个视图。

很好的工作,直到我试图旋转它…

当我旋转设备,在willRotateToInterfaceOrientation:duration:我更新collectionView.contentOffset ,使其保持在正确的项目,我调整currentCell,以便animation到新的维度。 问题在于两个状态之间的animation,“上一个”方向从左上angular开始animation,并进入视图中的其他单元格。 我在做什么错误的是,在屏幕上animation的视图是FUBAR?

这是它在行动中的样子:

http://www.smugmug.com/gallery/n-3F9kD/i-BwzRzRf/A (忽略波涛汹涌的video,那是Quicktime的错:p)

这是我的willAnimateRotationToInterfaceOrientation:duration

 - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; // Update the flowLayout's size to the new orientation's size UICollectionViewFlowLayout *flow = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout; if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { flow.itemSize = CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height); } else { flow.itemSize = CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height); } self.collectionView.collectionViewLayout = flow; [self.collectionView.collectionViewLayout invalidateLayout]; // Get the currently visible cell PreviewCellView *currentCell = (PreviewCellView*)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:_currentIndex inSection:0]]; // Resize the currently index to the new flow's itemSize CGRect frame = currentCell.frame; frame.size = flow.itemSize; currentCell.frame = frame; // Keep the collection view centered by updating the content offset CGPoint newContentOffset = CGPointMake(_currentIndex * frame.size.width, 0); self.collectionView.contentOffset = newContentOffset; } 

据我所知,我无法find任何示例代码,说明如何创build一个“照片库”样式集合视图,优雅地旋转。

我在这方面挣扎了好一阵子,直到我至less发​​现了这个“化妆方法”:

在旋转过程中,在collectionView的顶部添加一个全屏UIImageView和当前图像(并设置正确的自动布局约束)。 像这样:

 -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration: (NSTimeInterval)duration { [self.collectionView.collectionViewLayout invalidateLayout]; // show a UIImageView with the current image on top of the collectionView // to cover the ugly animation self.imageViewOnTopOfCollectionView.image = [self imageForItemAtIndexPath:self.currentIndexPath]; self.imageViewOnTopOfCollectionView.hidden = NO; // show a centered, very large 'fakeBackground' view on top of // the UICollectionView, but below the UIImageView self.fakeBackground.hidden = NO; } -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { // ... set correct contentOffset // hide the fake views again self.imageViewOnTopOfCollectionView.hidden = YES; self.fakeBackground.hidden = YES; } 

一个大型的“fakeBackground”将是一个额外的改进,以防止丑陋的集合视图animation的部分在它旋转的时候在这个imageViews框架之外可见。 例如一个超大的视图(大于所有维度的视图边界)UIView与collectionView具有相同的背景颜色,在collectionView和imageView之间有一个z-Index。

这项工作就像一个魅力:

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

添加到@ Goodsquirrel的伟大的答案,这里是我如何使用当前的iOS8 API和Swift实现它:

 override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator); // show the dummy imageView self.imageViewOnTopOfCollectionView.image = self.imageForItemAtIndex(self.currentIndex) self.imageViewOnTopOfCollectionView.hidden = false; coordinator.animateAlongsideTransition({ (context) -> Void in // update the dummy imageView's frame var frame:CGRect = self.imageViewOnTopOfCollectionView.frame; frame.size = size; self.imageViewOnTopOfCollectionView.frame = frame; // update the flow's item size if let flow = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { flow.itemSize = size; } // scroll to the current index self.scrollToItem(self.currentIndex); }, completion: { (context) -> Void in // remove the dummy imageView self.imageViewOnTopOfCollectionView.hidden = true; }); }