it-swarm.com.ru

Хранение contentOffset в UICollectionView при вращении ориентации интерфейса

Я пытаюсь обрабатывать изменения ориентации интерфейса в UICollectionViewController. Я пытаюсь добиться того, чтобы после поворота интерфейса у меня был same contentOffset. Это означает, что оно должно быть изменено в соответствии с отношением изменения границ.

Начиная с портрета со смещением содержимого {bounds.size.width * 2, 0}…

UICollectionView in portait

… Должно приводить к смещению содержимого в альбомной ориентации также с {bounds.size.width * 2, 0} (и наоборот).

UICollectionView in landscape

Вычисление нового смещения не является проблемой, но не знаю, где (или когда) его установить, чтобы получить плавную анимацию. То, что я делаю, это делает недействительным макет в willRotateToInterfaceOrientation:duration: и сбрасывает смещение содержимого в didRotateFromInterfaceOrientation::

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration;
{
    self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
                                                    self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
    [self.collectionView.collectionViewLayout invalidateLayout];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
    CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
                                           self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);
    [self.collectionView newContentOffset animated:YES];
}

Это изменяет смещение содержимого после поворота.

Как я могу установить его во время вращения? Я попытался установить новое смещение содержимого в willAnimateRotationToInterfaceOrientation:duration:, но это привело к очень странному поведению.

Пример можно найти в моем проекте на GitHub .

66
Tobias Kräntzer

Вот код в Swift 3.1 и то же самое работает для Swift 4.2

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.viewWillTransition(to: size, with: coordinator)
    let offset = self.collectionView?.contentOffset;
    let width  = self.collectionView?.bounds.size.width;

    let index     = round(offset!.x / width!);
    let newOffset = CGPoint(x: index * size.width, y: offset!.y)

    self.collectionView?.setContentOffset(newOffset, animated: false)


    coordinator.animate(alongsideTransition: { (context) in
        self.collectionView?.reloadData()
        self.collectionView?.setContentOffset(newOffset, animated: false)
    }, completion: nil)
}
19
GSK

Решение 1, «просто щелкнуть»

Если вам нужно только убедиться, что contentOffset заканчивается в правильной позиции, вы можете создать подкласс UICollectionViewLayout и реализовать targetContentOffsetForProposedContentOffset: method. Например, вы можете сделать что-то вроде этого, чтобы вычислить страницу:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = ceil(proposedContentOffset.x / [self.collectionView frame].size.width);
    return CGPointMake(page * [self.collectionView frame].size.width, 0);
}

Но проблема, с которой вы столкнетесь, заключается в том, что анимация для этого перехода очень странная. То, что я делаю в моем случае (которое почти совпадает с вашим):

Решение 2, "плавная анимация"

1) Сначала я устанавливаю размер ячейки, которым можно управлять с помощью collectionView: layout: sizeForItemAtIndexPath: делегировать метод следующим образом:

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout  *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.view bounds].size;
}

Обратите внимание, что [self.view bounds] будет меняться в зависимости от поворота устройства.

2) Когда устройство собирается вращаться, я добавляю imageView поверх представления коллекции со всеми масками изменения размера. Этот вид на самом деле скрывает странность collectionView (потому что он находится поверх него), и, поскольку willRotatoToInterfaceOrientation: метод вызывается внутри блока анимации, он будет соответствующим образом вращаться. Я также сохраняю следующий contentOffset в соответствии с показанным indexPath, чтобы я мог исправить contentOffset после завершения поворота:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration
{
    // Gets the first (and only) visible cell.
    NSIndexPath *indexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
    KSPhotoViewCell *cell = (id)[self.collectionView cellForItemAtIndexPath:indexPath];

    // Creates a temporary imageView that will occupy the full screen and rotate.
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[[cell imageView] image]];
    [imageView setFrame:[self.view bounds]];
    [imageView setTag:kTemporaryImageTag];
    [imageView setBackgroundColor:[UIColor blackColor]];
    [imageView setContentMode:[[cell imageView] contentMode]];
    [imageView setAutoresizingMask:0xff];
    [self.view insertSubview:imageView aboveSubview:self.collectionView];

    // Invalidate layout and calculate (next) contentOffset.
    contentOffsetAfterRotation = CGPointMake(indexPath.item * [self.view bounds].size.height, 0);
    [[self.collectionView collectionViewLayout] invalidateLayout];
}

Обратите внимание, что мой подкласс UICollectionViewCell имеет открытое свойство imageView.

3) Наконец, последний шаг - «привязать» смещение контента к допустимой странице и удалить временное изображение.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.collectionView setContentOffset:contentOffsetAfterRotation];
    [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}
18
fz.

Ответ «просто щелкнуть» выше не работал для меня, поскольку он часто не заканчивался на элементе, который был виден до поворота. Поэтому я получил макет потока, который использует элемент фокуса (если установлен) для вычисления смещения содержимого. Я устанавливаю элемент в willAnimateRotationToInterfaceOrientation и очищаю его в didRotateFromInterfaceOrientation. Корректировка вставки, кажется, необходима в IOS7, потому что представление «Коллекция» может располагаться под верхней панелью.

@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
13
troppoli

Для тех, кто использует iOS 8+, willRotateToInterfaceOrientation и didRotateFromInterfaceOrientation устарели. 

Вы должны использовать следующее сейчас:

/* 
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
    }];
}

Свифт 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
    }
}
11
Neilc

Я думаю, что правильным решением является переопределение - (CGPoint) targetContentOffsetForProposedContentOffset: (CGPoint) предложилContentOffset метод в подклассе UICollectionViewFlowLayout

Из документов:

Во время обновления макетов или при переходе между макетами, представление коллекции вызывает этот метод, чтобы дать вам возможность изменить предлагаемое смещение содержимого для использования в конце анимации . Вы можете переопределить этот метод, если анимация или переход могут Расположите элементы таким образом, чтобы это не было оптимальным для вас дизайн.

7
2cupsOfTech

Чтобы вернуться к решению Тропполи , вы можете установить смещение в своем пользовательском классе, не беспокоясь о том, чтобы не забыть реализовать код в вашем контроллере представления. prepareForAnimatedBoundsChange должен вызываться при повороте устройства, а затем finalizeAnimatedBoundsChange после его поворота.

@interface OrientationFlowLayout ()

@property (strong)NSIndexPath* pathForFocusItem;

@end

@implementation OrientationFlowLayout

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

- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds {
    [super prepareForAnimatedBoundsChange:oldBounds];
    self.pathForFocusItem = [[self.collectionView indexPathsForVisibleItems] firstObject];
}

- (void)finalizeAnimatedBoundsChange {
    [super finalizeAnimatedBoundsChange];
    self.pathForFocusItem = nil;
}

@end
4
Joe

Эта проблема немного беспокоила меня. Ответы с наибольшим количеством голосов показались мне слишком хакерскими, поэтому я просто немного их ошарашил и просто изменил альфа представления коллекции соответственно до и после поворота. Я также не анимирую обновление смещения контента.

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

Довольно гладкая и менее хакерская.

4
mafiOSo

Я использую вариант фз. ответ (iOS 7 и 8):

До вращения: 

  1. Сохранить текущий видимый путь индекса
  2. Создайте снимок collectionView
  3. Поместите UIImageView с ним поверх представления коллекции

После вращения:

  1. Прокрутите до сохраненного индекса
  2. Снимите вид изображения.

    @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];
    }
    
4
dulgan

Эта работа, как шарм:

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

После поворота ориентации интерфейса UICollectionViewCell обычно перемещается в другую позицию, потому что мы не будем обновлять contentSize и contentOffset. 

Таким образом, видимый UICollectionViewCell всегда не располагается в ожидаемой позиции.

Видимый UICollectionView, который мы ожидали, изображение следующим образом

Ориентация, которую мы ожидали

UICollectionView должен делегировать функцию [collectionView sizeForItemAtIndexPath] «UICollectionViewDelegateFlowLayout».

И вы должны рассчитать размер элемента в этой функции.

Пользовательский UICollectionViewFlowLayout должен переопределить функции следующим образом.

  1. -(void)prepareLayout

    , Установите itemSize, scrollDirection и другие.

  2. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

    , Рассчитать номер страницы или рассчитать видимое смещение контента.

  3. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset

    , Вернуть смещение визуального содержимого.

  4. -(CGSize)collectionViewContentSize

    , Вернуть общий размер содержимого collectionView.

Ваш viewController должен переопределить «willRotateToInterfaceOrientation» и в этой функции Вы должны вызвать функцию [XXXCollectionVew.collectionViewLayout invalidateLayout];

Но «willRotateToInterfaceOrientation» устарела в iOS 9, или вы можете вызвать функцию [XXXCollectionVew.collectionViewLayout invalidateLayout] другим способом.

Вот следующий пример: https://github.com/bcbod2002/CollectionViewRotationTest

2
Goston Wu

Что работа для меня заключается в следующем:

  1. Установите размер ваших ячеек my с помощью метода my UICollectionViewDelegateFlowLayout

    func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
    {
        return collectionView.bounds.size
    }
    
  2. После этого я реализую willRotateToInterfaceOrientationToInterfaceOrientation:duration:, как это

    override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) 
    {
        let currentPage = Int(collectionView.contentOffset.x / collectionView.bounds.size.width)
    
        var width = collectionView.bounds.size.height
        UIView.animateWithDuration(duration) {
            self.collectionView.setContentOffset(CGPointMake(width * CGFloat(currentPage), 0.0), animated: false)
            self.collectionView.collectionViewLayout.invalidateLayout()
        }
    }
    

Вышеприведенный код находится в Swift , но вы поняли, и это легко "перевести"

1
Mihai Fratu

в Свифте 3.

вы должны отследить, какой элемент ячейки (Page) представлен перед поворотом по indexPath.item, координате x или как-то еще. Затем в вашем UICollectionView:

override func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {

    let page:CGFloat = pageNumber // your tracked page number eg. 1.0
    return CGPoint(x: page * collectionView.frame.size.width, y: -(topInset))
    //the 'y' value would be '0' if you don't have any top EdgeInset
}

В моем случае я делаю недействительным макет в viewDidLayoutSubviews (), поэтому collectionView.frame.size.width - это ширина представления collectionVC, которое было повернуто.

1
Kyle KIM

Если обнаружено, что использование targetContentOffsetForProposedContentOffset не работает во всех сценариях, и проблема с использованием didRotateFromInterfaceOrientation заключается в том, что он дает визуальные артефакты. Мой отлично работающий код выглядит следующим образом:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    _indexPathOfFirstCell = [self indexPathsForVisibleItems].firstObject;
}

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    if (_indexPathOfFirstCell) {
        [UIView performWithoutAnimation:^{
            [self scrollToItemAtIndexPath:self->_indexPathOfFirstCell atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
        }];
        _indexPathOfFirstCell = nil;
    }
}

Ключ заключается в том, чтобы использовать метод willRotateToInterfaceOrientation, чтобы определить часть в представлении, к которому вы хотите прокрутить, и willAnimationRotationToInterfaceOrientation, чтобы пересчитать его, когда представление изменило свой размер (границы уже изменились, когда этот метод вызывается платформой) и фактически прокрутите на новую позицию без анимации. В моем коде я использовал индексный путь для первой визуальной ячейки, чтобы сделать это, но процент от contentOffset.y/contentSize.height также сделал бы работу немного по-другому.

1
Werner Altewischer

Swift 4.2 подкласс:

class RotatableCollectionViewFlowLayout: UICollectionViewFlowLayout {

    private var focusedIndexPath: IndexPath?

    override func prepare(forAnimatedBoundsChange oldBounds: CGRect) {
        super.prepare(forAnimatedBoundsChange: oldBounds)
        focusedIndexPath = collectionView?.indexPathsForVisibleItems.first
    }

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
        guard let indexPath = focusedIndexPath
            , let attributes = layoutAttributesForItem(at: indexPath)
            , let collectionView = collectionView else {
                return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
        }
        return CGPoint(x: attributes.frame.Origin.x - collectionView.contentInset.left,
                       y: attributes.frame.Origin.x - collectionView.contentInset.left)
    }

    override func finalizeAnimatedBoundsChange() {
        super.finalizeAnimatedBoundsChange()
        focusedIndexPath = nil
    }
}
0
Bogdan Novikov

Мой способ заключается в использовании объекта UICollectionViewFlowlayout.

Установите межстрочный интервал, если он прокручивается горизонтально. 

[flowLayout setMinimumLineSpacing:26.0f];

Установите интервал между интервалами, если он прокручивается вертикально.

[flowLayout setMinimumInteritemSpacing:0.0f];

Обратите внимание, что он ведет себя по-разному при повороте экрана. В моем случае он прокручивается горизонтально, поэтому минимальное расстояние между строками равно 26.0f. Тогда это кажется ужасным, когда он поворачивается в ландшафтном направлении. Я должен проверить вращение и установить минимальное расстояние между линиями для этого направления 0.0f, чтобы сделать это правильно. 

Это оно! Просто.

0
Itsraininggold

Я решил эту проблему с помощью следующих шагов: 

  1. Рассчитать текущий прокручиваемый NSIndexPath  
  2. Отключить прокрутку и разбиение на страницы в UICollectionView  
  3. Применить новый макет потока к UICollectionView
  4. Включить прокрутку и разбиение на страницы в UICollectionView
  5. Прокрутить UICollectionView к текущему NSIndexPath

Вот шаблон кода, демонстрирующий вышеперечисленные шаги: 

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                            duration:(NSTimeInterval)duration;
{
     //Calculating Current IndexPath
     CGRect visibleRect = (CGRect){.Origin = self.yourCollectionView.contentOffset, .size = self.yourCollectionView.bounds.size};
     CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
     self.currentIndexPath = [self.yourCollectionView indexPathForItemAtPoint:visiblePoint];

     //Disable Scrolling and Pagination
     [self disableScrolling];

     //Applying New Flow Layout
     [self setupNewFlowLayout];

     //Enable Scrolling and Pagination
     [self enableScrolling];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
     //You can also call this at the End of `willRotate..` method.
     //Scrolling UICollectionView to current Index Path
     [self.yourCollectionView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}

- (void) disableScrolling
{
    self.yourCollectionView.scrollEnabled   = false;
    self.yourCollectionView.pagingEnabled   = false;
}

- (void) enableScrolling
{
    self.yourCollectionView.scrollEnabled   = true;
    self.yourCollectionView.pagingEnabled   = true;
}

- (void) setupNewFlowLayout
{
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    flowLayout.minimumInteritemSpacing = 0;
    flowLayout.minimumLineSpacing = 0;
    [flowLayout setItemSize:CGSizeMake(EXPECTED_WIDTH, EXPECTED_HEIGHT)];

    [self.yourCollectionView setCollectionViewLayout:flowLayout animated:YES];
    [self.yourCollectionView.collectionViewLayout invalidateLayout];
}

Надеюсь, это поможет.

0
Salman Khakwani

Ответ «просто сделай снимок» - это правильный подход и не требует дополнительного сглаживания с наложением снимков IMO. Однако есть проблема, которая объясняет, почему некоторые люди видят, что в некоторых случаях правильная страница не прокручивается. При расчете страницы вы хотите использовать высоту, а не ширину. Зачем? Потому что геометрия вида уже повернута к тому времени, когда вызывается targetContentOffsetForProposedContentOffset, и теперь ширина была высотой. Также округление более чувствительно, чем потолок. Так:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = round(proposedContentOffset.x / self.collectionView.bounds.size.height);
    return CGPointMake(page * self.collectionView.bounds.size.width, 0);
}
0
John Scalo

У меня была проблема с моим проектом, я использовал два разных макета для UICollectionView.

mCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"LandScapeCell" forIndexPath:indexPath];

theCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"PortraitCell" forIndexPath:indexPath];

Затем проверьте его для каждой ориентации и используйте свою конфигурацию для каждой ориентации.

0
Arun_
-(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); }

Таким образом, вы можете настроить смещение содержимого и размер ячейки.

0
Nikhil Lihla

Возможно, вы захотите скрыть collectionView во время (неправильной) анимации и показать вид заполнителя ячейки, которая вместо этого вращается правильно.

Для простой фотогалереи я нашел способ сделать это, который выглядит довольно хорошо. Смотрите мой ответ здесь: Как повернуть UICollectionView, похожий на приложение для фотографий, и сохранить текущий вид по центру?

0
Goodsquirrel

Используйте <CollectionViewDelegateFlowLayout> и в методе didRotateFromInterfaceOrientation: перезагрузите данные CollectionView.

Реализуйте метод collectionView:layout:sizeForItemAtIndexPath: для <CollectionViewDelegateFlowLayout> и в методе проверьте ориентацию интерфейса и примените свой пользовательский размер каждой ячейки.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{

    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

    if (UIInterfaceOrientationIsPortrait(orientation)) {

        return CGSizeMake(CGFloat width, CGFloat height);

    } else {

        return CGSizeMake(CGFloat width, CGFloat height);

    }

}
0
Soto_iGhost

У меня есть похожий случай, в котором я использую это

- (void)setFrame:(CGRect)frame
{
    CGFloat currentWidth = [self frame].size.width;
    CGFloat offsetModifier = [[self collectionView] contentOffset].x / currentWidth;

    [super setFrame:frame];

    CGFloat newWidth = [self frame].size.width;

    [[self collectionView] setContentOffset:CGPointMake(offsetModifier * newWidth, 0.0f) animated:NO];
}

Это представление, которое содержит collectionView . В суперпредставлении я также делаю это

- (void)setFrame:(CGRect)frame
{    
    UICollectionViewFlowLayout *collectionViewFlowLayout = (UICollectionViewFlowLayout *)[_collectionView collectionViewLayout];

    [collectionViewFlowLayout setItemSize:frame.size];

    [super setFrame:frame];
}

Это делается для того, чтобы настроить размеры ячеек на полный экран (точнее полный просмотр;)). Если вы не сделаете этого здесь, может появиться много сообщений об ошибках, о том, что размер ячейки больше, чем представление коллекции, и что поведение для этого не определено и бла бла бла .....

Эти методы to конечно могут быть объединены в один подкласс viewview или в представление, содержащее view collection, но для моего текущего проекта это был логический путь.

0
Saren Inden

У меня были некоторые проблемы с блоком animateAlongsideTransition в animateAlongsideTransition (см. Код ниже). 

Обратите внимание, что он вызывается во время (но не раньше) анимации Моей задачей было обновить позицию прокрутки табличного представления, используя прокрутку до верхней видимой строки (я столкнулся с проблемой на iPad, когда ячейки табличного представления сдвинулись вверх когда устройство вращается, поэтому я нашел решение для этой проблемы). Но может быть, это будет полезно и для contentOffset.

Я попытался решить проблему следующим образом:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    __weak TVChannelsListTableViewController *weakSelf = self;

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        weakSelf.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }];
}

Но это не сработало. Например, индексный путь верхнего cel был (0, 20). Но когда был вызван поворот устройства animateAlongsideTransition, блок [[weakSelf.tableView indexPathsForVisibleRows] firstObject] возвратил путь индекса (0, 27).

Я думал, что проблема была в получении путей индекса к weakSelf. Поэтому, чтобы решить проблему, я переместил self.topVisibleRowIndexPath перед вызовом метода [coordinator animateAlongsideTransition: completion]:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    __weak TVChannelsListTableViewController *weakSelf = self;
    self.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];

    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }];
}

И еще одна интересная вещь, которую я обнаружил, заключается в том, что устаревшие методы willRotateToInterfaceOrientation и willRotateToInterfaceOrientation все еще успешно вызываются в iOS позже 8.0, когда метод viewWillTransitionToSize не переопределен. 

Таким образом, другой способ решения проблемы в моем случае состоял в том, чтобы использовать устаревший метод вместо нового. Я думаю, что это было бы не правильным решением, но можно попробовать, если другие способы не сработают :)

0
Yulia