gpt4 book ai didi

ios - 使用自定义布局的 UICollectionViewCell 高度

转载 作者:行者123 更新时间:2023-11-28 08:03:34 24 4
gpt4 key购买 nike

我在 UICollectionView 自定义布局实现期间遇到了问题。

问题是我需要在自定义布局的 prepare() 中计算 Collection View 单元格的高度。为此,我有:

func heightForItem(_ collectionView: UICollectionView, at indexPath: IndexPath) -> CGFloat 

在我的 View Controller 中实现的自定义布局委托(delegate)协议(protocol)中的方法。

然而,此方法在单元格出列之前调用,并且实际上有任何数据,我可以根据这些数据计算它的高度。因此,如果单元格的内容超出了它的初始范围——我看不到部分内容。

有没有人遇到过自定义布局的同样问题?你是怎么解决的?

自定义布局协议(protocol):

protocol CustomLayoutDelegateProtocol: class {
func numberOfSectionsInRow() -> Int
func indecesOfSectionsInRow() -> [Int]
func minimumInteritemSpace() -> CGFloat
func heightForItem(_ collectionView: UICollectionView, at indexPath: IndexPath) -> CGFloat
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
}

自定义布局本身:

class CustomLayoutClass: UICollectionViewLayout {

weak var delegate: CustomLayoutDelegateProtocol? {
didSet {
setupLayout()
}
}

private var cache = [UICollectionViewLayoutAttributes]()
private var contentHeight: CGFloat = 0.0
private var contentWidth: CGFloat {
guard let collectionView = collectionView else { return 0 }
return collectionView.bounds.width
}
private var interitemSpace: CGFloat?
private var numberOfColumns: Int?
private var columnedSections: [Int]?


override init() {
super.init()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

private func setupLayout() {
numberOfColumns = delegate?.numberOfSectionsInRow()
columnedSections = delegate?.indecesOfSectionsInRow()
interitemSpace = delegate?.minimumInteritemSpace()
}

override func invalidateLayout() {
cache.removeAll()
super.invalidateLayout()
}

override func prepare() {
if cache.isEmpty {
guard let collectionView = collectionView,
let numberOfColumns = numberOfColumns,
let columnedSections = columnedSections,
let interitemSpace = interitemSpace else { return }

let columnWidth = (contentWidth / CGFloat(numberOfColumns))
var xOffset = [CGFloat]()
for column in 0..<numberOfColumns {
var interitemSpace = interitemSpace
if column == 0 { interitemSpace = 0 }
xOffset.append(CGFloat(column) * columnWidth + interitemSpace)
}

var yOffset: CGFloat = 0.0

for section in 0..<collectionView.numberOfSections {
for item in 0..<collectionView.numberOfItems(inSection: section) {
let indexPath = IndexPath(item: item, section: section)
guard let sectionInsets = delegate?.collectionView(collectionView, layout: self, insetForSectionAt: indexPath.section),
let height = delegate?.heightForItem(collectionView, at: indexPath) else { continue }
let width = columnedSections.contains(section) ? columnWidth : contentWidth

let xOffsetIdx = columnedSections.contains(section) ? columnedSections.index(of: section)! % numberOfColumns : 0

yOffset += sectionInsets.top
let frame = CGRect(x: xOffset[xOffsetIdx], y: yOffset, width: width, height: height)

let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
cache.append(attributes)

contentHeight = max(contentHeight, frame.maxY)

let isLastInRow = (columnedSections.contains(section) && columnedSections.index(of: section)! % numberOfColumns == (numberOfColumns-1))
let isNotColumnedSection = !columnedSections.contains(section)

if isLastInRow || isNotColumnedSection {
yOffset += height + sectionInsets.bottom
}
}
}
}
}

override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()

for attributes in cache {
if attributes.frame.intersects(rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
}

以及协议(protocol)的实现(来自 View Controller ):

extension ViewController: CustomLayoutDelegateProtocol {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
guard let sectionType = InvoiceSectionIndexType(rawValue: section) else { return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) }

switch sectionType {
case .receiver:
return UIEdgeInsets(top: Constants.bigLineSpace, left: 0, bottom: Constants.bigLineSpace, right: 0)

default:
return UIEdgeInsets(top: 0, left: 0, bottom: Constants.commonLineSpace, right: 0)
}
}

func numberOfSectionsInRow() -> Int {
return Constants.numberOfSectionsInRow
}

func indecesOfSectionsInRow() -> [Int] {
return [0, 1]
}

func minimumInteritemSpace() -> CGFloat {
return Constants.interitemSpace
}

func heightForItem(_ collectionView: UICollectionView, at indexPath: IndexPath) -> CGFloat {
guard let sectionType = InvoiceSectionIndexType(rawValue: indexPath.section) else { return 0 }

switch sectionType {
case .next:
return Constants.nextButtonHeight

default:
return Constants.estimatedRowHeight
}
}
}

最佳答案

我自己解决了这个问题,虽然不是很明显,但解决方案很简单。

因为我们有具体单元格的 indexPath,我们可以找到我们的内容(在我的例子中,内容是放在标签中的简单文本)。然后我们可以创建宽度(在我的例子中我知道单元格的宽度)和高度为 CGFloat.greatestFiniteMagnitude 的标签(我们不会显示它,因为它仅用于计算)。然后我们将 sizeToFit() 应用到我们的标签,现在我们有了合适高度的标签。我们现在应该做的唯一一件事 - 将这个新高度应用于我们的单元格。

示例代码:

func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat {
// ask your DataSource for piece of data with indexPath

let testLabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
testLabel.text = // your text
testLabel.sizeToFit()

if testLabel.bounds.height > Constants.defaultContentLabelHeight {
return Constants.estimatedRowHeight + (testLabel.bounds.height - Constants.defaultContentLabelHeight)
} else {
return Constants.estimatedRowHeight
}
}

关于ios - 使用自定义布局的 UICollectionViewCell 高度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45846260/

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