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

Xcode CollectionViewController scrollToItemAtIndexPath не работает

Я создал элемент управления CollectionView и заполнил его изображениями. Теперь я хочу прокручивать элемент по определенному индексу при запуске. Я опробовал scrollToItemAtIndexPath следующим образом:

[self.myFullScreenCollectionView scrollToItemAtIndexPath:indexPath 
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

Однако я получаю следующее исключение. Может ли кто-нибудь направить меня туда, где я ошибаюсь.

2013-02-20 02:32:45.219 ControlViewCollection1[1727:c07] *** Assertion failure in 
-[UICollectionViewData layoutAttributesForItemAtIndexPath:], /SourceCache/UIKit_Sim/UIKit-2380.17
/UICollectionViewData.m:485 2013-02-20 02:32:45.221 ControlViewCollection1[1727:c07] must return a 
UICollectionViewLayoutAttributes instance from -layoutAttributesForItemAtIndexPath: for path 
<NSIndexPath 0x800abe0> 2 indexes [0, 4]
4b9b3361

Ответ 1

Является ли это ошибкой или функцией, UIKit выдает эту ошибку всякий раз, когда вызывается scrollToItemAtIndexPath:atScrollPosition:Animated до того, как UICollectionView выложил ее в виде subviews.

Как обходной путь, переместите свой вызов прокрутки в место в жизненном цикле контроллера представления, где вы уверены, что он уже вычислил его макет, например:

@implementation CollectionViewControllerSubclass

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // scrolling here doesn't work (results in your assertion failure)
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    NSIndexPath *indexPath = // compute some index path

    // scrolling here does work
    [self.collectionView scrollToItemAtIndexPath:indexPath
                                atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                        animated:YES];
}

@end

По крайней мере, сообщение об ошибке должно быть более полезным. Я открыл rdar://13416281; прошу прощения.

Ответ 2

Если вы пытаетесь выполнить прокрутку, когда контроллер просмотра загружается, обязательно вызовите layoutIfNeeded в UICollectionView, прежде чем вы вызовете scrollToItemAtIndexPath. Это лучше, чем перенос логики прокрутки в viewDidLayoutSubviews, потому что вы не будете выполнять операцию прокрутки каждый раз, когда выкладываются представления в родительском представлении.

Ответ 3

U может сделать это и по методу viewDidLoad

просто сделайте вызов preformBatchUpdates

[self performBatchUpdates:^{
        if ([self.segmentedDelegate respondsToSelector:@selector(segmentedBar:selectedIndex:)]){
            [self.segmentedDelegate segmentedBar:self selectedIndex:_selectedPage];
        }
    } completion:^(BOOL finished) {
        if (finished){
            [self scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedPage inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
        }

    }];

В моем случае у моего подкласса CollectionView есть свойство selectedPage, а на установщике этого свойства я вызываю

- (void)setSelectedPage:(NSInteger)selectedPage {
    _selectedPage = selectedPage;
    [self performBatchUpdates:^{
        if ([self.segmentedDelegate respondsToSelector:@selector(segmentedBar:selectedIndex:)]){
            [self.segmentedDelegate segmentedBar:self selectedIndex:_selectedPage];
        }
    } completion:^(BOOL finished) {
        if (finished){
            [self scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedPage inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
        }

    }];
}

В контроллере, который я вызываю это кодом

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationBar.segmentedPages.selectedPage = 1;
}

Ответ 4

Добавление логики прокрутки к viewDidAppear работало для меня:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.collectionView?.scrollToItemAtIndexPath(
        someIndexPath,
        atScrollPosition: UICollectionViewScrollPosition.None,
        animated: animated)
}

Добавление его в viewDidLoad не работает: оно игнорируется.

Добавление его в viewDidLayoutSubviews не работает, если вы не хотите прокручивать логику, вызывая любое изменение. В моем случае он не позволял пользователю вручную прокручивать элемент

Ответ 5

Также помните, что вам нужно использовать правильное значение UICollectionviewScrollPosition. Пожалуйста, ознакомьтесь с кодом ниже:

typedef NS_OPTIONS(NSUInteger, UICollectionViewScrollPosition) {
UICollectionViewScrollPositionNone                 = 0,

/* For a view with vertical scrolling */
// The vertical positions are mutually exclusive to each other, but are bitwise or-able with the horizontal scroll positions.
// Combining positions from the same grouping (horizontal or vertical) will result in an NSInvalidArgumentException.
UICollectionViewScrollPositionTop                  = 1 << 0,
UICollectionViewScrollPositionCenteredVertically   = 1 << 1,
UICollectionViewScrollPositionBottom               = 1 << 2,

/* For a view with horizontal scrolling */
// Likewise, the horizontal positions are mutually exclusive to each other.
UICollectionViewScrollPositionLeft                 = 1 << 3,
UICollectionViewScrollPositionCenteredHorizontally = 1 << 4,
UICollectionViewScrollPositionRight                = 1 << 5

};

Ответ 6

Начиная с iOS 9.3, Xcode 8.2.1, Swift 3:

Вызов scrollToItem(at:) из viewWillAppear() по-прежнему прерывается, особенно если вы используете верхние и нижние колонтитулы разделов, автоматический макет, вставки разделов.

Даже если вы вызываете setNeedsLayout() и layoutIfNeeded() в collectionView, поведение по-прежнему встречается. Включение кода прокрутки в блок анимации не работает надежно.

Как указано в других ответах, решение состоит в том, чтобы вызвать scrollToItem(at:) только после того, как вы уверены, что все было выложено. т.е. в viewDidLayoutSubviews().

Однако вам нужно быть избирательным; вы не хотите выполнять прокрутку каждый раз, когда вызывается viewWillLayoutSubviews(). Таким образом, решение состоит в том, чтобы установить флаг в viewWillAppear() и действовать на нем в viewDidLayoutSubviews().

то есть.

fileprivate var needsDelayedScrolling = false

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)
    self.needsDelayedScrolling = true
    // ...
}

override func viewDidLayoutSubviews()
{
    super.viewDidLayoutSubviews()

    if self.needsDelayedScrolling {
        self.needsDelayedScrolling = false
        self.collectionView!.scrollToItem(at: someIndexPath,
                at: .centeredVertically,
                animated: false)
        }
    }
}

Ответ 7

Swift 3

UIView.animate(withDuration: 0.4, animations: {
    myCollectionview.scrollToItem(at: myIndexPath, at: .centeredHorizontally, animated: false)
}, completion: {
    (value: Bool) in
    // put your completion stuff here
})

Ответ 8

Иногда collectionView(_:didSelectItemAt:) либо не вызывается в основном потоке, либо блокирует его, вызывая scrollToItem(at:at:animated:) ничего не делать.

Обходите это, делая:

DispatchQueue.main.async {
  collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}

Ответ 9

Это основано на ответе @Womble, все кредиты идут им:

Метод viewDidLayoutSubviews() вызывается повторно. Для меня (iOS 11.2) при первом вызове collectionView.contentSize есть {0,0}. Во второй раз contentSize верен. Поэтому мне пришлось добавить чек для этого:

var needsDelayedScrolling = false

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.needsDelayedScrolling = true
    // ...
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if self.needsDelayedScrolling && collectionView.contentSize.width > 0 {
        self.needsDelayedScrolling = false
        self.collectionView!.scrollToItem(at: someIndexPath,
                at: .centeredVertically,
                animated: false)
        }
    }
}

После добавления этого дополнительного && collectionView.contentSize.width > 0 он работает красиво.