听起来您想要的是一种使用UICollectionView生成UITableView之类的布局的方法。如果这确实是您想要的,那么正确的方法是使用自定义UICollectionViewLayout子类(可能类似于SBTableLayout)。
另一方面,如果您真的要问是否有一种干净的方法可以使用默认的UICollectionViewFlowLayout做到这一点,那么我相信这是没有办法的。即使使用iOS8的自动调整大小单元,也不是一件容易的事。正如您所说,基本问题是流程布局的机制无法固定一个尺寸并让另一个响应。(此外,即使您可以,也需要两次布局遍历来确定多行标签的大小,这会带来额外的复杂性。这可能与自大小调整单元格想要通过一次调用systemLayoutSizeFittingSize来计算所有大小的方式不符。)
但是,如果您仍想使用流布局创建类似于tableview的布局,并使用其单元格确定其自身大小并自然响应集合视图的宽度,那么当然可以。仍然有凌乱的方式。我已经使用“大小调整单元格”完成了该操作,即,未显示的UICollectionViewCell控制器仅用于计算单元格大小。
这种方法有两个部分。第一部分是通过收集视图的宽度并使用大小调整单元格来计算单元格的高度,以使收集视图委托计算正确的单元格大小。
在您的UICollectionViewDelegateFlowLayout中,您可以实现以下方法:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
// NOTE: here is where we say we want cells to use the width of the collection view
let requiredWidth = collectionView.bounds.size.width
// NOTE: here is where we ask our sizing cell to compute what height it needs
let targetSize = CGSize(width: requiredWidth, height: 0)
/// NOTE: populate the sizing cell's contents so it can compute accurately
self.sizingCell.label.text = items[indexPath.row]
let adequateSize = self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
return adequateSize
}
这将导致收集视图根据封闭的收集视图来设置单元格的宽度,但随后请调整大小的单元格计算高度。
第二部分是使尺寸调整单元使用其自身的AL约束来计算高度。由于多行UILabel有效地需要两阶段布局过程,因此这可能比应该做的难。工作是在method中完成的preferredLayoutSizeFittingSize
,就像这样:
/*
Computes the size the cell will need to be to fit within targetSize.
targetSize should be used to pass in a width.
the returned size will have the same width, and the height which is
calculated by Auto Layout so that the contents of the cell (i.e., text in the label)
can fit within that width.
*/
func preferredLayoutSizeFittingSize(targetSize:CGSize) -> CGSize {
// save original frame and preferredMaxLayoutWidth
let originalFrame = self.frame
let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
// assert: targetSize.width has the required width of the cell
// step1: set the cell.frame to use that width
var frame = self.frame
frame.size = targetSize
self.frame = frame
// step2: layout the cell
self.setNeedsLayout()
self.layoutIfNeeded()
self.label.preferredMaxLayoutWidth = self.label.bounds.size.width
// assert: the label's bounds and preferredMaxLayoutWidth are set to the width required by the cell's width
// step3: compute how tall the cell needs to be
// this causes the cell to compute the height it needs, which it does by asking the
// label what height it needs to wrap within its current bounds (which we just set).
let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// assert: computedSize has the needed height for the cell
// Apple: "Only consider the height for cells, because the contentView isn't anchored correctly sometimes."
let newSize = CGSize(width:targetSize.width,height:computedSize.height)
// restore old frame and preferredMaxLayoutWidth
self.frame = originalFrame
self.label.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
return newSize
}
(此代码改编自WWDC2014会话“高级收藏夹视图”上的示例代码中的Apple示例代码。)
有几点需要注意。它使用layoutIfNeeded()强制整个单元格的布局,以便计算和设置标签的宽度。但这还不够。我相信您还需要进行设置,preferredMaxLayoutWidth
以便标签将在“自动版式”中使用该宽度。然后只有systemLayoutSizeFittingSize
在考虑标签的情况下,才能使用该单元来计算单元的高度。
我喜欢这种方法吗?没有!!感觉太复杂了,它做了两次布局。但是,只要性能不成问题,我宁愿在运行时执行两次布局,而不必在代码中定义两次,这似乎是唯一的选择。
我的希望是,最终自动调整大小的单元将以不同的方式工作,而这一切将变得更加简单。
示例项目在工作中显示它。
但是,为什么不只使用自动调整大小的单元呢?
从理论上讲,iOS8的“自定义单元格”的新功能应使其不必要。如果您使用自动布局(AL)定义了一个单元格,则该集合视图应该足够聪明,以使其能够自行调整大小并正确地进行布局。在实践中,我还没有看到任何示例可将其用于多行标签。我认为这部分是因为自调整大小的细胞机制仍是越野车。
但我敢打赌,这主要是因为“自动布局”和标签通常很棘手,这就是UILabel需要一个基本的两步布局过程。对我而言,目前尚不清楚如何使用自定义大小的单元格执行两个步骤。
就像我说的,这确实是另一种布局的工作。流布局本质的一部分是放置具有大小的对象,而不是固定宽度并让他们选择高度。
那么,preferredLayoutAttributesFittingAttributes:呢?
preferredLayoutAttributesFittingAttributes:
我认为该方法是一种红色鲱鱼。只有新的自动调整大小单元机制才能使用该功能。因此,只要该机制不可靠,这不是答案。
以及systemlayoutSizeFittingSize:是怎么回事?
您是对的,文档令人困惑。
上Docs systemLayoutSizeFittingSize:
和systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
两个建议,你应该只通过UILayoutFittingCompressedSize
与UILayoutFittingExpandedSize
作为targetSize
。但是,方法签名本身,标题注释和功能的行为表明它们正在响应targetSize
参数的确切值。
实际上,如果设置UICollectionViewFlowLayoutDelegate.estimatedItemSize
,以启用新的自动调整大小的单元机制,则该值似乎作为targetSize传入。并且UILabel.systemLayoutSizeFittingSize
似乎返回与完全相同的值UILabel.sizeThatFits
。考虑到to的参数systemLayoutSizeFittingSize
应该是一个粗略的目标,而to的参数sizeThatFits:
应该是最大的限制范围,因此这是可疑的。
更多资源
而可悲的是认为这样的常规要求应要求“的科研资源”,我认为它。很好的例子和讨论是: