【发布时间】:2018-02-01 10:18:54
【问题描述】:
我在UICollectionView 自定义布局实现过程中遇到了问题。
问题是我需要在自定义布局的prepare() 中计算集合视图单元格的高度。为此,我有:
func heightForItem(_ collectionView: UICollectionView, at indexPath: IndexPath) -> CGFloat
在我的视图控制器中实现的自定义布局的委托协议中的方法。
但是,此方法是在单元格出列之前调用的,并且实际上有任何数据,我可以根据这些数据计算它的高度。因此,如果单元格的内容超出了它的初始范围 - 我看不到部分内容。
有没有人在自定义布局中遇到过同样的问题?你是怎么解决的?
自定义布局的协议:
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
}
}
协议的实现(来自视图控制器):
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
}
}
}
【问题讨论】:
-
请显示一些代码,以便我们更好地帮助您
-
@user1000 添加了有问题的协议、布局和协议实现
-
你试过 UICollectionViewDelegateFlowLayout 协议来计算项目大小吗?
-
@KrishnaKumar FlowLayout 不能满足我的布局需求,所以我不得不使用自定义
标签: ios swift layout collections view