无法使用 AutoLayout 设置 UICollectionViewCell 的宽度

Posted

技术标签:

【中文标题】无法使用 AutoLayout 设置 UICollectionViewCell 的宽度【英文标题】:Unable to set width of UICollectionViewCell with AutoLayout 【发布时间】:2018-09-18 09:54:18 【问题描述】:

我有一个集合视图,其中包含动态高度的单元格(基于可能的多行标签和内部的文本视图),当我将它跨越多行时,它可以完美地适应高度。但是,当文本只有一个单词或没有覆盖整个屏幕宽度时,单元格的宽度与需要的一样宽,从而导致单元格彼此相邻而不是彼此下方。

Take a look at the result with different lengths of text inside the textview.

但是,我希望单元格彼此位于下方,想想 Instagram 或 Twitter 那种视图。我显然需要为单元格设置宽度约束,使其等于屏幕的整个宽度,但我似乎找不到如何做到这一点。

我在 AppDelegate 中使用 FlowLayout 和估计的ItemSize:

let layout = UICollectionViewFlowLayout()
window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))

layout.estimatedItemSize = CGSize(width: 414, height: 150)

我还在viewDidLoad中为collectionView设置了约束,所以collectionView占据了屏幕的整个宽度和高度:

collectionView?.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
collectionView?.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
collectionView?.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
collectionView?.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true

我对标签和文本视图的约束看起来像这样,也允许宽度采用所需的大小(所以它不是固定的):

// vertical constraints usernameLabel & commentTextView
addConstraintsWithFormat(format: "V:|-16-[v0]-2-[v1]-16-|", views: usernameLabel, commentTextView)

// horizontal constraints usernameLabel
addConstraintsWithFormat(format: "H:|-16-[v0]-16-|", views: usernameLabel)

// horizontal constraints commentTextView
addConstraintsWithFormat(format: "H:|-16-[v0]-16-|", views: commentTextView)

在我看来,所有这些对于完成这项工作至关重要。但是,在我看来,我还没有设置宽度约束来完成这项工作。我试图在 collectionView 上添加一个 widthAnchor(等于 view.widthAnchor),这不起作用:

collectionView?.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true

我也尝试在 sizeForItemAt 方法中设置宽度,但这也没有用:

return CGSize(width: view.frame.width, height: 100)

现在,我正在检查宽度,看看哪里出了问题,但我得到了我需要的结果,我想:

print("collectionView width: ", collectionView?.frame.size.width)
print("collectionView height: ", collectionView?.frame.size.height)

print("screen width: ", UIScreen.main.bounds.width)
print("screen height: ", UIScreen.main.bounds.height)

结果:

collectionView width:  Optional(414.0)
collectionView height:  Optional(736.0)
screen width:  414.0
screen height:  736.0

我不知道如何前进。我浏览过的与此问题相关的所有文章或帖子要么明确计算高度,要么使用我上面提到的两种解决方案。

关于我做错了什么有什么想法吗?

【问题讨论】:

检查您是否已将 collectionView 委托设置为您要覆盖 sizeForItemAt 函数的控制器,否则它不会被调用。 其实你是在用UICollectionViewCell模仿UITableViewCell的行为。您尝试完成的 UI 可以使用 UITableViewCell 轻松完成,它开箱即用,具有自动(可变)高度。但是,如果您需要使用UICollectionViewCell 完成这项工作,您应该查看this answer。还要检查other answer。 【参考方案1】:

更新:

我根据 Siju Satheesachandran 的回答为我的 HomeController 添加了一个扩展:

extension HomeController 
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        return CGSize(width: 400, height: 100)
    

然后,在 cellForItemAt 方法中,我添加了当前单元格宽度的打印:

print("cell width: ", cell.frame.size.width)

返回以下五次:

cell width:  400.0

所以在我看来,需要的尺寸是被聆听的,但我的单元格只是适应它想要的任何宽度?

另一个有趣的更新:

当我将 TextView 的文本更改为太长,因此它应该跨越多行,然后我运行应用程序时,应用程序在不加载和显示黑屏的情况下无限次给出此调试错误:

2018-09-18 15:34:35.806688+0200 prjDynamicCells[39793:1627318] The behavior of the UICollectionViewFlowLayout is not defined because:
2018-09-18 15:34:35.807472+0200 prjDynamicCells[39793:1627318] the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
2018-09-18 15:34:35.807572+0200 prjDynamicCells[39793:1627318] Please check the values returned by the delegate.
2018-09-18 15:34:35.807911+0200 prjDynamicCells[39793:1627318] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7feb5ec17650>, and it is attached to <UICollectionView: 0x7feb5f841600; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x60000044e6d0>; layer = <CALayer: 0x60000003f4a0>; contentOffset: 0, -64; contentSize: 414, 1000; adjustedContentInset: 64, 0, 0, 0> collection view layout: <UICollectionViewFlowLayout: 0x7feb5ec17650>.
2018-09-18 15:34:35.808009+0200 prjDynamicCells[39793:1627318] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.

【讨论】:

【参考方案2】:

确保在应用中添加此协议

UICollectionViewDelegate
UICollectionViewDataSource
UICollectionViewDelegateFlowLayout

请使用以下方法

    extension YourViewController: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout 

        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
             return CGSize(width: screenWidth, height: screenWidth)
        

    

【讨论】:

添加扩展时,我收到错误“HomeController”与协议“UICollectionViewDataSource”的冗余一致性(UICollectionViewDelegateFlowLayout 和 UICollectionViewDelegate 也出现相同的错误)。当我尝试将这些协议添加到我的类 HomeController 中,然后我在其中调用 sizeForItemAt 方法时,我也得到了冗余一致性错误..所以我已经在调用它了,我猜?还是我需要在其他地方添加它? 你为你的 HomeController 类继承了 UICollectionViewController 吗?如果是,则这些协议已经分配。 如果子类声明与已经从超类继承的协议的一致性,您将收到该错误消息。从子类声明中删除协议一致性可以解决问题:冗余一致性“意味着您正在指定扩展 FlickrPhotosViewController : UICollectionViewDataSource,但这不是必需的,因为 FlickrPhotosViewController 的超类已经提供了 UICollectionViewDataSource 一致性。您可以改用扩展 FlickrPhotosViewController。 是的,我在 HomeController 中对 UICollectionViewController 进行了子类化,因为我需要它来设置我的 CollectionView 的所有内容(numberOfItemsInSection、cellForItemAt、minimumLineSpacingForSectionAt),并且我的一般 viewDidLoad 也包含 CollectionView 的约束。如果我删除它,我会收到错误,因为它丢失了。那么A不能添加我需要的所有吗? 你可以使用扩展控制器来代替

以上是关于无法使用 AutoLayout 设置 UICollectionViewCell 的宽度的主要内容,如果未能解决你的问题,请参考以下文章

如何使用autolayout设置tableHeaderView(UITableView)的高度?

如何使用 Autolayout iOS 在两个视图之上设置视图

(八十七)AutoLayout的简单介绍与实例

autolayout--约束的优先级

在自定义视图和视图控制器中使用 AutoLayout

IOS / Autolayout:如何修复大文本视图的宽度?