diffable 数据源节标题在更新期间闪烁
Posted
技术标签:
【中文标题】diffable 数据源节标题在更新期间闪烁【英文标题】:diffable data source section header blinks during update 【发布时间】:2020-11-11 16:47:04 【问题描述】:我目前面临的问题是,当将新快照应用到我当前的数据源时,页眉、页脚和装饰视图不属于集合视图的子视图,这可能会导致奇怪的闪烁。以前有人遇到过这个问题吗?
我通过以下方式更新数据源:
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(Sections.allCases)
items.forEach snapshot.appendItems([$0], toSection: ItemSectionMapper.getSection(for: $0))
self.dataSource?.apply(snapshot)
编辑:
它似乎只发生在 ios 14 设备上。
编辑2: 这是示例项目中相同问题的屏幕记录: https://imgur.com/a/0rS9aZU
下面的代码:
import UIKit
// MARK: - Cell -
final class Cell: UICollectionViewCell
static let reuseIdentifier = "Cell"
var isExpanded = false
didSet label.numberOfLines = numberOfLines
var numberOfLines: Int isExpanded ? 0 : 3
lazy var label: UILabel =
let label = UILabel()
label.numberOfLines = numberOfLines
label.frame.size = contentView.bounds.size
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return label
()
override init(frame: CGRect)
super.init(frame: frame)
contentView.addSubview(label)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func sizeThatFits(_ size: CGSize) -> CGSize
label.sizeThatFits(size)
final class Header: UICollectionReusableView
static let elementKind = "Header"
lazy var label: UILabel =
let label = UILabel()
label.numberOfLines = 1
label.frame.size = bounds.size
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return label
()
override init(frame: CGRect)
super.init(frame: frame)
addSubview(label)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func sizeThatFits(_ size: CGSize) -> CGSize
label.sizeThatFits(size)
// MARK: - UIViewController -
class ViewController: UIViewController
struct Item: Hashable
let text: String
var isExpanded = false
private let uuid = UUID()
var items: [Item] = [
.init(
text: """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.
"""
),
.init(
text: """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.
""",
isExpanded: true
)
]
lazy var collectionView: UICollectionView =
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCollectionViewLayout())
collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.reuseIdentifier)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.contentInset.top = 44
collectionView.backgroundColor = .white
collectionView.delegate = self
return collectionView
()
lazy var dataSource = UICollectionViewDiffableDataSource<Int, Item>(collectionView: collectionView) collectionView, indexPath, itemIdentifier in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.reuseIdentifier, for: indexPath) as? Cell else fatalError()
cell.isExpanded = itemIdentifier.isExpanded
cell.label.text = itemIdentifier.text
return cell
override func viewDidLoad()
super.viewDidLoad()
dataSource.supplementaryViewProvider = (collectionView, kind, indexPath) -> UICollectionReusableView? in
guard let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: Header.elementKind, for: indexPath) as? Header else fatalError()
view.label.text = "Test"
return view
view.addSubview(collectionView)
collectionView.register(Header.self, forSupplementaryViewOfKind: Header.elementKind, withReuseIdentifier: Header.elementKind)
updateSnapshot()
private func createCollectionViewLayout() -> UICollectionViewCompositionalLayout
let layoutSize = NSCollectionLayoutSize.init(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(200)
)
let section = NSCollectionLayoutSection(group:
.vertical(
layoutSize: layoutSize,
subitems: [.init(layoutSize: layoutSize)]
)
)
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .estimated(20)), elementKind: Header.elementKind, alignment: .top)
section.boundarySupplementaryItems = [header]
section.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
section.interGroupSpacing = 20
return .init(section: section)
private func updateSnapshot()
var snapshot = NSDiffableDataSourceSnapshot<Int, Item>()
snapshot.appendSections([0])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
// MARK: - UICollectionViewDelegate -
extension ViewController: UICollectionViewDelegate
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
guard let itemIdentifier = dataSource.itemIdentifier(for: indexPath) else return
items[indexPath.row] = .init(text: itemIdentifier.text, isExpanded: !itemIdentifier.isExpanded)
updateSnapshot()
向@JWK致敬
【问题讨论】:
【参考方案1】:这种行为似乎出乎意料,尽管我相信它的发生仅仅是因为整个部分都在apply(_:animatingDifferences:completion:)
中使用动画进行了更新。您可以尝试一些解决方法:
-
在调用
apply(_:animatingDifferences:completion:)
时将animatingDifferences
设置为false
。如果您想要动画,则不理想。
添加另一个部分而不是使用boundarySupplementaryItems
。不展开的部分不应该在视觉上受到影响。您可能需要引入另一个单元格并为此使用UICollectionViewCompositionalLayout
的init(sectionProvider:)
(为每个部分提供正确的NSCollectionLayoutSection
)。
如果您使用的是 iOS 14+,我认为您可以通过将 UICollectionViewListCell
的 accessories
属性设置为 UICellAccessory.OutlineDisclosureOptions(style: .header)
来免费获得所需的行为。有一个示例项目以及其他有用的示例here。
【讨论】:
以上是关于diffable 数据源节标题在更新期间闪烁的主要内容,如果未能解决你的问题,请参考以下文章
iOS 13 上 TableView 错误的 Diffable 数据源:移动的关联不一致