使用estimatedItemSize删除项目时,UICollectionView单元格会调整大小

Ash*_*lls 8 ios autolayout uicollectionview swift

我有一个简单的项目,其故事板只包含一个单独的a UICollectionViewController,使用Xcode 7.1.1 for iOS 9.1构建

class ViewController: UICollectionViewController {

    var values = ["tortile", "jetty", "tisane", "glaucia", "formic", "agile", "eider", "rooter", "nowhence", "hydrus", "outdo", "godsend", "tinkler", "lipscomb", "hamlet", "unbreeched", "fischer", "beastings", "bravely", "bosky", "ridgefield", "sunfast", "karol", "loudmouth", "liam", "zunyite", "kneepad", "ashburn", "lowness", "wencher", "bedwards", "guaira", "afeared", "hermon", "dormered", "uhde", "rusher", "allyou", "potluck", "campshed", "reeda", "bayonne", "preclose", "luncheon", "untombed", "northern", "gjukung", "bratticed", "zeugma", "raker"]

    @IBOutlet weak var flowLayout: UICollectionViewFlowLayout!
    override func viewDidLoad() {
        super.viewDidLoad()        
        flowLayout.estimatedItemSize = CGSize(width: 10, height: 10)
    }

    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return values.count
    }

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as! MyCell
        cell.name = values[indexPath.row]
        return cell
    }

    override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        values.removeAtIndex(indexPath.row)
        collectionView.deleteItemsAtIndexPaths([indexPath])
    }
}

class MyCell: UICollectionViewCell {

    @IBOutlet weak var label: UILabel!

    var name: String? {
        didSet {
            label.text = name
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

从集合视图中删除单元格时,所有剩余的单元格将对其进行动画处理estimatedItemSize,然后交换回正确的大小.

在此输入图像描述

有趣的是,这会在动画发生时为每个单元格生成自动布局约束警告:

2015-12-02 14:30:45.236 CollectionTest[1631:427853] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x14556f780 h=--& v=--& H:[UIView:0x1456ac6c0(10)]>",
    "<NSLayoutConstraint:0x1456acfd0 UIView:0x1456ac6c0.trailingMargin == UILabel:0x1456ac830'raker'.trailing>",
    "<NSLayoutConstraint:0x1456ad020 UILabel:0x1456ac830'raker'.leading == UIView:0x1456ac6c0.leadingMargin>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x1456acfd0 UIView:0x1456ac6c0.trailingMargin == UILabel:0x1456ac830'raker'.trailing>
Run Code Online (Sandbox Code Playgroud)

我最初的想法是,打破这些限制是导致调整大小问题的原因.

更新单元格的awakeFromNib方法:

override func awakeFromNib() {
    super.awakeFromNib()
    contentView.translatesAutoresizingMaskIntoConstraints = false
} 
Run Code Online (Sandbox Code Playgroud)

修复警告,但问题仍然存在.

我尝试在单元格和它之间重新添加自己的约束contentView,但这并没有解决问题:

override func awakeFromNib() {
    super.awakeFromNib()
    contentView.translatesAutoresizingMaskIntoConstraints = false

    for constraint in [
        contentView.leadingAnchor.constraintEqualToAnchor(leadingAnchor),
        contentView.trailingAnchor.constraintEqualToAnchor(trailingAnchor),
        contentView.topAnchor.constraintEqualToAnchor(topAnchor),
        contentView.bottomAnchor.constraintEqualToAnchor(bottomAnchor)]
    {
        constraint.priority = 999
        constraint.active = true
    }
}
Run Code Online (Sandbox Code Playgroud)

思考?

Dmi*_*try 2

流布局在进行布局后通过估计大小计算单元格的实际大小,以定义哪些单元格可见。之后它会根据实际尺寸调整布局。

但是,当它制作动画时,当它计算动画的初始位置时,它没有达到使单元出队并在那里运行自动布局的阶段,因此它仅使用估计的大小。

最简单的方法是尝试给出最接近的估计大小,或者您是否可以在sizeForItemAt调用中的委托中提供大小。

就我而言,我试图在不插入或删除单元格的情况下对布局属性进行动画处理,对于该特定情况,我子类化UICollectionViewFlowLayout然后覆盖此方法:

override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
    if !context.invalidateEverything && context.invalidatedItemIndexPaths == nil && context.contentOffsetAdjustment == .zero  && context.contentSizeAdjustment == .zero {
        return
    }
    super.invalidateLayout(with: context)
}
Run Code Online (Sandbox Code Playgroud)

这可以防止在没有任何更改的情况下使用估计大小重新计算布局属性。