如何旋转类似于照片应用程序的 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 【问题描述】:

我有一个使用UICollectionViewUICollectionViewFlowLayout 的照片库视图,它有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 并保持当前视图居中?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅在 iPhone 上旋转照片?

如何旋转从照片库中挑选的照片进行编辑

如何像 iPhone 照片应用程序那样防止自动旋转?

如何在自定义 UICollectionView 的旋转后触发 drawrect?

在ios应用程序中更改位置iPhone时如何旋转照片[关闭]

实现类似于 iPhone 联系人应用程序的“添加照片”按钮