Как повернуть UICollectionView аналогично приложению фотографий и сохранить текущее изображение в центре?

У меня есть представление фотогалереи, которое использует 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 size to the new orientation 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 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

Я долгое время боролся с этим, пока я, по крайней мере, не нашел это "косметическое обходное решение":

Добавьте полный экран UIImageView с текущим изображением (и установите правильные ограничения макета) в верхней части коллекцииView во время вращения. Например:

-(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" станет дополнительным улучшением, чтобы предотвратить появление части анимации уродливого коллекционного просмотра вне этого кадра изображения. Пока он вращается. Например. (больше, чем границы обзора во всех измерениях) UIView с тем же цветом фона, что и CollectionView, с z-индексом только между collectionView и imageView.

Ответ 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];
    }];
}

Ответ 3

Добавляя к @Goodsquirrel отличный ответ, вот как я его реализовал с использованием текущего API iOS8 и 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 frame
        var frame:CGRect = self.imageViewOnTopOfCollectionView.frame;
        frame.size = size;
        self.imageViewOnTopOfCollectionView.frame = frame;

        // update the flow 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;
    });
}