xcode CollectionViewController scrollToItemAtIndexPath 不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14977896/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 04:44:24  来源:igfitidea点击:

xcode CollectionViewController scrollToItemAtIndexPath not working

xcode

提问by Karthik Pai

I have created a CollectionViewControl and filled it with images. Now I want to scroll to item at a particular index on start. I have tried out scrollToItemAtIndexPathas follows:

我创建了一个CollectionView控件并用图像填充它。现在我想在开始时滚动到特定索引处的项目。我试过scrollToItemAtIndexPath如下:

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

However, I am getting following exception. Could anyone guide me on where am I going wrong.

但是,我收到以下异常。谁能指导我我哪里出错了。

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]

回答by followben

Whether it's a bug or a feature, UIKit throws this error whenever scrollToItemAtIndexPath:atScrollPosition:Animatedis called before UICollectionViewhas laid out its subviews.

无论是错误还是功能,UIKitscrollToItemAtIndexPath:atScrollPosition:AnimatedUICollectionView布局其子视图之前调用时都会抛出此错误 。

As a workaround, move your scrolling invocation to a place in the view controller lifecycle where you're sure it has already computed its layout, like so:

作为一种解决方法,将您的滚动调用移动到视图控制器生命周期中您确定它已经计算其布局的位置,如下所示:

@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

At the very least, the error message should probably be more helpful. I've opened a rdar://13416281; please dupe.

至少,错误消息应该更有帮助。我打开了一个rdar://13416281;请欺骗。

回答by Code Commander

If you are trying to scroll when the view controller is loading, make sure to call layoutIfNeededon the UICollectionViewbefore you call scrollToItemAtIndexPath. This is better than putting the scroll logic in viewDidLayoutSubviews because you won't perform the scroll operation every time the parent view's subviews are laid out.

如果您正试图滚动当视图控制器加载,请确保调用layoutIfNeededUICollectionView你打电话之前scrollToItemAtIndexPath。这比将滚动逻辑放在 viewDidLayoutSubviews 中要好,因为您不会在每次布局父视图的子视图时都执行滚动操作。

回答by jwswart

Sometimes collectionView(_:didSelectItemAt:)is either not called on the main thread, or blocks it, causing scrollToItem(at:at:animated:)to not do anything.

有时collectionView(_:didSelectItemAt:)要么没有在主线程上调用,要么阻塞它,导致scrollToItem(at:at:animated:)什么都不做。

Work around this by doing:

通过执行以下操作来解决此问题:

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

回答by Dmitry Nelepov

U can do this and on viewDidLoad method

您可以在 viewDidLoad 方法上执行此操作

just make call preformBatchUpdates

只需拨打 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];
        }

    }];

In my case my subclass CollectionView has a property selectedPage, and on a setter of this property i call

在我的例子中,我的子类 CollectionView 有一个属性 selectedPage,并且在这个属性的 setter 上我调用

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

    }];
}

In view controller i calling this by code

在视图控制器中,我通过代码调用它

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

回答by Womble

As of iOS 9.3, Xcode 8.2.1, Swift 3:

从 iOS 9.3、Xcode 8.2.1、Swift 3 开始:

Calling scrollToItem(at:)from viewWillAppear()is still broken, particularly if you are using Section Headers/Footers, Auto Layout, Section Insets.

调用scrollToItem(at:)viewWillAppear()仍然是断开的,特别是如果你正在使用部分页眉/页脚,自动布局,科插图。

Even if you call setNeedsLayout()and layoutIfNeeded()on the collectionView, the behavior is still borked. Putting the scrolling code in to an animation block doesn't work reliably.

即使您在 上调用setNeedsLayout()和,该行为仍然很无聊。将滚动代码放入动画块并不可靠。layoutIfNeeded()collectionView

As indicated in the other answers, the solution is to only call scrollToItem(at:)once you are sure everything has been laid out. i.e. in viewDidLayoutSubviews().

如其他答案所示,解决方案是仅scrollToItem(at:)在您确定一切都已布置好后才致电。即在viewDidLayoutSubviews().

However, you need to be selective; you don't want to perform scrolling every time viewWillLayoutSubviews()is called. So a solution is to set a flag in viewWillAppear(), and act it on it in viewDidLayoutSubviews().

但是,您需要有选择性;您不想每次viewWillLayoutSubviews()调用时都执行滚动。因此,解决方案是在 中设置一个标志viewWillAppear(),然后在 中对其进行操作viewDidLayoutSubviews()

i.e.

IE

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)
        }
    }
}

回答by Alena

Also remember that you need to use proper UICollectionviewScrollPosition value. Please see code below for clarification:

还要记住,您需要使用正确的 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

};

};

回答by rghome

Adding the scrolling logic to viewDidAppearworked for me:

添加滚动逻辑viewDidAppear对我有用:

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

Adding it to viewDidLoaddoesn't work: it gets ignored.

将它添加到viewDidLoad不起作用:它被忽略。

Adding it to viewDidLayoutSubviewsdoesn't work unless you want to scroll logic calling any time anything changes. In my case, it prevented the user from manually scrolling the item

viewDidLayoutSubviews除非您想在任何更改时滚动逻辑调用,否则将其添加到不起作用。就我而言,它阻止了用户手动滚动项目

回答by Stijn Westerhof

Swift 3

斯威夫特 3

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

回答by bcattle

This is based on @Womble's answer, all credit goes to them:

这是基于@Womble 的回答,所有功劳都归功于他们:

The method viewDidLayoutSubviews()gets called repeatedly. For me (iOS 11.2) the first time it gets called the collectionView.contentSizeis {0,0}. The second time, the contentSizeis correct. Therefore, I had to add a check for this:

该方法viewDidLayoutSubviews()被重复调用。对我(iOS 11.2)来说,它第一次被称为collectionView.contentSizeis {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)
        }
    }
}

After adding that extra && collectionView.contentSize.width > 0it works beautifully.

添加该额外&& collectionView.contentSize.width > 0内容后,它的效果非常好。