【问题标题】:UICollectionView invalidateLayout doesn't fire the sizeForItem on screen orientation changeUICollectionView invalidateLayout 不会在屏幕方向更改时触发 sizeForItem
【发布时间】:2017-10-31 22:18:39
【问题描述】:

我有一个 UICollectionViewController 有一堆单元格,每个单元格都有屏幕大小。

如果我以纵向或横向启动应用程序,一切都会按预期工作。

问题是当我更改屏幕方向并调用collectionViewLayout.invalidateLayout() 时,单元格大小保持不变,而没有调整它们的大小。

我不知道为什么会这样。

这里有一些代码:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    print("size for item")
    return CGSize(width: view.frame.width, height: view.frame.height)
}


override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
    coordinator.animate(alongsideTransition: { (_) in
        self.collectionViewLayout.invalidateLayout() // layout update
    }, completion: nil)
}

在第一次加载时,我从设置的尺寸中获取打印件,但在更改方向后,它不会调用它,所以当我更改方向时,单元格大小不适合屏幕,因为它适合启动。

任何想法可能是什么原因造成的,或者我在这里做错了什么?

谢谢

编辑

不知何故,我试图在willTransition 函数中插入一个打印语句,但我注意到它不会在屏幕旋转时触发。我认为问题出在invaldateLayout() 之外,因为它从不触发。

这个覆盖函数怎么不自动调用呢?

【问题讨论】:

    标签: ios swift uicollectionview


    【解决方案1】:

    UICollectionViewFlowLayoutInvalidationContext 上有一个专用标志,称为invalidateFlowLayoutDelegateMetrics。文档内容如下:

    此属性的默认值为 false。如果您因更改任何项目的大小而使布局无效,请将此属性设置为 true。 当此属性设置为 true 时,流布局对象会重新计算其项目和视图的大小,并根据需要查询委托对象以获取该信息。

    在流布局子类中升旗。

    class CollectionViewFlowLayout: UICollectionViewFlowLayout {
            
        override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
            let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
            if let collectionView = collectionView {
                context.invalidateFlowLayoutDelegateMetrics = collectionView.bounds.size != newBounds.size
            }
            return context
        }
    }
    

    【讨论】:

    • 太棒了!网络上很少有人提到这一点。我将它与shouldInvalidateLayout(forBoundsChange newBounds: CGRect)结合使用
    【解决方案2】:

    我可以推荐的是覆盖布局类并实现shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool

    class CollectionViewLayoutSelfSizingCells : UICollectionViewFlowLayout 
    {    
        override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
            if let collectionView = self.collectionView {
                return collectionView.frame.size != newBounds.size
            }
    
            return false
        }
    }
    

    这是我找到的最好的解决方案,它适用于整个项目。 老实说,我很惊讶UICollectionViewFlowLayout不是默认设置

    【讨论】:

    • 很酷的东西,也可以通过扩展名extension UICollectionViewFlowLayout { open override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
    • @AlexandreG 我很确定你不想通过扩展来做到这一点,因为它会改变你在应用程序中拥有的所有布局的行为——这不是你想要的。
    【解决方案3】:

    您应该实现func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 方法。每当视图控制器的大小发生变化时都会调用此方法,例如在纵向和横向之间旋转时:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
    
        coordinator.animate(alongsideTransition: { (_) in
            self.collectionViewLayout.invalidateLayout() // layout update
        }, completion: nil)
    }
    

    【讨论】:

    • 天啊,你是对的。我混淆了委托方法。正如您所提到的,当我应该使用 viewWillTransition 时,我正在使用 willTransition。谢谢:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多