设置UICollectionViewFlowLayout的属性在使用自动布局时不起作用

时间:2015-08-20 20:34:24

标签: ios cocoa-touch autolayout uicollectionview

我尝试使用 UICollectionViewFlowLayout 设置 UICollectionView ,并满足以下要求: minimumLineSpacing 应该始终是一个 - UICollectionView 的高度的三分之一。我最初的想法是覆盖 viewDidLayoutSubviews ,如下所示:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    collectionViewFlowLayout.minimumLineSpacing = collectionView.frame.height / 3
    collectionViewFlowLayout.invalidateLayout()
}

请注意,我使用 viewDidLayoutSubviews ,因为我计划使用自动布局,框架可能依赖于某些复杂的约束。所以我无法自己计算帧数,但必须等到自动布局计算出它才能在 viewDidLayoutSubviews 中使用。

我通过以编程方式创建 UICollectionView 来测试这一点(并旋转模拟器以查看 minimumLineSpacing 是否始终正确)。它似乎工作得很好。

然后,我切换到自动布局。我只是简单地将集合视图的顶部,底部,前导和尾随空间限制在其超级视图中。在这样做之后,设置 minimumLineSpacing 不再具有预期的效果,它根本没有改变集合视图的外观。

以下代码很好地演示了该问题。只要我将useAutoLayout设置为true,设置 minimumLineSpacing 就不再有用了。

class DemoViewController: UIViewController, UICollectionViewDataSource {

    var collectionView: UICollectionView!

    var collectionViewFlowLayout: UICollectionViewFlowLayout!

    // MARK: - UIViewController

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionViewFlowLayout = UICollectionViewFlowLayout()
        collectionViewFlowLayout.itemSize = CGSizeMake(100, 100)

        collectionView = UICollectionView(frame: view.frame, collectionViewLayout: collectionViewFlowLayout)
        collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.dataSource = self

        view.addSubview(collectionView)

        let useAutoLayout = false // Change this to true to test!

        if useAutoLayout {
            collectionView.setTranslatesAutoresizingMaskIntoConstraints(false)

            NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[collectionView]-|", options: nil, metrics: nil, views: ["collectionView" : collectionView]))
            NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[collectionView]-|", options: nil, metrics: nil, views: ["collectionView" : collectionView]))
        } else {
            collectionView.autoresizingMask = .FlexibleHeight | .FlexibleWidth
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        collectionViewFlowLayout.minimumLineSpacing = collectionView.frame.height / 3
        collectionViewFlowLayout.invalidateLayout()
    }

    // MARK: - <UICollectionViewDataSource>

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 100
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! UICollectionViewCell

        cell.backgroundColor = UIColor.greenColor()

        return cell
    }

}

在模拟器中测试此代码,旋转它,看看当useAutoLayout设置为true时如何设置 minimumLineSpacing 不做任何事情。所以我的问题是:如何使用自动布局并仍然提供 minimumLineSpacing

备注
Base SDK 设置为 iOS 8.4 SDK 。设置itemSizeminimumInteritemSpacing等其他属性也不起作用。

1 个答案:

答案 0 :(得分:1)

我已经复制了你描述的内容。这很奇怪。

我认为有一些关于轮换的内容会导致invalidLayout()在此上下文中被忽略。也许问题是你对invalidateLayout()的调用发生在布局对象认为它已经响应或正在响应的点上,因为旋转自动产生的布局失效以及随后的边界变化。集合视图。然后忽略您的失效,因为它被合并到自动失效中为时已晚,而且太早被视为单独的失效。我在猜测。我注意到你甚至可以继续增加minimumLineSpacing那里,并且它会愉快地进入无限,而集合视图没有机智再次进行布局。

但如果您设置视图控制器以使震动事件触发失效,则会注意到该值。

因此,您可以通过强制在运行循环的下一轮发生无效来解决问题,从而避免在旋转期间阻止它的任何奇怪的特殊逻辑。例如,如果您使用以下内容替换viewDidLayoutSubviews()

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()

  let newValue = collectionView.bounds.height / 3
  dispatch_async(dispatch_get_main_queue()) {
    [weak collectionViewFlowLayout] in
    collectionViewFlowLayout?.minimumLineSpacing = newValue
  }

然后它有效。

为什么这是必要的?我不知道。我不认为这是必要的。这感觉就像UICollectionView中的一个错误,或者至少是一个非常不直观的API。