使用自动布局时设置UICollectionViewFlowLayout的属性不工作 [英] Settings UICollectionViewFlowLayout's properties not working when using Auto Layout

查看:207
本文介绍了使用自动布局时设置UICollectionViewFlowLayout的属性不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想建立一个的 UICollectionView 的用的 UICollectionViewFlowLayout 的有以下要求: minimumLineSpacing 的应该永远是完全相同的三分之一在 UICollectionView 的高度的。我最初的想法是要覆盖的 viewDidLayoutSubviews 的是这样的:

I'm trying to set up a UICollectionView with a UICollectionViewFlowLayout with the following requirement: the minimumLineSpacing should always be exactly one-third of the height of the UICollectionView. My initial thought was to override viewDidLayoutSubviews like this:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

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

请注意,我用的 viewDidLayoutSubviews 的,因为我计划使用自动布局和框架可能取决于一些复杂的约束。所以,我无法计算框架,但我必须等待,直到自动布局计算这对我来说,使用的 viewDidLayoutSubviews

Note that I use viewDidLayoutSubviews because I'm planning to use Auto Layout and the frame may depend on some complex constraints. So I can't calculate the frame myself but have to wait until Auto Layout calculated it for me to use in viewDidLayoutSubviews.

我通过创建的 UICollectionView 的编程(和旋转模拟器,以查看是否在<青霉> minimumLineSpacing 的是总是正确的)这个测试了一下。它似乎工作得很好。

I tested this a bit by creating a UICollectionView programmatically (and rotating the simulator to see if the minimumLineSpacing is always correct). It seemed to work just fine.

然后,我切换到自动布局。我只是受限集合视图的顶部,底部,开头和结尾的空间,它的父。这样做,设置后的 minimumLineSpacing 的没有达到预期效果了,它根本没有改变对集合视图的外观东西。

Then, I switched to Auto Layout. I simply constrained the collection view's top, bottom, leading and trailing space to its superview. After doing so, setting the minimumLineSpacing didn't have the intended effect anymore, it simply didn't change anything about the appearance of the collection view.

以下code很好地说明了这个问题。当我设置 useAutoLayout 真正,设置的 minimumLineSpacing 的不工作了

The following code nicely demonstrates the issue. As soon as I set useAutoLayout to true, setting the minimumLineSpacing doesn't work anymore.

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
    }

}

测试此code在模拟器,旋转,看看如何设置的 minimumLineSpacing 的时候设置 useAutoLayout 不会做任何事情到真正。所以我的问题是:如何使用自动布局,并仍然提供的 minimumLineSpacing

Test this code in the Simulator, rotate it and see how setting the minimumLineSpacing doesn't do anything when useAutoLayout is set to true. So my question is: How can I use Auto Layout and still provide a minimumLineSpacing?

备注结果
基础SDK 的设置为的iOS 8.4 SDK 的。设置其它属性,如 itemSize minimumInteritemSpacing 也不行。

Notes
Base SDK is set to iOS 8.4 SDK. Setting other properties like itemSize or minimumInteritemSpacing doesn't work either.

推荐答案

我复制你的描述。这是非常奇怪的。

I've reproduced what you describe. It's very strange.

我觉得有一些是关于具体的旋转导致 invalidLayout()将在这方面忽略。也许问题是,您的来电的invalidateLayout()发生在那里的布局对象认为它已经做出回应,或者是在应对的过程中的一个点,到布局失效由集合视图的旋转和由此产生的边界变化而自动产生的。那么你的失效被忽略,因为它来得太晚了被合并进入自动之一,并且还为时过早算作一个单独的失效。我猜。我注意到,你甚至可以保持递增 minimumLineSpacing 在那里,它会高兴地进军到无限远没有集合视图以往任何时候都具有机智再做布局。

I think there is something about rotation specifically that causes invalidLayout() to be ignored in this context. Perhaps the problem is that your call to invalidateLayout() occurs at a point where the layout object thinks it has already responded, or is in the process of responding, to the layout invalidation automatically produced by the rotation and consequent bounds change of the collection view. Then your invalidation is ignored, because it comes too late to be coalesced into the automatic one, and too soon to count as a separate invalidation. I'm guessing. I notice that you can even keep incrementing the minimumLineSpacing there, and it will happily march up to infinity without the collection view ever having the wit to do layout again.

但是,如果你设置视图控制器,这样一摇事件触发失效,那么它注意到的价值。

But if you set up the view controller so that a shake event triggers the invalidation, then it notices the value.

所以,你可以通过强制无效在运行循环的下一回合的情况发生,从而逃避什么奇怪特殊的逻辑旋转过程中阻止它解决问题。例如,如果您更换 viewDidLayoutSubviews()通过以下内容:

So you can solve the problem by forcing the invalidation to happen at the next turn of the run loop, thus escaping whatever weird special logic is blocking it during rotation. For instance, if you replace your viewDidLayoutSubviews() with the following:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()

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

然后它工作。

为什么会这样有必要吗?我不知道。我不认为它应该是必要的。这感觉就像在 UICollectionView 给我一个bug,或者至少是一个非常直观的API一块的。

Why should this be necessary? I don't know. I don't think it should be necessary. This feels like a bug in UICollectionView to me, or at least a very unintuitive piece of API.

这篇关于使用自动布局时设置UICollectionViewFlowLayout的属性不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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