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

UICollectionView: просмотр подкачки как вкладки Safari или поиск в App Store

Я хочу реализовать "карты" в своем приложении, например, вкладки Safari или поиск в App Store.

Я покажу пользователю одну карту в центре экрана и в части предыдущей и следующей карт с левой и правой сторон. (См. Например, "Поиск в App Store" или вкладки Safari)

Я решил использовать UICollectionView, и мне нужно изменить размер страницы (не нашел, как) или реализовать собственный подкласс макета (не знаете как)?

Любая помощь, пожалуйста?

4b9b3361

Ответ 1

Ниже приведен самый простой способ, который я нашел, чтобы получить этот эффект. Он включает в себя просмотр коллекции и дополнительный секретный вид прокрутки.

Настройте представления коллекции

  • Настройте представление коллекции и все его методы источника данных.
  • Рамка просмотра коллекции; он должен охватывать всю ширину, которую вы хотите видеть.
  • Установите представление коллекции contentInset:

    _collectionView.contentInset = UIEdgeInsetsMake(0, (self.view.frame.size.width-pageSize)/2, 0, (self.view.frame.size.width-pageSize)/2);
    

Это помогает правильно компенсировать ячейки.

Настройте свой секретный прокрутка

  • Создайте прокрутку, поместите ее куда угодно. Вы можете установить его hidden, если хотите.
  • Задайте размер границ scrollview для желаемого размера вашей страницы.
  • Установите себя как делегат прокрутки.
  • Задайте свой contentSize размер ожидаемого содержимого вашего вида коллекции.

Переместить распознаватель жестов

  • Добавьте секретный признак жестов scrollview в представление коллекции и отключите распознаватель жестов вида коллекции:

    [_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer];
    _collectionView.panGestureRecognizer.enabled = NO;
    

делегат

- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
    CGPoint contentOffset = scrollView.contentOffset;
    contentOffset.x = contentOffset.x - _collectionView.contentInset.left;
    _collectionView.contentOffset = contentOffset;
}

По мере перемещения scrollview получите его смещение и установите его на смещение вида коллекции.

Сообщаю об этом здесь, поэтому просмотрите эту ссылку для обновлений: http://khanlou.com/2013/04/paging-a-overflowing-collection-view/

Ответ 2

Вы можете подклассифицировать UICollectionViewFlowLayout и переопределить так:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)contentOffset 
                                 withScrollingVelocity:(CGPoint)velocity
{
    NSArray* layoutAttributesArray = 
   [self layoutAttributesForElementsInRect:self.collectionView.bounds];

    if(layoutAttributesArray.count == 0)
        return contentOffset;


    UICollectionViewLayoutAttributes* candidate = 
    layoutAttributesArray.firstObject;

    for (UICollectionViewLayoutAttributes* layoutAttributes in layoutAttributesArray)
    {
        if (layoutAttributes.representedElementCategory != UICollectionElementCategoryCell)
            continue;


        if((velocity.x > 0.0 && layoutAttributes.center.x > candidate.center.x) ||
           (velocity.x <= 0.0 && layoutAttributes.center.x < candidate.center.x))
            candidate = layoutAttributes;


    }

    return CGPointMake(candidate.center.x - self.collectionView.bounds.size.width * 0.5f, contentOffset.y);
}

Это приведет к следующей или предыдущей ячейке в зависимости от скорости... однако она не будет привязываться к текущей ячейке.

Ответ 3

@Mike M answer в Swift...

class CenteringFlowLayout: UICollectionViewFlowLayout {

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

        guard let collectionView = collectionView,
            let layoutAttributesArray = layoutAttributesForElementsInRect(collectionView.bounds),
            var candidate = layoutAttributesArray.first else { return proposedContentOffset }

        layoutAttributesArray.filter({$0.representedElementCategory == .Cell }).forEach { layoutAttributes in

            if (velocity.x > 0 && layoutAttributes.center.x > candidate.center.x) ||
                (velocity.x <= 0 && layoutAttributes.center.x < candidate.center.x) {
                candidate = layoutAttributes
            }
        }

        return CGPoint(x: candidate.center.x - collectionView.bounds.width / 2, y: proposedContentOffset.y)
    }

}

Ответ 4

Немного отредактируйте ответ Соруша, который помогло. Единственное редактирование, которое я сделал вместо отключения жестов:

[_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer];
_collectionView.panGestureRecognizer.enabled = NO;

Я отключил прокрутку в коллекции:

_collectionView.scrollEnabled = NO;

Как запрет жестов также отключил секретный жестов scrollview.

Ответ 5

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

Я попытался реализовать решение Soroush, но это не работает для меня.

Поскольку UICollectionView является подклассом UIScrollView, он будет реагировать на важный метод UIScrollViewDelegate, который:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset

targetContentOffset (inout pointer) позволяет вам переопределить точку остановки для вида коллекции (x в этом случае, если вы проведите по горизонтали).

Быстрая заметка о нескольких переменных, найденных ниже:

  • self.cellWidth - это ваша ширина ячейки коллекции (вы можете даже жестко указать ее там, если хотите)

  • self.minimumLineSpacing - это минимальное расстояние между строками между ячейками

  • self.scrollingObjects - это массив объектов, содержащихся в представлении коллекции (мне это нужно в основном для подсчета, чтобы узнать, когда прекратить прокрутку)

Итак, идея состоит в том, чтобы реализовать этот метод в делегате представления коллекции, например:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
{       
    if (self.currentIndex == 0 && velocity.x < 0) {
        // we have reached the first user and we're trying to go back
        return;
    }

    if (self.currentIndex == (self.scrollingObjects.count - 1) && velocity.x > 0) {
        // we have reached the last user and we're trying to go forward
        return;
    }

    if (velocity.x < 0) {
        // swiping from left to right (going left; going back)
        self.currentIndex--;
    } else {
        // swiping from right to left (going right; going forward)
        self.currentIndex++;
    }

    float xPositionToStop = 0;

    if (self.currentIndex == 0) {
        // first row
        xPositionToStop = 0;
    } else {
        // one of the next ones
        xPositionToStop = self.currentIndex * self.cellWidth + (self.currentIndex + 1) * self.minimumLineSpacing - ((scrollView.bounds.size.width - 2*self.minimumLineSpacing - self.cellWidth)/2);
    }

    targetContentOffset->x = xPositionToStop;

    NSLog(@"Point of stopping: %@", NSStringFromCGPoint(*targetContentOffset));
}

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

Ответ 6

Предыдущее довольно сложно, UICollectionView является подклассом UIScrollView, поэтому просто выполните следующее:

[self.collectionView setPagingEnabled:YES];

Вы все готовы пойти.

Смотрите подробное руководство.