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

Posted

技术标签:

【中文标题】使用自动布局时设置 UICollectionViewFlowLayout 的属性不起作用【英文标题】:Settings UICollectionViewFlowLayout's properties not working when using Auto Layout 【发布时间】:2015-08-20 20:34:23 【问题描述】:

我正在尝试使用 UICollectionViewFlowLayout 设置 UICollectionView,并满足以下要求:minimumLineSpacing 应始终恰好为三分之一UICollectionView 的高度。我最初的想法是像这样覆盖 viewDidLayoutSubviews

override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()

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

请注意,我使用 viewDidLayoutSubviews 是因为我打算使用自动布局,并且框架可能取决于一些复杂的约束。所以我无法自己计算框架,但必须等到 Auto Layout 计算好后才能在 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 等其他属性也不起作用。

【问题讨论】:

不使用viewDidLayoutSubviews,您是否尝试过提供委托方法- collectionView:layout:minimumLineSpacingForSectionAtIndex: 【参考方案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。

【讨论】:

感谢您的帮助。虽然我不太喜欢 dispatch_async 解决方法,但我同意这是我现在唯一能做的事情。我已经怀疑这是一个错误,所以我提交了一个雷达 (openradar.appspot.com/radar?id=5081000568684544) 是的,我也不喜欢这种变通方法,因为感觉它没有必要!但如果 UIKit 本身是不正确的,则可能没有“正确”的解决方案。

以上是关于使用自动布局时设置 UICollectionViewFlowLayout 的属性不起作用的主要内容,如果未能解决你的问题,请参考以下文章

快速使用自动布局时为 UIView 高度设置动画

如何使用自动布局以编程方式快速设置旋转时 UICollectionView 的宽度

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

错误:线程 1:“无法设置未准备好约束的视图层次结构的布局。”使用自动布局时

未设置 NIB“使用自动布局”时,我可以使用 NSLayoutConstraint 吗?

当我使用自动布局和 xib 制作 uitableviewcell 时,为啥要将 translatesAutoresizingMaskIntoConstraints 设置为 YES?