Подтвердить что ты не робот

Локальная логика UICollectionView отсутствует в горизонтальном просмотре прокрутки

У меня есть UICollectionView, который работает нормально, пока я не начну прокрутку. Вот некоторые фотографии сначала: enter image description here

Как вы можете видеть, это здорово. Когда я запускаю прокрутку (пейджинг включен), первый идет немного за кадром: enter image description here

Это проблема. У моего представления есть 3 вида, и я хочу прокручивать и показывать только 3 вида. Но по мере того, как он прокручивается (пейджинг включен), он скрывает немного первого представления и показывает немного следующего первого представления со следующей страницы.

А вот видео, потому что его трудно объяснить: Видео проблемы (Dropbox)

Вот изображение моих настроек UICollectionView: enter image description here

Это будет здорово, если кто-то может помочь!

4b9b3361

Ответ 1

Основная проблема заключается в том, что Flow Layout не предназначен для поддержки поискового вызова. Чтобы достичь эффекта пейджинга, вам придется пожертвовать пространством между ячейками. И аккуратно вычислите рамку ячеек и сделайте так, чтобы ее можно было разделить на рамку просмотра коллекции без остатков. Я объясню причину.

Высказывание следующего макета - это то, что вы хотели.

enter image description here

Обратите внимание, что самое левое поле (зеленый) не является частью интервала между ячейками. Он определяется секцией раздела макета потока. Поскольку компоновка потока не поддерживает гетерогенное значение интервала. Это не тривиальная задача.

Поэтому после установки интервала и вставки. Следующий макет - это то, что вы получите.

enter image description here

После перехода на следующую страницу. Очевидно, что ваши ячейки не выровнены, как вы ожидали.

enter image description here

Создание расстояния между ячейками 0 может решить эту проблему. Однако это ограничивает ваш дизайн, если вы хотите получить дополнительный запас на странице, особенно если маржа отличается от интервала между ячейками. Он также требует, чтобы кадр представления должен делиться на кадр ячейки. Иногда бывает больно, если ваш ракурс не фиксирован (учитывая случай вращения).

Реальное решение заключается в подклассе UICollectionViewFlowLayout и переопределении следующих методов

- (CGSize)collectionViewContentSize
{
    // Only support single section for now.
    // Only support Horizontal scroll 
    NSUInteger count = [self.collectionView.dataSource collectionView:self.collectionView
                                               numberOfItemsInSection:0];

    CGSize canvasSize = self.collectionView.frame.size;
    CGSize contentSize = canvasSize;
    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
    {
        NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
        NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;
        NSUInteger page = ceilf((CGFloat)count / (CGFloat)(rowCount * columnCount));
        contentSize.width = page * canvasSize.width;
    }

    return contentSize;
}


- (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGSize canvasSize = self.collectionView.frame.size;

    NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
    NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;

    CGFloat pageMarginX = (canvasSize.width - columnCount * self.itemSize.width - (columnCount > 1 ? (columnCount - 1) * self.minimumLineSpacing : 0)) / 2.0f;
    CGFloat pageMarginY = (canvasSize.height - rowCount * self.itemSize.height - (rowCount > 1 ? (rowCount - 1) * self.minimumInteritemSpacing : 0)) / 2.0f;

    NSUInteger page = indexPath.row / (rowCount * columnCount);
    NSUInteger remainder = indexPath.row - page * (rowCount * columnCount);
    NSUInteger row = remainder / columnCount;
    NSUInteger column = remainder - row * columnCount;

    CGRect cellFrame = CGRectZero;
    cellFrame.origin.x = pageMarginX + column * (self.itemSize.width + self.minimumLineSpacing);
    cellFrame.origin.y = pageMarginY + row * (self.itemSize.height + self.minimumInteritemSpacing);
    cellFrame.size.width = self.itemSize.width;
    cellFrame.size.height = self.itemSize.height;

    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
    {
        cellFrame.origin.x += page * canvasSize.width;
    }

    return cellFrame;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes * attr = [super layoutAttributesForItemAtIndexPath:indexPath];
    attr.frame = [self frameForItemAtIndexPath:indexPath];
    return attr;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * attrs = [NSMutableArray array];

    [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) {
        NSIndexPath * idxPath = attr.indexPath;
        CGRect itemFrame = [self frameForItemAtIndexPath:idxPath];
        if (CGRectIntersectsRect(itemFrame, rect))
        {
            attr = [self layoutAttributesForItemAtIndexPath:idxPath];
            [attrs addObject:attr];
        }
    }];

    return attrs;
}

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

Кроме того, если у вас нет миллионов ячеек. Кэширование этих UICollectionViewLayoutAttributes может быть хорошей идеей.

Ответ 2

Вы можете отключить пейджинг в UICollectionView и реализовать пользовательский механизм горизонтальной прокрутки/подкачки с настраиваемой шириной/смещением страницы следующим образом:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    float pageWidth = 210;

    float currentOffset = scrollView.contentOffset.x;
    float targetOffset = targetContentOffset->x;
    float newTargetOffset = 0;

    if (targetOffset > currentOffset)
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
    else
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;

    if (newTargetOffset < 0)
        newTargetOffset = 0;
    else if (newTargetOffset > scrollView.contentSize.width)
        newTargetOffset = scrollView.contentSize.width;

    targetContentOffset->x = currentOffset;
    [scrollView setContentOffset:CGPointMake(newTargetOffset, 0) animated:YES];
}

Ответ 3

Этот ответ довольно поздний, но я только что играл с этой проблемой и обнаружил, что причиной дрейфа является межстрочный интервал. Если вы хотите, чтобы UICollectionView/FlowLayout на странице с точным кратным ширины ваших ячеек, вы должны установить:

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
flowLayout.minimumLineSpacing = 0.0;

Вы бы не подумали, что интервал строк вступает в игру в горизонтальной прокрутке, но, по-видимому, он это делает.

В моем случае я экспериментировал с подкачкой слева направо, по одной ячейке за раз, без пробелов между ячейками. Каждый поворот страницы вносил крошечный бит дрейфа из желаемой позиции, и он, казалось, накапливался линейно. ~ 10.0 очков за ход. Я понял, что 10.0 - это значение по умолчанию minimumLineSpacing в макете потока. Когда я устанавливаю его на 0.0, никакого дрейфа, когда я устанавливаю его на половину ширины границ, каждая страница дрейфует дополнительную половину границ.

Изменение minimumInteritemSpacing не имело эффекта.

edit - из документации для UICollectionViewFlowLayout:

@property (неатомный) CGFloat minimumLineSpacing;

Обсуждение

...

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

Значение по умолчанию для этого свойства - 10.0.

Ответ 4

enter image description here

Решение из следующей статьи является элегантным и простым. Основная идея заключается в создании scrollView поверх вашей коллекцииView с передачей всех значений contentOffset.

http://b2cloud.com.au/tutorial/uiscrollview-paging-size/

С помощью этого метода следует сказать:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;

Я не добился гладкой анимации, как это происходит с pagingEnabled = YES.

Ответ 5

Я знаю, что этот вопрос старый, но для тех, кто случайно наткнется на это.

Все, что вам нужно сделать, чтобы исправить это, - установить MinimumInterItemSpacing в 0 и уменьшить кадр содержимого.

Ответ 6

Думаю, я понимаю проблему. Я попытаюсь заставить вас понять это.

Если вы внимательно присмотритесь, вы увидите, что этот вопрос происходит только постепенно, а не только при прокрутке первой страницы.

enter image description here

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

Вместо этого вы должны сделать элемент UICollectionView, который равен 1/3 ширины всего представления, а затем добавить в него это закругленное изображение. Чтобы ссылаться на изображение, зеленый цвет должен быть вашим UICollectionViewItem, а не черным.

Ответ 7

@devdavid находилось в точке flowLayout.minimumLineSpacing до нуля.

Это также можно сделать в редакторе макетов, установив для параметра Min Spacing for Lines значение 0:

Простой способ

Ответ 8

Вы откатываете свой собственный UICollectionViewFlowLayout?

Если это так, добавление -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity поможет вам вычислить, где должна быть остановлена ​​прокрутка.

Это может работать (NB: UNTESTED!):

-(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
                             withScrollingVelocity:(CGPoint)velocity
{
  CGFloat offsetAdjustment = MAXFLOAT;
  CGFloat targetX = proposedContentOffset.x + self.minimumInteritemSpacing + self.sectionInset.left;

  CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);

  NSArray *array = [super layoutAttributesForElementsInRect:targetRect];
  for(UICollectionViewLayoutAttributes *layoutAttributes in array) {

    if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
      CGFloat itemX = layoutAttributes.frame.origin.x;

      if (ABS(itemX - targetX) < ABS(offsetAdjustment)) {
        offsetAdjustment = itemX - targetX;
      }
    }
  }

  return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

Ответ 9

Мой ответ основан на ответе fooobar.com/questions/102737/..., но более прост.

enter image description here

Вы должны поместить UIScrollView выше UICollectionView и дать им равные размеры:

@property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;

Затем настройте contentInset вида коллекции, например:

CGFloat inset = self.view.bounds.size.width*2/9;
self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);

И contentSize прокрутки:

self.scrollView.contentSize = CGSizeMake(self.placesCollectionView.bounds.size.width*[self.collectionView numberOfItemsInSection:0],0);

Не забудьте установить делегат прокрутки:

self.scrollView.delegate = self;

И реализуем главную магию:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.scrollView) {
        CGFloat inset = self.view.bounds.size.width*2/9;
        CGFloat scale = (self.placesCollectionView.bounds.size.width-2*inset)/scrollView.bounds.size.width;
        self.collectionView.contentOffset = CGPointMake(scrollView.contentOffset.x*scale - inset, 0);
    }
}

Ответ 10

Ваша ширина UICollectionView должна быть точным умножением ширины ячейки + левой и правой вставками. В вашем примере, если ширина ячейки равна 96, ширина UICollectionView должна быть (96 + 5 + 5) * 3 = 318. Или, если вы хотите сохранить ширину UICollectionView 320, ваша ширина ячейки должна быть 320 / 3 - 5 - 5 = 96.666.

Если это не помогает, ваша ширина UICollectionView может отличаться от той, что установлена ​​в файле xib, когда приложение запускается. Чтобы проверить это - добавьте оператор NSLog для распечатки размера представления во время выполнения:

NSLog(@"%@", NSStringFromCGRect(uiContentViewController.view.frame));

Ответ 11

Это та самая проблема, с которой я столкнулся, и я отправил свое решение на другой пост, поэтому я отправлю его снова здесь.

Я нашел решение для него, и он включал подклассификацию UICollectionViewFlowLayout.

Мой размер CollectionViewCell равен 302 X 457, и я установил минимальное расстояние между строками до 18 (9pix для каждой ячейки)

Когда вы выходите из этого класса, существует несколько методов, которые необходимо переопределить. Один из них -

  • (CGSize) collectionViewContentSize

В этом методе мне нужно было добавить общую ширину того, что было в UICollectionView. Это включает в себя ([datasource count] * widthOfCollectionViewCell) + ([источник данных] * 18)

Вот мои пользовательские методы UICollectionViewFlowLayout....

-(id)init
{
    if((self = [super init])){

       self.itemSize = CGSizeMake(302, 457);
       self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
       self.minimumInteritemSpacing = 0.0f;
       self.minimumLineSpacing = 18.0f;
       [self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
   }
    return self;
}



-(CGSize)collectionViewContentSize{
   return CGSizeMake((numCellsCount * 302)+(numCellsCount * 18), 457);
}

Это сработало для меня, поэтому я надеюсь, что кто-то найдет это полезным!

Ответ 12

вы также можете установить ширину отображения на "320 + интервал", а затем установить разрешение страницы на "да". он будет прокручивать "320 + интервал" для каждого раза. Я думаю, это потому, что включение страницы будет прокручивать ширину просмотра, но не ширину экрана.

Ответ 13

Я думаю, что у меня есть решение этой проблемы. Но я не знаю, если это будет лучше.

UICollectionViewFlowLayout содержит свойство, называемое sectionInset. Таким образом, вы можете установить раздел "Вставка" в соответствии с вашими потребностями и сделать 1 страницу равной одной секции. Поэтому ваша прокрутка должна автоматически помещаться на страницах (= разделы)

Ответ 14

У меня была аналогичная проблема с пейджингом. Несмотря на то, что вставки ячейки были равны 0, а ячейка была точно такого же размера по ширине и высоте, что и UICollectionView, пейджинг был неправильным.

То, что я заметил, звучит как ошибка в этой версии (UICollectionView, iOS 6): я мог видеть, что если бы я работал с UICollectionView с width = 310px или выше, а height = 538px, я был в беда. Однако, если бы я уменьшил ширину до, скажем, 300px (той же высоты), я получил все, что работает отлично!

Для некоторых будущих ссылок я надеюсь, что это поможет!

Ответ 15

Я столкнулся с подобной проблемой при попытке получить горизонтальный пейджинг, работающий на сетке 3 x 3 ячеек с вставками секций и расстоянием между ячейками и линиями.

Ответ для меня (после многих предложений, включая подклассы UICollectionViewFlowLayout и различные решения делегатов UIScrollView) был прост. Я просто использовал разделы в UICollectionView, разбив мой набор данных на разделы из 9 элементов (или меньше) и используя методы данных datasource numberOfSectionsInCollectionView и numberOfItemsInSection UICollectionView.

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

Ответ 16

Если вы используете раскладку по умолчанию для своего UICollectionView и НЕ хотите места между каждой ячейкой, вы можете установить для свойства miniumumLineSpacing значение 0 через:

((UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout).minimumLineSpacing = 0;