objective-c 调用 setCollectionViewLayout:animated 不会重新加载 UICollectionView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22475985/
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
Calling setCollectionViewLayout:animated does not reload UICollectionView
提问by Robert J. Clegg
I am using a UICollectionView with two custom layouts - which are subclassed from UICollectionViewFlowLayout
我正在使用带有两个自定义布局的 UICollectionView - 它们是从 UICollectionViewFlowLayout
When my view first loads I set it to a default view using:
当我的视图第一次加载时,我使用以下方法将其设置为默认视图:
[self.collectionView setCollectionViewLayout:self.tableViewLayout];
This calls: cellForItemAtIndexPath- so no problem there. This is in viewWillAppearmethod.
这称为:cellForItemAtIndexPath- 所以没问题。这是在viewWillAppear方法中。
Then I use a UIButton to change the layout from current view to the next layout like so:
然后我使用 UIButton 将布局从当前视图更改为下一个布局,如下所示:
-(void)changeViewLayoutButtonPressed
{
self.changeLayout = !self.changeLayout;
if (self.changeLayout){
[self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];
}
else {
[self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];
}
}
After this call - numberOfItemsInSectionis called. I have checked in the debugger that the return account is not zero.
在此调用之后 -numberOfItemsInSection被调用。我已经在调试器中检查过返回帐户不为零。
After that call, nothing else is called and my UICollectionView changes the layout but since cellForItemAtIndexPathis not called - the cells are not setup correctly.
在那次调用之后,没有调用其他任何东西,我的 UICollectionView 更改了布局,但由于cellForItemAtIndexPath没有调用 - 单元格设置不正确。
if I put [self.collectionView roloadData]inside of numberOfItemsInSection- then cellForItemAtIndexPathis called and cells are setup.
如果我把[self.collectionView roloadData]里面的numberOfItemsInSection-那么cellForItemAtIndexPath被称为和细胞设置。
I shouldn't need to do a manual call to reload the data in numberOfItemsInSection. Can someone tell me whats going on?
我不需要手动调用来重新加载numberOfItemsInSection. 有人可以告诉我这是怎么回事吗?
Edit
编辑
I forgot to mention I am using two different UINibs for the two different cells as they need slightly different layouts applied to them. Would this be an issue? Thanks!
我忘了提到我对两个不同的单元使用了两个不同的 UINib,因为它们需要应用到它们的布局略有不同。这会是个问题吗?谢谢!
Edit 2
编辑 2
Right, so I have gotten it to work almost like I want it to. Here is all the code used for the collection view and how I change its layouts.
是的,所以我让它几乎像我想要的那样工作。这是用于集合视图的所有代码以及我如何更改其布局。
The Custom Layouts
自定义布局
These are subclassed from UICollectionViewFlowLayout- The reason I subclassed is simply due to the face my UICollectionViewneeds to look and behave very close to Apple's pre-made layout. Just different cell sizes, though.
这些是从子类化的UICollectionViewFlowLayout- 我子类化的原因仅仅是因为我UICollectionView需要外观和行为非常接近 Apple 的预制布局。不过,只是不同的单元格大小。
TableViewLayout- No code in the header file. - BBTradeFeedTableViewLayout.m
TableViewLayout- 头文件中没有代码。 - BBTradeFeedTableViewLayout.m
@implementation BBTradeFeedTableViewLayout
-(id)init
{
self = [super init];
if (self){
self.itemSize = CGSizeMake(320, 80);
self.minimumLineSpacing = 0.1f;
}
return self;
}
@end
Gride Layout- No code in the header file. - BBTradeFeedGridViewLayout.m
网格布局- 头文件中没有代码。 - BBTradeFeedGridViewLayout.m
@implementation BBTradeFeedGridViewLayout
-(id)init
{
self = [super init];
if (self){
self.itemSize = CGSizeMake(159, 200);
self.minimumInteritemSpacing = 0.1f;
self.minimumLineSpacing = 0.1f;
}
return self;
}
@end
Then in the ViewControllerthat has my UICollectionView- I declare the two custom layouts like so:
然后在ViewController我的UICollectionView- 我声明两个自定义布局,如下所示:
@property (strong, nonatomic) BBTradeFeedTableViewLayout *tableViewLayout;
@property (strong, nonatomic) BBTradeFeedGridViewLayout *grideLayout;
I then register the two .xib files for the collectionView:
然后我为 collectionView 注册两个 .xib 文件:
[self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:@"BBItemTableViewCell" bundle:nil] forCellWithReuseIdentifier:@"TableItemCell"];
[self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:@"BBItemGridViewCell" bundle:nil] forCellWithReuseIdentifier:@"GridItemCell"];
Then, I have a UIButton that changes the layout:
然后,我有一个改变布局的 UIButton:
-(void)changeViewLayoutButtonPressed
{
if (!self.gridLayoutActive){
self.gridLayoutActive = YES;
[self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];
self.lastLayoutUsed = @"GridLayout";
}
else {
self.gridLayoutActive = NO;
[self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];
self.lastLayoutUsed = @"TableLayOut";
}
[self.tradeFeedCollectionView reloadItemsAtIndexPaths:[self.tradeFeedCollectionView indexPathsForVisibleItems]];
}
Finally - when a web service call is done - it calls [self.tradeFeedCollectionView reloadData];
最后 - 当 Web 服务调用完成时 - 它调用 [self.tradeFeedCollectionView reloadData];
So there my UICollectionViewgets reloaded and the last method:
所以我UICollectionView被重新加载和最后一个方法:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *tableCellIdentifier = @"TableItemCell";
static NSString *gridCellIdentifier = @"GridItemCell";
if (indexPath.item < [self.tradeSearchArray count]){
if (self.gridLayoutActive == NO){
BBItemTableViewCell *tableItemCell = [collectionView dequeueReusableCellWithReuseIdentifier:tableCellIdentifier forIndexPath:indexPath];
if ([self.tradeSearchArray count] > 0){
self.toolBarButtomItem.title = [NSString stringWithFormat:@"%d Results", self.searchResult.searchResults];
tableItemCell.gridView = NO;
tableItemCell.backgroundColor = [UIColor whiteColor];
tableItemCell.item = self.tradeSearchArray[indexPath.row];
}
return tableItemCell;
}else
{
BBItemTableViewCell *gridItemCell= [collectionView dequeueReusableCellWithReuseIdentifier:gridCellIdentifier forIndexPath:indexPath];
if ([self.tradeSearchArray count] > 0){
self.toolBarButtomItem.title = [NSString stringWithFormat:@"%d Results", self.searchResult.searchResults];
gridItemCell.gridView = YES;
gridItemCell.backgroundColor = [UIColor whiteColor];
gridItemCell.item = self.tradeSearchArray[indexPath.row];
}
return gridItemCell;
}
The problem with the above code
上面代码的问题
The above code does work. However, the cells don't animate nicely and it looks odd, almost as if a refreshed happened. Which is due to calling:
上面的代码确实有效。但是,这些单元格的动画效果不佳,看起来很奇怪,几乎就像刷新了一样。这是由于调用:
[self.tradeFeedCollectionView reloadItemsAtIndexPaths:[self.tradeFeedCollectionView indexPathsForVisibleItems]];
[self.tradeFeedCollectionView reloadItemsAtIndexPaths:[self.tradeFeedCollectionView indexPathsForVisibleItems]];
But that is the only way I could get it to be close to what I want.
但这是我让它接近我想要的唯一方法。
回答by liuyaodong
-setCollectionViewLayout:or -setCollectionViewLayout:animated:won't cause your UICollectionViewreload its data.
-setCollectionViewLayout:或-setCollectionViewLayout:animated:不会导致您UICollectionView重新加载其数据。
If you want to change your cell style or update the data, call
[self.collectionView reloadData]andUICollectionViewDataSourceprotocol methods will be called.If you want to change your
UICollectionViewlayout to another, call-setCollectionViewLayout:. Or if you want to update yourUICollectionViewlayout, just call[self.collectionView.collectionViewLayout invalidateLayout].
如果要更改单元格样式或更新数据,将调用 call
[self.collectionView reloadData]和UICollectionViewDataSourceprotocol 方法。如果要将
UICollectionView布局更改为其他布局,请调用-setCollectionViewLayout:。或者,如果您想更新您的UICollectionView布局,只需调用[self.collectionView.collectionViewLayout invalidateLayout].
Cell layout and data are two different things in UICollectionView.
单元格布局和数据在UICollectionView.
Update
you should discard all the stale data, probably by -reloadData. And calculate the new frames for every cell in your UICollectionViewLayout subclasses. Override - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPathand - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect.
You decide the new contentOffset. For instance, you could keep the indexPath of some visible cell and decide to scroll to that indexPathor setContentOffset:to the frame.originof the new cell at the same indexPathafter your layout changed.
更新
您应该丢弃所有陈旧数据,可能由-reloadData. 并计算 UICollectionViewLayout 子类中每个单元格的新框架。覆盖- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath和- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect。
您决定新的contentOffset. 举例来说,你可以保留一些可见单元的indexPath并决定滚动到indexPath或setContentOffset:在frame.origin同一新小区indexPath后,你的布局改变。
- And I find this answermay give you help.
- Also check the awesome video WWDC 2012 Advanced Collection Views and Building Custom Layouts
- 我发现这个答案可能会给你帮助。
- 另请查看精彩视频WWDC 2012 Advanced Collection Views and Building Custom Layouts
回答by Artheyn
Simply call reloadDatabeforeyou switch your layout (the performBatchUpdatescall is not mandatory):
只需reloadData在切换布局之前performBatchUpdates调用(调用不是强制性的):
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:yourLayout animated:YES];
In Swift:
在斯威夫特:
self.collectionView?.reloadData()
self.collectionView?.collectionViewLayout.invalidateLayout()
self.collectionView?.setCollectionViewLayout(yourLayout, animated: true)
回答by Lolloz89
You should call [self.tradeFeedCollectionView invalidateLayout];before
你应该叫[self.tradeFeedCollectionView invalidateLayout];前
if (self.changeLayout){
[self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];
}
else {
[self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];
}
回答by Michael Rose
You should only have to call this method:
你应该只需要调用这个方法:
[self.collectionView setCollectionViewLayout:layout animated:YES];
I had a similar bug, but it ended up that my subviews of the cells were not setup with Auto Layout. The cell was resizing correctly, but my subviews were not, so it looked like the cell size never changed. Check this in your code quickly by setting clipsToBounds = YES on your cell.contentView.
我有一个类似的错误,但最终我的单元格子视图没有设置自动布局。单元格正确调整大小,但我的子视图没有,所以看起来单元格大小从未改变。通过在 cell.contentView 上设置 clipsToBounds = YES 来快速检查您的代码。
回答by Shivam Singh
Here is the simple trick if you want to update layouts Use it wherever you are updating the collectionView:
如果您想更新布局,这是一个简单的技巧,无论您在哪里更新 collectionView,都可以使用它:
[self.collectionView removeFromSuperview]; //First remove the Collection View
//Relocate the view with layouts
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumInteritemSpacing = 10;
layout.minimumLineSpacing = 10;
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.width,100) collectionViewLayout:layout];
[self.collectionView setDataSource:self];
[self.collectionView setDelegate:self];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[self.collectionView reloadData];
[self.view addSubview:self.collectionView];
回答by halfer
(Posted solution on behalf of the question author).
(代表问题作者发布解决方案)。
I finally found a nice and simple solution.
我终于找到了一个漂亮而简单的解决方案。
The marked answer was correct, that I needed to call, -reloadDataon my UICollectionView- However, this destroyed the animation when switching between the two cells.
标记的答案是正确的,我需要调用,-reloadData在我的UICollectionView- 但是,这在两个单元格之间切换时破坏了动画。
After taking in suggestions from the marked answer - I just reworked my `UIButton' method, that changes the cells
从标记的答案中获得建议后 - 我刚刚重新设计了我的“UIButton”方法,它改变了单元格
I found that if I call:
我发现如果我打电话:
[self.collectionview performBatchUpdates:^{ } completion:^(BOOL finished) {
}];
According to the WDDC video - this animates the changes works with -reloadDatamethod:
根据 WDDC 视频 - 这使更改工作的动画-reloadData方法为:
Example:
例子:
[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^{
[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:self.grideLayout animated:YES];
} completion:^(BOOL finished) {
}];
}
回答by Rivera
I found that to easily and nicely animate both cells' kind and layout simultaneously it is best to reloadSections(instead of reloadData) followed by setting a new layout with animation:
我发现要同时轻松地为两个单元格的种类和布局设置动画,最好reloadSections(而不是reloadData)然后设置带有动画的新布局:
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
[self.collectionView setCollectionViewLayout:newLayout animated:animated];

