gpt4 book ai didi

ios - 动态高度 UITableViewCell 内的动态高度 UICollectionView

转载 作者:IT王子 更新时间:2023-10-29 05:11:16 30 4
gpt4 key购买 nike

我有一个水平的 UICollectionView 固定到 UITableViewCell 的所有边缘。 Collection View 中的项目是动态调整大小的,我想让 TableView 的高度等于最高的 Collection View 单元格的高度。

View 的结构如下:

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)
}
}

在我的 UIViewController 中,我有 tableView.estimatedRowHeight = 500tableView.rowHeight = UITableView.automaticDimension

我试图使 systemLayoutSizeFitting 中的表格 View 单元格的大小等于 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

如何使我的表格 View 单元格的高度等于我的 Collection View 的最高项目,由布局的 collectionViewContentSize 表示?

最佳答案

有一些步骤可以实现这一点:

  • 定义自调整大小 UICollectionViewCell
  • 定义自调整大小 UICollectionView
  • 定义自调整大小 UITableViewCell
  • 计算所有像元大小并找到最高的(可能很昂贵)
  • 重新加载任何 child 的 bounds 更改的高阶 View

- 定义自调整大小 UICollectionViewCell

这里有两个选择:- 将自动布局与自调整 UI 元素(如 UIImageViewUILabel 等)结合使用- 计算并提供您自己的尺寸

-将自动布局与自调整 UI 元素结合使用:

如果您将 estimatedItemsSize 设置为 automatic 并使用自调整 UI 元素正确布局单元格 subview ,它将在运行时自动调整大小。 请注意由于复杂的布局而导致的昂贵的布局时间和滞后。特别是在旧设备上。

-计算并提供您自己的尺寸:

您可以设置单元格大小的最佳位置之一是 intrinsicContentSize。尽管还有其他地方可以执行此操作(例如在 flowLayout 类等中),但这是您可以保证单元格不会与其他自调整 UI 元素冲突的地方:

override open var intrinsicContentSize: CGSize { return /*CGSize you prefered*/ }

- 定义自调整大小 UICollectionView

任何 UIScrollView 子类都可以根据它的 contentSize 自调整大小。由于 collectionView 和 tableView 的内容大小不断变化,您应该通知 layouter(在任何地方都正式称为 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)
}
}
}

因此,在重写 intrinsicContentSize 之后,我们会观察 contentSize 的变化,并通知需要了解这一点的 selfSizingDelegate

- 定义自调整大小 UITableViewCell

这个单元格可以通过在它们的 tableView 中实现来自动调整大小:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}

我们将在这个单元格中放置一个 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
}
}

所以我们符合委托(delegate)并实现更新单元格的功能。

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()
}
}
}

请注意,我为 UIViewUITableViewCell 编写了一些辅助扩展来访问父 TableView :

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)
}
}

至此,您的 Collection View 将具有完全需要的高度(如果它是垂直的),但您应该记住限制所有单元格的宽度,以免超出屏幕宽度。

extension SelfSizingTableViewCell: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width, height: heights[indexPath.row])
}
}

注意 heights 了吗?这是您应该缓存(手动/自动)所有单元格的计算高度的地方,以免变得迟钝。 (出于测试目的,我随机生成了所有这些)

- 最后两步:

最后两个步骤已经涵盖,但您应该考虑有关您寻求的案例的一些重要事项:

1 - 如果您要求 Autolayout 为您计算所有单元格大小,Autolayout 可能会成为史诗般的性能 killer ,但它是可靠的。

2 - 您必须缓存所有尺寸并获取最高尺寸并将其返回到 tableView(:,heightForRowAt:)(不再需要自调整 tableViewCell)

3 - 考虑到最高的那个可能比整个 tableView 还高,2D 滚动会很奇怪。

4 - 如果您重复使用包含 collectionViewtableView 的单元格,则滚动偏移、插入和许多其他属性的行为将以您不期望的方式出现。

这是您可能遇到的所有布局中最复杂的布局之一,但是一旦您习惯了它,它就会变得简单。

关于ios - 动态高度 UITableViewCell 内的动态高度 UICollectionView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56318626/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com