chi*_*uda 21 uitableview ios uicollectionview swift
我将水平UICollectionView固定到的所有边缘UITableViewCell。集合视图中的项目是动态调整大小的,我想使表视图的高度等于最高的集合视图单元格的高度。
视图的结构如下:
UITableView
UITableViewCell
UICollectionView
UICollectionViewCell
UICollectionViewCell
UICollectionViewCell
class TableViewCell: UITableViewCell {
private lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: self.frame, collectionViewLayout: layout)
return collectionView
}()
var layout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 350, height: 20)
layout.scrollDirection = .horizontal
return layout
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let nib = UINib(nibName: String(describing: CollectionViewCell.self), bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: CollectionViewCellModel.identifier)
addSubview(collectionView)
// (using SnapKit)
collectionView.snp.makeConstraints { (make) in
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
}
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
collectionView.layoutIfNeeded()
collectionView.frame = CGRect(x: 0, y: 0, width: targetSize.width , height: 1)
return layout.collectionViewContentSize
}
override func layoutSubviews() {
super.layoutSubviews()
let spacing: CGFloat = 50
layout.sectionInset = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing)
layout.minimumLineSpacing = spacing
}
}
class OptionsCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
contentView.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
return contentView.systemLayoutSizeFitting(CGSize(width: targetSize.width, height: 1))
}
}
class CollectionViewFlowLayout: UICollectionViewFlowLayout {
private var contentHeight: CGFloat = 500
private var contentWidth: CGFloat = 200
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
if let attrs = super.layoutAttributesForElements(in: rect) {
// perform some logic
contentHeight = /* something */
contentWidth = /* something */
return attrs
}
return nil
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
}
Run Code Online (Sandbox Code Playgroud)
在我身上UIViewController,我有tableView.estimatedRowHeight = 500和tableView.rowHeight = UITableView.automaticDimension。
我正在尝试使表格视图单元格的大小systemLayoutSizeFitting等于collectionViewContentSize,但是在布局有机会将其设置collectionViewContentSize为正确值之前调用它。这些方法的调用顺序如下:
cellForRowAt
CollectionViewFlowLayout collectionViewContentSize: (200.0, 500.0) << incorrect/original value
**OptionsTableViewCell systemLayoutSizeFitting (200.0, 500.0) << incorrect/original value**
CollectionViewFlowLayout collectionViewContentSize: (200.0, 500.0) << incorrect/original value
willDisplay cell
CollectionViewFlowLayout collectionViewContentSize: (200.0, 500.0) << incorrect/original value
TableViewCell layoutSubviews() collectionViewContentSize.height: 500.0 << incorrect/original value
CollectionViewFlowLayout collectionViewContentSize: (200.0, 500.0) << incorrect/original value
CollectionViewFlowLayout layoutAttributesForElements
CollectionViewFlowLayout collectionViewContentSize: (450.0, 20.0)
CollectionViewFlowLayout layoutAttributesForElements
CollectionViewFlowLayout collectionViewContentSize: (450.0, 20.0)
CollectionViewCell systemLayoutSizeFitting
CollectionViewCell systemLayoutSizeFitting
CollectionViewCell systemLayoutSizeFitting
CollectionViewFlowLayout collectionViewContentSize: (450.0, 20.0)
CollectionViewFlowLayout layoutAttributesForElements
CollectionViewFlowLayout collectionViewContentSize: (450.0, 301.0) << correct size
CollectionViewFlowLayout collectionViewContentSize: (450.0, 301.0) << correct size
TableViewCell layoutSubviews() collectionViewContentSize.height: 301.0 << correct height
CollectionViewFlowLayout layoutAttributesForElements
CollectionViewFlowLayout collectionViewContentSize: (450.0, 301.0) << correct size
TableViewCell layoutSubviews() collectionViewContentSize.height: 301.0 << correct height
Run Code Online (Sandbox Code Playgroud)
如何使表格视图单元格的高度等于集合视图的最高项目(由布局的代表)的高度collectionViewContentSize?
Moj*_*ini 17
有一些步骤可以实现此目的:
UICollectionViewCellUICollectionViewUITableViewCellbounds更改的高阶视图UICollectionViewCell您在这里有两个选择:-对自动调整尺寸的UI元素(如UIImageViewor UILabel或or等)使用自动布局-计算并提供您自己的尺寸
- 将自动布局与自定义界面元素一起使用:
如果使用自定尺寸的UI元素正确设置estimatedItemsSizebe automatic和布局单元格子视图,它将自动进行实时定尺寸。但是要注意昂贵的布局时间和滞后,因为布局复杂。特别是在较旧的设备上。
- 计算并提供您自己的尺寸:
可以设置像元大小的最佳位置之一是intrinsicContentSize。尽管还有其他地方可以执行此操作(例如,在flowLayout类等中),但是可以保证单元格不会与其他自定义UI元素发生冲突:
override open var intrinsicContentSize: CGSize { return /*CGSize you prefered*/ }
Run Code Online (Sandbox Code Playgroud)
UICollectionView所有UIScrollView子类可以selfsizing二人到它的contentSize。由于collectionView和tableView内容的大小在不断变化,因此您应该通知layouter(官方在任何地方都不称为布局器):
protocol CollectionViewSelfSizingDelegate: class {
func collectionView(_ collectionView: UICollectionView, didChageContentSizeFrom oldSize: CGSize, to newSize: CGSize)
}
class SelfSizingCollectionView: UICollectionView {
weak var selfSizingDelegate: CollectionViewSelfSizingDelegate?
override open var intrinsicContentSize: CGSize {
return contentSize
}
override func awakeFromNib() {
super.awakeFromNib()
if let floawLayout = collectionViewLayout as? UICollectionViewFlowLayout {
floawLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
} else {
assertionFailure("Collection view layout is not flow layout. SelfSizing may not work")
}
}
override open var contentSize: CGSize {
didSet {
guard bounds.size.height != contentSize.height else { return }
frame.size.height = contentSize.height
invalidateIntrinsicContentSize()
selfSizingDelegate?.collectionView(self, didChageContentSizeFrom: oldValue, to: contentSize)
}
}
}
Run Code Online (Sandbox Code Playgroud)
因此,在覆盖之后,intrinsicContentSize我们观察contentSize的变化,并告知selfSizingDelegate需要对此进行了解。
UITableViewCell通过在以下单元中实现此单元,可以自定义大小tableView:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Run Code Online (Sandbox Code Playgroud)
我们将selfSizingCollectionView在此单元格中放置一个并实现它:
class SelfSizingTableViewCell: UITableViewCell {
@IBOutlet weak var collectionView: SelfSizingCollectionView!
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
return collectionView.frame.size
}
override func awakeFromNib() {
...
collectionView.selfSizingDelegate = self
}
}
Run Code Online (Sandbox Code Playgroud)
因此,我们符合委托并实现更新单元格的功能。
extension SelfSizingTableViewCell: CollectionViewSelfSizingDelegate {
func collectionView(_ collectionView: UICollectionView, didChageContentSizeFrom oldSize: CGSize, to newSize: CGSize) {
if let indexPath = indexPath {
tableView?.reloadRows(at: [indexPath], with: .none)
} else {
tableView?.reloadData()
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我为UIView和UITableViewCell访问父表视图编写了一些帮助程序扩展:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
extension UITableViewCell {
var tableView: UITableView? {
return (next as? UITableView) ?? (parentViewController as? UITableViewController)?.tableView
}
var indexPath: IndexPath? {
return tableView?.indexPath(for: self)
}
}
Run Code Online (Sandbox Code Playgroud)
至此,您的集合视图将具有恰好需要的高度(如果它是垂直的),但是您应该记住限制所有单元格的宽度,以免超出屏幕宽度。
extension SelfSizingTableViewCell: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width, height: heights[indexPath.row])
}
}
Run Code Online (Sandbox Code Playgroud)
通知heights吗?在这里,您应该缓存(手动/自动)所有单元格的计算高度,以免延迟。(出于测试目的,我会随机生成所有这些文件)
已经涵盖了最后两个步骤,但是您应该考虑有关所寻求案例的一些重要事项:
1-如果您要求自动布局为您计算所有单元格大小,它可能是史诗般的性能杀手,但这是可靠的。
2-您必须缓存所有大小并获得最高的大小并将其返回tableView(:,heightForRowAt:)(不再需要调整大小tableViewCell)
3-考虑到最高的可能比整个高,tableView而2D滚动会很奇怪。
4-如果您重复使用包含collectionView或的单元格tableView,则滚动偏移量,插入量和许多其他属性的行为会与您期望的方式不同。
这是您可能遇到的最复杂的布局之一,但是一旦习惯了它,它就会变得很简单。
我设法通过CollectionView的高度约束参考(不过我不使用SnapKit,也不使用programaticUI)来实现您想要的功能。
这是我的做法,可能会有所帮助:
NSLayoutConstraint为UICollectionView 注册了一个,该UICollectionView已经有一个topAnchor和bottomAnchor等于其超级视图class ChatButtonsTableViewCell: UITableViewCell {
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var buttonsCollectionView: UICollectionView!
@IBOutlet weak var buttonsCollectionViewHeightConstraint: NSLayoutConstraint! // reference
}
Run Code Online (Sandbox Code Playgroud)
然后在我的UITableViewDelegate实现中,在cellForRowAt上,将高度约束的常量设置为与实际的collectionView框架相同,如下所示:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let item = items[indexPath.section]
let item = groupedItems[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: ChatButtonsTableViewCell.identifier, for: indexPath) as! ChatButtonsTableViewCell
cell.chatMessage = nil // reusability
cell.buttonsCollectionView.isUserInteractionEnabled = true
cell.buttonsCollectionView.reloadData() // load actual content of embedded CollectionView
cell.chatMessage = groupedItems[indexPath.section][indexPath.row] as? ChatMessageViewModelChoicesItem
// >>> Compute actual height
cell.layoutIfNeeded()
let height: CGFloat = cell.buttonsCollectionView.collectionViewLayout.collectionViewContentSize.height
cell.buttonsCollectionViewHeightConstraint.constant = height
// <<<
return cell
}
Run Code Online (Sandbox Code Playgroud)我的假设是,您可以使用此方法将collectionView的高度设置为最大单元格的高度,let height: CGFloat = cell.buttonsCollectionView.collectionViewLayout.collectionViewContentSize.height而不是所需的实际高度。
另外,如果您使用的是水平UICollectionView,考虑到contentSize.height将等于您的最大单元格高度,则您甚至不必更改此行。
它对我来说完美无缺,希望对您有所帮助
编辑
只是在想,你可能需要放置一些layoutIfNeeded()与invalidateLayout()在这里和那里。如果无法立即使用,我可以告诉您我将其放在哪里。
编辑2
还忘了提到我实现了willDisplay和estimatedHeightForRowAt。
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let buttonCell = cell as? ChatButtonsTableViewCell {
cellHeights[indexPath] = buttonCell.buttonsCollectionView.collectionViewLayout.collectionViewContentSize.height
} else {
cellHeights[indexPath] = cell.frame.size.height
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath] ?? 70.0
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
925 次 |
| 最近记录: |