[英]Calculating Size of Cell for CollectionView Mosaic Layout
I'm trying to make a mosaic collection view layout similar to Google's Keep app.我正在尝试制作类似于 Google 的 Keep 应用的马赛克集合视图布局。 I've subclassed UICollectionViewLayout
similar to the many tutorials found online.我对UICollectionViewLayout
进行了子类化,类似于在网上找到的许多教程。 In order to properly layout the collection view cells, the calling class must implement a delegate, HeightForCellAtIndexPath
method to get the cell's height.为了正确布局集合视图单元格,调用 class 必须实现委托, HeightForCellAtIndexPath
方法来获取单元格的高度。 In my case, I also get the cell's width to create 1, 2 or 3 column layouts.就我而言,我还获得了单元格的宽度来创建 1、2 或 3 列布局。
In all of the tutorials, the height of the cell's content is known and does not need to be computed.在所有教程中,单元格内容的高度都是已知的,不需要计算。 In my case, the size of content is not known and needs to be computed.就我而言,内容的大小是未知的,需要计算。 I've tried many different ways of calculating this but none work perfectly.我尝试了许多不同的计算方法,但没有一个能完美运行。 My latest attempt entails creating a CardContent
class and adding that to a cell's contentView
in cellForItemAt
and also instantiate a CardContent
instance in HeightForCellAtIndexPath
to calculate the size of the content that is passed to the layout class.我最近的尝试需要创建一个CardContent
class 并将其添加到 cellForItemAt 中单元格的contentView
中,并在cellForItemAt
中实例化一个CardContent
实例以计算传递给布局HeightForCellAtIndexPath
的内容的大小。
I'm sure there are many problems with my methodology, but from what I can gather, the issue appears to be with the multi-line labels not laid out correctly in HeightForCellAtIndexPath
in that the labels are not wrapping to multi line and remain as a single line thus giving me an incorrect height of the contentView
.我确定我的方法存在很多问题,但据我所知,问题似乎在于多行标签在HeightForCellAtIndexPath
中的布局不正确,因为标签没有换成多行并保持为单行因此给了我不正确的contentView
高度。
CardContentCell.swift CardContentCell.swift
import UIKit
class CardContentCell: UICollectionViewCell {
var todoList: TodoList! {
didSet {
self.backgroundColor = UIColor(todoList.color)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 5.0
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
CardContent.swift卡片内容.swift
Edit: Added createLineItem
method.编辑:添加createLineItem
方法。 See answer below.请参阅下面的答案。
class CardContent: UIStackView {
var todoList: TodoList!
var verticalItemSpacing: CGFloat = 10.0
var cellWidth: CGFloat!
init(todoList: TodoList, cellWidth: CGFloat = 0.0) {
self.todoList = todoList
self.cellWidth = cellWidth
super.init(frame: CGRect(x: 0, y: 0, width: cellWidth, height: 0))
self.axis = .vertical
self.alignment = .fill
self.distribution = .fill
self.contentMode = .scaleToFill
self.spacing = 10.0
layoutContent()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createTitleLabel(title: String) -> UILabel {
let label = UILabel()
label.text = title
label.font = label.font.withSize(20.0)
label.numberOfLines = 2
label.lineBreakMode = .byTruncatingTail
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
func createItemLabel(text: String) -> UILabel {
let label = UILabel()
label.text = text
label.font = label.font.withSize(17.0)
label.numberOfLines = 3
label.lineBreakMode = .byTruncatingTail
label.translatesAutoresizingMaskIntoConstraints = false
label.sizeToFit()
return label
}
func createLineItem(text: String) -> UIStackView {
let hstack = UIStackView()
hstack.axis = .horizontal
hstack.alignment = .fill
hstack.distribution = .fillProportionally
let imgView = createImgView(withFont: lineItemFont)
let textLabel = createItemLabel(text: text)
hstack.addArrangedSubview(imgView)
hstack.addArrangedSubview(textLabel)
return hstack
}
func layoutContent() {
self.addArrangedSubview(createTitleLabel(title: todoList.title))
for todo in todoList.todos.prefix(6) {
let lineItem = createLineItem(text: todo.text)
self.addArrangedSubview(lineItem)
}
}
}
MyCollectionView.swift MyCollectionView.swift
extension MyCollectionView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! CardContentCell
cell.todoList = todoLists[indexPath.row]
let content = CardContent(todoList: cell.todoList)
cell.contentView.addSubview(content)
content.pinTopAndSides(to: cell.contentView) // See extension below
return cell
}
}
extension MyCollectionView: CardLayoutDelegate {
func collectionView(_ collectionView: UICollectionView, HeightForCellAtIndexPath indexPath: IndexPath, cellWidth: CGFloat) -> CGFloat {
let todoList = todoLists[indexPath.row]
let stackView = CardContent(todoList: todoList, cellWidth: cellWidth)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.setNeedsLayout()
stackView.layoutIfNeeded()
let size = stackView.frame.size
return size.height
}
}
extension UIView {
func pinTopAndSides(to other: UIView) {
translatesAutoresizingMaskIntoConstraints = false
leadingAnchor.constraint(equalTo: other.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: other.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: other.topAnchor).isActive = true
}
}
The result is, if there are always 6 line items, then the computed height is always 230 (in a 2 column layout).结果是,如果始终有 6 个行项目,则计算的高度始终为 230(在 2 列布局中)。 In the screen shot below, the cell is colored while the rest of the content overflows.在下面的屏幕截图中,单元格被着色,而内容的 rest 溢出。
Barring a better solution, the answer for me involved not using a nested horizontal UIStackview
.除非有更好的解决方案,否则我的答案是不使用嵌套的水平UIStackview
。 That was fraught with unknowns and hard to diagnose auto layout issues.这充满了未知数并且难以诊断自动布局问题。 Instead, I used a UIView
and added my own constraints.相反,我使用了UIView
并添加了我自己的约束。
Here's the method that creates said view.这是创建所述视图的方法。 It's interesting that no one took a close enough look at my question that in my hurry to copy and past, I omitted this most crucial method in the original post.有趣的是,没有人仔细研究我的问题,以至于在我急于复制和过去的过程中,我在原帖中省略了这个最关键的方法。 I will update the question with the original implementation of this method for reference.我将使用此方法的原始实现来更新问题以供参考。
func createLineItem(text: String) -> UIView {
let view = UIView()
let imgView = createImgView(withFont: lineItemFont)
imgView.translatesAutoresizingMaskIntoConstraints = false
let textLabel = createItemLabel(text: text)
textLabel.translatesAutoresizingMaskIntoConstraints = false
imgView.tintColor = self.textColor
view.addSubview(imgView)
view.addSubview(textLabel)
imgView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
imgView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
textLabel.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 5.0).isActive = true
textLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
textLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
textLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
return view
}
And,as for the HeightForCellAtIndexPath
delegate function, setting the widthAnchor
to the cell width provided the correct height of the cell:而且,对于HeightForCellAtIndexPath
委托 function,将widthAnchor
设置为单元格宽度提供了正确的单元格高度:
func collectionView(_ collectionView: UICollectionView, HeightForCellAtIndexPath indexPath: IndexPath, cellWidth: CGFloat) -> CGFloat {
let stackView = CardContent(todoList: todoList)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.widthAnchor.constraint(equalToConstant: cellWidth).isActive = true
stackView.setNeedsLayout()
stackView.layoutIfNeeded()
let size = stackView.frame.size
return size.height
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.