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
xcode CollectionViewController scrollToItemAtIndexPath not working
提问by Karthik Pai
I have created a CollectionView
Control and filled it with images. Now I want to scroll to item at a particular index on start. I have tried out scrollToItemAtIndexPath
as 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:Animated
is called before UICollectionView
has laid out its subviews.
无论是错误还是功能,UIKitscrollToItemAtIndexPath:atScrollPosition:Animated
在UICollectionView
布局其子视图之前调用时都会抛出此错误 。
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 layoutIfNeeded
on the UICollectionView
before 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.
如果您正试图滚动当视图控制器加载,请确保调用layoutIfNeeded
在UICollectionView
你打电话之前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 viewDidAppear
worked for me:
添加滚动逻辑viewDidAppear
对我有用:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.collectionView?.scrollToItemAtIndexPath(
someIndexPath,
atScrollPosition: UICollectionViewScrollPosition.None,
animated: animated)
}
Adding it to viewDidLoad
doesn't work: it gets ignored.
将它添加到viewDidLoad
不起作用:它被忽略。
Adding it to viewDidLayoutSubviews
doesn'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.contentSize
is {0,0}
. The second time, the contentSize
is correct. Therefore, I had to add a check for this:
该方法viewDidLayoutSubviews()
被重复调用。对我(iOS 11.2)来说,它第一次被称为collectionView.contentSize
is {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 > 0
it works beautifully.
添加该额外&& collectionView.contentSize.width > 0
内容后,它的效果非常好。