【发布时间】:2018-08-22 13:58:15
【问题描述】:
我的数据集显示在 UICollectionView 中。数据集分为多个部分,每个部分都有一个标题。此外,每个单元格下方都有一个详细视图,单击该单元格时会展开该视图。
供参考:
为简单起见,我将详细信息单元格实现为默认隐藏的标准单元格(高度:0),当单击非详细信息单元格时,高度设置为非零值。这些单元格是使用invalidateItems(at indexPaths: [IndexPath]) 更新的,而不是在performBatchUpdates(_ updates: (() -> Void)?, completion: ((Bool) -> Void)? = nil) 中重新加载单元格,否则动画看起来会出现故障。
现在问题来了,invalidateItems 函数显然只更新单元格,而不是像节标题这样的补充视图,因此仅调用此函数将导致节标题溢出:
在谷歌上搜索了一段时间后,我发现要更新补充视图,必须致电invalidateSupplementaryElements(ofKind elementKind: String, at indexPaths: [IndexPath])。这可能会正确重新计算节标题的边界,但会导致内容不显示:
这很可能是因为func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView 似乎没有被调用。
如果有人能告诉我如何正确地使上述问题的补充意见无效,我将不胜感激。
代码:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return dataManager.getSectionCount()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let count = dataManager.getSectionItemCount(section: section)
reminder = count % itemsPerWidth
return count * 2
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if isDetailCell(indexPath: indexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Reusable.CELL_SERVICE, for: indexPath) as! ServiceCollectionViewCell
cell.lblName.text = "Americano detail"
cell.layer.borderWidth = 0.5
cell.layer.borderColor = UIColor(hexString: "#999999").cgColor
return cell
} else {
let item = indexPath.item > itemsPerWidth ? indexPath.item - (((indexPath.item / itemsPerWidth) / 2) * itemsPerWidth) : indexPath.item
let product = dataManager.getItem(index: item, section: indexPath.section)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Reusable.CELL_SERVICE, for: indexPath) as! ServiceCollectionViewCell
cell.lblName.text = product.name
cell.layer.borderWidth = 0.5
cell.layer.borderColor = UIColor(hexString: "#999999").cgColor
return cell
}
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
if indexPath.section == 0 {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: Reusable.CELL_SERVICE_HEADER_ROOT, for: indexPath) as! ServiceCollectionViewHeaderRoot
header.lblCategoryName.text = "Section Header"
header.imgCategoryBackground.af_imageDownloader = imageDownloader
header.imgCategoryBackground.af_setImage(withURLRequest: ImageHelper.getURL(file: category.backgroundFile!))
return header
} else {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: Reusable.CELL_SERVICE_HEADER, for: indexPath) as! ServiceCollectionViewHeader
header.lblCategoryName.text = "Section Header"
return header
}
default:
assert(false, "Unexpected element kind")
}
}
// MARK: UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width / CGFloat(itemsPerWidth)
if isDetailCell(indexPath: indexPath) {
if expandedCell == indexPath {
return CGSize(width: collectionView.frame.size.width, height: width)
} else {
return CGSize(width: collectionView.frame.size.width, height: 0)
}
} else {
return CGSize(width: width, height: width)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if section == 0 {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height / 3)
} else {
return CGSize(width: collectionView.frame.width, height: heightHeader)
}
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if isDetailCell(indexPath: indexPath) {
return
}
var offset = itemsPerWidth
if isLastRow(indexPath: indexPath) {
offset = reminder
}
let detailPath = IndexPath(item: indexPath.item + offset, section: indexPath.section)
let context = UICollectionViewFlowLayoutInvalidationContext()
let maxItem = collectionView.numberOfItems(inSection: 0) - 1
var minItem = detailPath.item
if let expandedCell = expandedCell {
minItem = min(minItem, expandedCell.item)
}
// TODO: optimize this
var cellIndexPaths = (0 ... maxItem).map { IndexPath(item: $0, section: 0) }
var supplementaryIndexPaths = (0..<collectionView.numberOfSections).map { IndexPath(item: 0, section: $0)}
for i in indexPath.section..<collectionView.numberOfSections {
cellIndexPaths.append(contentsOf: (0 ... collectionView.numberOfItems(inSection: i) - 1).map { IndexPath(item: $0, section: i) })
//supplementaryIndexPaths.append(IndexPath(item: 0, section: i))
}
context.invalidateSupplementaryElements(ofKind: UICollectionElementKindSectionHeader, at: supplementaryIndexPaths)
context.invalidateItems(at: cellIndexPaths)
if detailPath == expandedCell {
expandedCell = nil
} else {
expandedCell = detailPath
}
UIView.animate(withDuration: 0.25) {
collectionView.collectionViewLayout.invalidateLayout(with: context)
collectionView.layoutIfNeeded()
}
}
编辑: 演示此问题的简约项目:https://github.com/vongrad/so-expandable-collectionview
【问题讨论】:
-
直接简述你在哪里卡住了。以及你尝试了什么
-
为什么不使用collectionView.reloadSections(:)?
-
我开始重新加载单个项目以及重新加载整个部分(如 @sj-r 所建议的那样),但这导致动画远非流畅。因此,我转到了失效上下文,并且几乎尝试了我的问题中提到的事情。
-
尝试调用[supplementaryView setNeedsDisplay]。
-
我已经试过了,但是没用
标签: ios swift uicollectionview