使用estimatedItemSize删除项目时,UICollectionView单元格会调整大小 [英] UICollectionView cells resizing when deleting items with estimatedItemSize

查看:414
本文介绍了使用estimatedItemSize删除项目时,UICollectionView单元格会调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  class ViewController:UICollectionViewController {

var values = [tortile,jetty,tisane,glaucia,formic ,敏捷,eider,rooter,nowhence,hydrus,outdo,godsend,tinkler,lipscomb,hamlet,unbreeched,fischer, beastings,勇敢地,bosky,ridgefield,sunfast,karol,loudmouth,liam,zunyite,kneepad,ashburn,lowness,wencher ,卧床,guaira,afeared,hermon,dormered,uhde,rusher,allyou,potluck,campshed,reeda,bayonne, preclose,午餐,未注明,北方,gjukung,bratticed,zeugma,raker]

@IBOutlet弱var flowLayout:UICollectionViewFlowLayout!
覆盖func viewDidLoad(){
super.viewDidLoad()
flowLayout.estimatedItemSize = CGSize(宽度:10,高度:10)
}

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

override func collectionView(collectionView:UICollectionView,cellForItemAtIndexPath indexPath:NSIndexPath) - > UICollectionViewCell {
让cell = collectionView.dequeueReusableCellWithReuseIdentifier(MyCell,forIndexPath:indexPath)为! MyCell
cell.name = values [indexPath.row]
返回单元格
}

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

class MyCell:UICollectionViewCell {

@IBOutlet弱var标签:UILabel!

var name:String? {
didSet {
label.text = name
}
}
}

从集合视图中删除单元格时,所有剩余的单元格将动画为 estimatedItemSize ,然后交换回正确的大小。 / p>



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

  2015-12-02 14:30:45.236 CollectionTest [1631:427853]无法同时满足限制。 
可能至少以下列表中的一个约束是您不想要的约束。
试试这个:
(1)查看每个约束并尝试找出你不期望的;
(2)找到添加了不需要的约束或约束的代码并修复它。
(注意:如果你看到你不理解的NSAutoresizingMaskLayoutConstraints,请参阅UIView属性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>


将尝试通过违约约束来恢复
< NSLayoutConstraint:0x1456acfd0 UIView:0x1456ac6c0.trailingMargin == UILabel:0x1456ac830'raker'.trailing>

我最初的想法是打破这些限制是造成调整大小问题的原因。



更新单元格的 awakeFromNib 方法:

 覆盖func awakeFromNib(){
super.awakeFromNib()
contentView.translatesAutoresizingMaskIntoConstraints = false
}

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



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

 覆盖func awakeFromNib (){
super.awakeFromNib()
contentView.translatesAutoresizingMaskIntoConstraints = false

for [
contentView.leadingAnchor.constraintEqualToAnchor(leadingAnchor)中的约束,
contentView.trailingAnchor.constraintEqualToAnchor(trailingAnchor),
contentView.topAnchor.constraintE qualToAnchor(topAnchor),
contentView.bottomAnchor.constraintEqualToAnchor(bottomAnchor)]
{
constraint.priority = 999
constraint.active = true
}
}

想法?

解决方案

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



然而,当它动画时,当它计算动画的初始位置时,它不会到达出列单元格的阶段并且在那里运行自动布局,因此它仅使用估计的尺寸。



最简单的方法是尝试给出最接近的估计尺寸,或者如果你可以提供尺寸委托 sizeForItemAt 调用。



在我的情况下,我试图在不插入或删除单元格的情况下为layoutAttributes设置动画该特定情况我将 UICollectionViewFlowLayout 子类化,然后重写此方法:

 覆盖func invalidateLayout(带上下文:UICollectionViewLayoutInvalidationContext){
if!context.invalidateEverything&& context.invalidatedItemIndexPaths == nil&& context.contentOffsetAdjustment == .zero&& context.contentSizeAdjustment == .zero {
return
}
super.invalidateLayout(with:context)
}

这可以防止在没有任何更改时使用估计的尺寸重新计算布局属性。


I have a simple project with a storyboard containing only a single a UICollectionViewController, built with 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
        }
    }
}

When deleting the cells from the collection view, all remaining cells animate to their estimatedItemSize, and then swap back to the correct size.

Interestingly, this produces auto layout constraint warnings for each cell when the animation occurs:

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>

My initial thought was that breaking these constraints was what was causing the resizing problem.

Updating the cell's awakeFromNib method:

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

fixes the warnings, but the problem still occurs.

I tried re-adding my own constraints between the cell and its contentView, but this didn't resolve the issue:

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

Thoughts?

解决方案

flow layout calculates actual sizes of cells after doing layout by estimated sizes to define which ones are visible. After that it adjusts the layout based on real sizes.

However, when it animates, when it calculates initial position for animation, it doesn't reach the stage of dequeueing cells and running auto layout there, so it uses only estimated sizes.

The easiest way is to try to give the closest estimated sizes, or if you could provide the size in the delegate in sizeForItemAt call.

In my case, I was trying to animate layoutAttributes without inserting or deleting cells and for that specific case I subclassed UICollectionViewFlowLayout and then overridden this method:

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

This prevents recalculating layout attributes using estimated sizes when nothing has been changed.

这篇关于使用estimatedItemSize删除项目时,UICollectionView单元格会调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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