Yappli で iOS エンジニアをしている三宅です!
Yappli では CMS を利用してアプリをノーコードで作成できるアプリプラットフォームの開発を行っています。そんなプラットフォームは動的に UI を構築する特性から複雑なレイアウトを組み立てる処理がいくつもあります。
複雑なレイアウトを組み立てるのに一部の機能では Compositional Layout を採用して実装している箇所がありますが、DecorationView を設定するときにも CMS から取得した Appearance 情報を動的に適用したくなります。
CollectionView の DecorationView は SupplementaryView のように参照を取得することができなさそうなので、工夫しつつ実現可能か試してみたいと思います。
DecorationView は UICollectionReusableView を継承して実装し、 apply(_ layoutAttributes:)
メソッドを override してカスタムした UICollectionViewLayoutAttributes を受け取るようにしてみます。
カスタムの UICollectionViewLayoutAttributes は、UICollectionViewCompositionalLayout を継承したクラスを定義して layoutAttributesForElements(rect:)
で設定する形で実装をしていこうと思います💪🏻
セクションを用意して書くほどの内容ではないですが、Appearance 情報を持つ UICollectionViewLayoutAttribute を定義していきます。
final class DecoratableLayoutAttributes: UICollectionViewLayoutAttributes {
private var appearance: Appearance?
}
次に UICollectionViewCompositionalLayout を継承して layoutAttributesForElements(rect:)
メソッドを override し、 DecoratableLayoutAttributes を設定していきます。
final class DecoratableCompositionalLayout: UICollectionViewCompositionalLayout {
private let appearances: [Appearance]
init(appearances: [Appearance]) {
self.appearances = appearances
}
...
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)
return layoutAttributes?.map { [weak self] attribute in
var decoratableAttribute = DecoratableLayoutAttributes()
decoratableAttribute.appearance = self?.appearances[attribute.indexPath.section]
...
return decoratableAttribute
}
}
}
DecorationView で apply(_ layoutAttributes:)
を override して DecoratableLayoutAttributes の参照を取得します。
final class BlockBackgroundDecoration: UICollectionReusableView {
...
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
if let attribute = layoutAttributes as? DecoratableLayoutAttributes,
let appearance = attribute.appearance {
// ここで受け取った Apperance を DecorationView へ適用する
}
}
}
NSCollectionLayoutSection には DecorationView を設定する decorationItems
というプロパティがあります。このプロパティに NSCollectionLayoutDecorationItem
を設定することで、指定した elementKind の DecorationView を利用してくれます。
let section = NSCollectionLayoutSection(group: group)
section.decorationItems = [
.background(elementKind: elementKind)
]
また UICollectionView にも利用する DecorationView のクラスと elementKind を登録しておきます。