删除UICollectionView边缘的单元格 - 滚动后不会立即显示单元格 [英] Deleting cell at edge of UICollectionView - cells not appearing immediately after scroll

查看:101
本文介绍了删除UICollectionView边缘的单元格 - 滚动后不会立即显示单元格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个标准的垂直滚动流布局,其中填充了足够的单元格以进行滚动。滚动到底部时,如果删除项目,使得集合视图的内容大小必须缩小以容纳新项目数(即删除底行上的最后一项),则从该列中滚动的单元格行顶部是隐藏的。在删除动画结束时,顶行显示没有动画 - 这是一个非常不愉快的效果。

Consider an standard, vertically scrolling flow layout populated with enough cells to cause scrolling. When scrolled to the bottom, if you delete an item such that the content size of the collection view must shrink to accommodate the new number of items (i.e. delete the last item on the bottom row), the row of cells that scroll in from the top are hidden. At the end of the deletion animation, the top row appears without animation - it's a very unpleasant effect.

慢动作:

它是很容易重现:


  1. 创建一个新的单一视图项目并更改默认的 ViewController UICollectionViewController的子类

添加 UICollectionViewController 到使用标准流布局的故事板,并将其类更改为 ViewController 。为单元格原型提供标识符Cell,大小为200x200。

Add a UICollectionViewController to the storyboard that uses a standard flow layout, and change its class to ViewController. Give the cell prototype the identifier "Cell" and a size of 200x200.

将以下代码添加到 ViewController.m


@interface ViewController ()
@property(nonatomic, assign) NSInteger numberOfItems;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.numberOfItems = 19;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.numberOfItems;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    self.numberOfItems--;
    [collectionView deleteItemsAtIndexPaths:@[indexPath]];
}

@end


我看过这个问题的其他表现形式处理集合视图,只是上面的例子似乎是最简单的证明问题。 UICollectionView 似乎在默认动画期间陷入某种瘫痪的恐慌状态,并且在动画完成之前拒绝取消隐藏某些单元格。它甚至可以阻止手动调用 cell.hidden = NO 隐藏的单元格产生效果(隐藏仍然 YES 之后)。如果您可以获取对要取消隐藏的单元格的引用,那么下拉到底层并设置 hidden 就可以了,这在处理避开的单元格时非常重要尚未显示。

I've seen other manifestations of this problem when dealing with collection views, it's just that the above example seems the simplest to demonstrate the issue. UICollectionView seems to go into some kind of paralysed state of panic during the default animations, and refuses to unhide certain cells until after the animation completes. It even prevents manual calls to cell.hidden = NO on hidden cells from having an effect (hidden is still YES afterwards). Dropping down to the underlying layer and setting hidden there works, provided you can get a reference to the cell you want to unhide, which is non-trivial when dealing with cells that haven't been displayed yet.

-initialLayoutAttributesForAppearingItemAtIndexPath 正在为调用时可见的每个项目调用 deleteItemsAtIndexPaths:,但不适用于滚动到视图中的那些。可以通过在批量更新块中立即调用 reloadData 来解决此问题,这似乎使集合视图意识到顶行将要出现:

-initialLayoutAttributesForAppearingItemAtIndexPath is being called for every item visible at the time of the call to deleteItemsAtIndexPaths:, but not for the ones that are scrolled into view. It is possible work around the issue by calling reloadData inside a batch update block immediately afterwards, which appears to make the collection view realise that the top row is about to appear:

[collectionView deleteItemsAtIndexPaths:@[indexPath]];
[collectionView performBatchUpdates:^{
    [collectionView reloadData];
} completion:nil];

但不幸的是,这对我来说不是一个选择。我试图通过操纵单元格层来实现一些自定义动画计时。动画,并调用 reloadData 通过导致不必要的布局回调真的抛出了一些东西。

But unfortunately this is not an option for me. I am trying to implement some custom animation timing by manipulating the cell layers & animations, and calling reloadData really throws things out of whack by causing unnecessary layout callbacks.


我添加了日志语句到很多布局方法,并浏览了一些堆栈框架,试图找出问题所在。至关重要的是,我正在检查何时调用 layoutSubviews ,当集合视图从布局对象请求布局属性时( layoutAttributesForElementsInRect:)以及在单元格上调用 applyLayoutAttributes:时。

I added log statements to a lot of layout methods and looked through some stack frames to try and find out what's going wrong. Crucially, I'm checking when layoutSubviews is called, when the collection view asks for layout attributes from the layout object (layoutAttributesForElementsInRect:) and when applyLayoutAttributes: is called on the cells.

我希望看到一系列这样的方法:

I would expect to see a sequence of methods like this:

// user taps cell (to delete it)
-deleteItemsAtIndexPaths:
-layoutAttributesForElementsInRect:
-finalLayoutAttributes...:                // Called for the item being deleted
-finalLayoutAttributes...:                // \__ Called for each index path visible
-initialLayoutAttributes...:              // /   when deletion started
-applyLayoutAttributes:                   // Called for the item being deleted, to apply final layout attributes
// collection view begins scrolling up
-layoutSubviews:                          // Called multiple times as the 
-layoutAttributesForElementsInRect:       // collection view scrolls
// ... for any new set of
// ... attributes returned:
-collectionView:cellForItemAtIndexPath:
-applyLayoutAttributes:                   // Sets the standard attributes for the new cell
// collection view finishes scrolling

大多数情况正在发生;在视图滚动时正确触发布局,并且集合视图正确地查询布局以查看要显示的单元格的属性。但是, collectionView:cellForItemAtIndexPath:以及相应的 applyLayoutAttributes:方法在删除之后才会被调用,当布局为最后一次调用导致为隐藏单元格分配其布局属性(设置 hidden = NO )。

Most of this is happening; layout is correctly triggered as the view scrolls, and the collection view properly queries the layout for the attributes of cells to be displayed. However, collectionView:cellForItemAtIndexPath: and the corresponding applyLayoutAttributes: methods are not being called until after the deletion, when layout is invoked one last time causing the hidden cells to be assigned their layout attributes (sets hidden = NO).

所以似乎尽管从布局对象接收到所有正确的响应,但是集合视图设置了某种标志,以便在更新期间不更新单元格。 UICollectionView 上的私有方法在 layoutSubviews 中调用,它似乎负责刷新单元格的外观: _updateVisibleCellsNow:。这是在应用单元格起始属性之前最终要求数据源获取新单元格的地方,这似乎是失败点,因为它应该在它应该被调用时。

So it seems that despite receiving all the correct responses from the layout object, the collection view has some kind of flag set to not update the cells during the update. There is a private method on UICollectionView called from within layoutSubviews that seems responsible for refreshing the cells' appearance: _updateVisibleCellsNow:. This is from where the data source eventually gets asked for a new cell before applying the cells starting attributes, and it seems this is the point of failure, as it is not being called when it should be.


此外,这似乎与更新动画有关,或者至少单元格是未针对插入/删除的持续时间进行更新。例如,下面的工作没有毛刺:

Additionally, this does seem to be related to the update animation, or at least cells are not updated for the duration of the insertion/deletion. For example the following works without glitches:

- (void)addCell
{
    NSIndexPath *indexPathToInsert = [NSIndexPath indexPathForItem:self.numberOfItems
                                                         inSection:0];
    self.numberOfItems++;
    [self.collectionView insertItemsAtIndexPaths:@[indexPathToInsert]];
    [self.collectionView scrollToItemAtIndexPath:indexPathToInsert
                                atScrollPosition:UICollectionViewScrollPositionCenteredVertically
                                        animated:YES];
}

如果在插入的单元格在外面时调用上述方法插入单元格当前可见边界,项目插入时没有动画,集合视图滚动到它,正确地出列并在途中显示单元格。

If the above method is called to insert a cell while the inserted cell is outside the current visible bounds, the item is inserted without animation and the collection view scrolls to it, properly dequeuing and displaying cells on the way.

iOS中出现问题7& iOS 8 beta 5.

推荐答案

调整内容插入内容,使其超出设备屏幕大小的范围稍微。

Adjust your content insets so that they go beyond the bounds of the device's screen size slightly.

collectionView.contentInsets = UIEdgeInsetsMake(-5,0,0,0); //Adjust this value until it looks ok

这篇关于删除UICollectionView边缘的单元格 - 滚动后不会立即显示单元格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆