如何旋转类似于照片应用程序的 UICollectionView 并保持当前视图居中?
Posted
技术标签:
【中文标题】如何旋转类似于照片应用程序的 UICollectionView 并保持当前视图居中?【英文标题】:How to rotate a UICollectionView similar to the photos app and keep the current view centered? 【发布时间】:2013-09-09 23:05:55 【问题描述】:我有一个使用UICollectionView
和UICollectionViewFlowLayout
的照片库视图,它有pagingEnabled
并且水平滚动一次只显示一个视图。
在我尝试旋转它之前效果很好......
当我旋转设备时,在willRotateToInterfaceOrientation:duration:
中,我更新了collectionView.contentOffset
,使其停留在正确的项目上,我调整了 currentCell 的大小,使其动画到新的尺寸。 问题出在两个状态之间的动画中,“前一个”方向动画从左上角并扔进视图其他单元格。我是什么做错了,以至于屏幕上的动画视图是 FUBAR?
这是实际效果:
http://www.smugmug.com/gallery/n-3F9kD/i-BwzRzRf/A(忽略断断续续的视频,那是 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;
据我所知,我在任何地方都找不到任何示例代码来说明如何制作优雅旋转的“照片库”风格的收藏视图。
【问题讨论】:
【参考方案1】:我为此苦苦挣扎了很长一段时间,直到我至少找到了这个“美容解决方法”:
在旋转期间在 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”将是一个额外的改进,以防止丑陋的集合视图动画的一部分在这个 imageViews 框架旋转时在它之外可见。例如。一个超大(在所有维度上都大于视图边界)UIView,其背景颜色与 collectionView 相同,z-Index 位于 collectionView 和 imageView 之间。
【讨论】:
我也选择了这个解决方案,但一直希望有一种优雅的方式来使用 UICollectionView 本身,但它看起来不会发生这样的事情:) 我唯一要补充的是:在 willRotate 方法中,计算您当前的滚动位置。然后在 didRotate 中使用该位置。看了你的代码后,我花了几分钟才弄清楚那部分,但我突然明白了。否则,优雅地解决一个不优雅的问题。 对 fakeBackground 的替代方法是简单地将 imageViewOnTopOfCollectionView 的 backgroundColor 设置为黑色(或其他一些纯色)。 @alku83:我认为这不会解决问题。 imageViewOnTopOfCollectionView 根本不足以在旋转过程中完全覆盖下面的视图。更改它的背景颜色不会使视图变大。 (fakeBackground 不仅对于小于屏幕的图像是必需的,而且对于全屏图像也是必需的。)【参考方案2】:这项工作就像一个魅力:
-(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];
];
【讨论】:
这是否解决了op提到的问题?【参考方案3】:除了@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;
);
【讨论】:
以上是关于如何旋转类似于照片应用程序的 UICollectionView 并保持当前视图居中?的主要内容,如果未能解决你的问题,请参考以下文章
如何在自定义 UICollectionView 的旋转后触发 drawrect?