具有自动布局的UICollectionView自定义单元格

raw*_*bee 192 ios autolayout uicollectionview uicollectionviewcell

我正在尝试UICollectionViewCells使用自动布局进行自我调整,但我似乎无法让单元格根据内容调整自己的大小.我无法理解如何根据单元格内容中的内容更新单元格的大小.

这是我尝试过的设置:

  • 在其contentView中自定义UICollectionViewCell一个UITextView.
  • 滚动显示UITextView已禁用.
  • contentView的水平约束是:"H:| [_textView(320)]",即UITextView固定在单元格左侧,显式宽度为320.
  • contentView的垂直约束是:"V:| -0 - [_ textView]",即UITextView固定到单元格的顶部.
  • UITextView具有高度限制设置为其中一个恒定的UITextView报告将适合文本.

这是单元格背景设置为红色,UITextView背景设置为蓝色的样子: 细胞背景红色,UITextView背景蓝色

我把我已经在GitHub上玩的项目在这里.

Dan*_*sko 284

针对Swift 4进行了更新

preferredLayoutAttributesFittingAttributes 改名为 preferredLayoutAttributesFitting


针对iOS 9进行了更新

在iOS 9下看到我的GitHub解决方案中断后,我终于有时间充分调查此问题.我现在更新了repo,以包含自我调整单元格的不同配置的几个示例.我的结论是自我定型细胞在理论上很好,但在实践中却很混乱.在进行自我定型细胞时要谨慎.

TL; DR

看看我的GitHub项目


自定义单元格仅支持流程布局,因此请确保使用的是您正在使用的内容.

您需要设置两件事来使自定义单元格起作用.

1. systemLayoutSizeFittingSize开启systemLayoutSizeFitting

设置estimatedItemSize属性后,流布局将变为动态.

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
Run Code Online (Sandbox Code Playgroud)

2.添加对细胞子类的大小调整的支持

这有两种口味; 自动布局自定义覆盖UICollectionViewFlowLayout.

使用自动布局创建和配置单元格

我不会详细介绍这一点,因为有一篇关于为单元配置约束的精彩文章.只是要小心Xcode 6打破了iOS 7的一堆东西,所以,如果你支持iOS 7,你需要做的事情就像确保在单元格的contentView上设置autoresizingMask并且将contentView的边界设置为单元格的边界时细胞被加载(即estimatedItemSize).

您需要注意的事情是,您的单元格需要比表格视图单元格更严格地受到约束.例如,如果您希望宽度是动态的,那么您的单元格需要高度约束.同样,如果您希望高度是动态的,那么您需要对细胞进行宽度约束.

实现preferredLayoutAttributesFittingAttributes您的自定义单元格

调用此函数时,您的视图已经配置了内容(awakeFromNib即已被调用).假设您的约束已经适当设置,您可以拥有这样的实现:

//forces the system to do one layout pass
var isHeightCalculated: Bool = false

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    //Exhibit A - We need to cache our calculation to prevent a crash.
    if !isHeightCalculated {
        setNeedsLayout()
        layoutIfNeeded()
        let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
        var newFrame = layoutAttributes.frame
        newFrame.size.width = CGFloat(ceilf(Float(size.width)))
        layoutAttributes.frame = newFrame
        isHeightCalculated = true
    }
    return layoutAttributes
}
Run Code Online (Sandbox Code Playgroud)

注意在iOS 9上,如果您不小心,行为会发生一些变化,可能会导致您的实施崩溃(请参阅此处).实现时,preferredLayoutAttributesFittingAttributes您需要确保只更改一次布局属性的框架.如果您不这样做,布局将无限期地调用您的实现并最终崩溃.一种解决方案是在您的单元格中缓存计算出的大小,并在您重复使用单元格时更改此值,或者像我对cellForItem属性一样更改其内容.

体验您的布局

此时,您应该在collectionView中拥有"正常运行"的动态单元格.我在测试期间还没有找到足够的开箱即用解决方案,所以如果有的话,请随时发表评论.它仍然感觉像preferredLayoutAttributesFittingAttributes赢得动态调整恕我直言的战斗.

注意事项

请注意,如果您使用原型单元格来计算estimatedItemSize - 如果您的XIB使用大小类,这将会中断.原因是当您从XIB加载单元格时,其大小类将配置为isHeightCalculated.这只会在iOS 8及更高版本上打破,因为在iOS 7上将根据设备加载大小类(iPad = Regular-Any,iPhone = Compact-Any).您可以在不加载XIB的情况下设置estimatedItemSize,也可以从XIB加载单元格,将其添加到collectionView(这将设置traitCollection),执行布局,然后将其从superview中删除.或者,您也可以让您的单元格覆盖UITableViewgetter并返回相应的特征.由你决定.

如果我错过任何事情,请告诉我,希望我帮助并祝好运编码


  • 这似乎在iOS 9 GM上彻底打破了.设置`estimatedItemSize`会导致崩溃 - 自动布局试图处理UICollectionViewCell的方式似乎存在一些巨大的错误. (17认同)
  • 遇到类似的问题,是否有人找到了解决方法? (3认同)
  • 我正在尝试使用流布局实现相同的功能,但是当我返回具有更新大小的newFrame时,布局似乎不会重新计算y位置,这仍然基于estimatedSize中的高度.少了什么东西? (2认同)
  • 正如其他人所说,您的解决方案并不总是有效。尝试在选择和取消选择后更改单元格的约束,例如。整个集合再次中断。自动调整大小在 ios9 上被破坏了!我将恢复使用 itemSize 委托并自己进行计算。 (2认同)

Mat*_*ala 43

Daniel Galasko答案的一些关键变化解决了我所有的问题.不幸的是,我没有足够的声誉直接评论(还).

在步骤1中,使用自动布局时,只需将单个父UIView添加到单元格即可.单元格内的所有内容都必须是父级的子视图.这回答了我所有的问题.虽然Xcode自动为UITableViewCells添加了这个,但它不适用于UICollectionViewCells(但它应该).根据文件:

要配置单元格的外观,请将显示数据项内容所需的视图添加为contentView属性中视图的子视图.不要直接将子视图添加到单元格本身.

然后完全跳过第3步.这不是必需的.

  • iOS 9,Xcode 7.单元格在storyboard中进行了原型,设置了自定义子类.试图创建一个名为`contentView`的属性,Xcode抱怨它与现有属性冲突.尝试将子视图添加到`self.contentView`并为其设置约束,然后应用程序崩溃. (2认同)

paw*_*221 38

在iOS10中有一个名为UICollectionViewFlowLayout.automaticSize(以前UICollectionViewFlowLayoutAutomaticSize)的新常量,所以相反:

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)
Run Code Online (Sandbox Code Playgroud)

你可以用这个:

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
Run Code Online (Sandbox Code Playgroud)

它具有更好的性能,尤其是当您的集合视图中的单元格具有常量wid时

  • 你在哪里设置这个?在viewdidload?你如何处理flowlayout? (8认同)
  • 我设法实现了这一点 - 但它有一个奇怪的行为,如果有一个单元格,它会使单元格居中。这是关于它的堆栈溢出问题 - 让我难倒了:http://stackoverflow.com/questions/43266924/stop-single-uicollectionview-cell-flowing-to-the-centre-of-the-screen/43269736?noredirect =1 (3认同)
  • 它使我的细胞很小,没用 (2认同)

Mar*_*moy 30

在iOS 10+中,这是一个非常简单的两步过程.

  1. 确保所有单元格内容都放在单个UIView中(或者像UIStackView一样放在UIView的后代内部,这样可以简化自动布局).就像动态调整UITableViewCells一样,整个视图层次结构需要配置约束,从最外层容器到最内层视图.这包括UICollectionViewCell和直接子视图之间的约束

  2. 指示UICollectionView的flowlayout自动调整大小

    yourFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    
    Run Code Online (Sandbox Code Playgroud)

注意(编辑):

在Swift 4中(可能还有3个),它已被重命名为 __CODE__

  • 我没有提到性能,只说简单。仅当您在左右之间以及顶部和底部之间一直存在约束时,调整单元格大小才有效。当所有视图都包含在一个容器中时,最容易实现。如果该容器是 UIStackView,则它比 UIView 的任何其他后代更容易。 (2认同)

kar*_*nms 12

  1. 在viewDidLoad()上添加flowLayout

    override func viewDidLoad() {
        super.viewDidLoad() 
        if let flowLayout = infoCollection.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(width: 1, height:1)
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 另外,将UIView设置为您的单元格的mainContainer,并在其中添加所有必需的视图.

  3. 请参阅这个令人敬畏,令人兴奋的教程以获得进一步的参考: 在iOS 9和10中使用autolayout自动调整单元格的UICollectionView


noo*_*lar 7

经过一段时间的努力,我注意到如果你不禁用滚动,调整大小对UITextViews不起作用:

let textView = UITextView()
textView.scrollEnabled = false
Run Code Online (Sandbox Code Playgroud)


Fat*_*tie 6

contentView 主播之谜:

在一个奇怪的案例中

    contentView.translatesAutoresizingMaskIntoConstraints = false
Run Code Online (Sandbox Code Playgroud)

不会工作。向 contentView 添加了四个显式锚点并且它起作用了。

class AnnoyingCell: UICollectionViewCell {
    
    @IBOutlet var word: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame); common() }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder); common() }
    
    private func common() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}
Run Code Online (Sandbox Code Playgroud)

和往常一样

    estimatedItemSize = UICollectionViewFlowLayout.automaticSize
Run Code Online (Sandbox Code Playgroud)

YourLayout: UICollectionViewFlowLayout

谁知道?可能会帮助某人。

信用

https://www.vadimbulavin.com/collection-view-cells-self-sizing/

偶然发现了那里的提示 - 在有关此的所有 1000 篇文章中从未在其他任何地方看到过。


归档时间:

查看次数:

196822 次

最近记录:

6 年 前