
一部のアプリケーションでは、図のようにコレクションビューの背景イメージや色で異なるパーティションを設定する必要があります。特にeコマースプラットフォームカテゴリのアプリケーションはそうです。しかし、コレクションビュー自体は背景イメージを直接設定することをサポートしていません、いくつかの研究の後、あなたはこの効果を達成するためにUICollectionViewFlowLayoutを書き換えることができます。

背景Viewを装飾するセクションを登録するカスタムUICollectionReusableViewクラス
class SectionBackgroundReusableView: UICollectionReusableView {
static let BACKGAROUND_CID = "BACKGAROUND_CID"
private lazy var bg_imageView = UIImageView().then {
addSubview($0)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
bg_imageView.frame = bounds
guard let att = layoutAttributes as? SectionDecorationViewCollectionViewLayoutAttributes else {
return
}
self.backgroundColor = UIColor.clear
bg_imageView.layer.cornerRadius = 5
bg_imageView.clipsToBounds = true
bg_imageView.backgroundColor = att.backgroundColor
guard let imageName = att.imageName else {
self.bg_imageView.image = nil
return
}
guard let image_url = URL(string: imageName) else {
return
}
self.bg_imageView.kf.setImage(with: image_url)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class SectionDecorationViewCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
// 背景イメージを飾る
var imageName: String?
// 背景色
var backgroundColor = UIColor.white
/// 定義された属性のタイプは、NSCopyingプロトコルに従う必要がある。
/// - Parameter zone:
/// - Returns:
override func copy(with zone: NSZone? = nil) -> Any {
let copy = super.copy(with: zone) as! SectionDecorationViewCollectionViewLayoutAttributes
copy.imageName = self.imageName
copy.backgroundColor = self.backgroundColor
return copy
}
/// 定義された属性の型も等しい判定方法を実装する必要がある。
/// - Parameter object:
/// - Returns: 等しいかどうか
override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? SectionDecorationViewCollectionViewLayoutAttributes else {
return false
}
if self.imageName != rhs.imageName {
return false
}
if !self.backgroundColor.isEqual(rhs.backgroundColor) {
return false
}
return super.isEqual(object)
}
}
- 最初にバックグラウンドビューを登録します。
override init() {
super.init()
// バックグラウンドビューの登録
self.register(SectionBackgroundReusableView.self, forDecorationViewOfKind: SectionBackgroundReusableView.BACKGAROUND_CID)
}
- 背景の位置とサイズの設定
// レイアウト設定データ
override func prepare() {
super.prepare()
// collectionViewが現在パーティション化されていない場合は、直接から
guard let numberOfSections = self.collectionView?.numberOfSections
else {
return
}
// を終了するcardDecorationDelegateが存在しない。
guard let delegate = decorationDelegate else {
return
}
if decorationBackgroundAttrs.count > 0 {
decorationBackgroundAttrs.removeAll()
}
for section:Int in 0..<numberOfSections {
// 最初の下のセクションだけでなく、最後の項目のプロパティのレイアウトを取得する
guard let numberOfItems = self.collectionView?.numberOfItems(inSection: section),
numberOfItems > 0,
let firstItem = self.layoutAttributesForItem(at:
IndexPath(item: 0, section: section)),
let lastItem = self.layoutAttributesForItem(at:
IndexPath(item: numberOfItems - 1, section: section))
else {
continue
}
var sectionInset:UIEdgeInsets = self.sectionInset
/// 内側のマージンのセクションを取得する
let inset:UIEdgeInsets = delegate.collectionView(collectionView: self.collectionView!, layout: self, insetForSectionAt: section)
if !(inset == .zero) {
sectionInset = inset
}
/// セクションヘッダーサイズを取得する
let headerSize = delegate.collectionView(collectionView: self.collectionView!, layout: self, headerForSectionAt: section)
var sectionFrame:CGRect = .zero
if self.scrollDirection == .horizontal {
let hx = (firstItem.frame.origin.x) - headerSize.width + sectionInset.left
let hy = (firstItem.frame.origin.y) + sectionInset.top
let hw = ((lastItem.frame.origin.x) + (lastItem.frame.size.width)) - sectionInset.right
let hh = ((lastItem.frame.origin.y) + (lastItem.frame.size.height)) - sectionInset.bottom
sectionFrame = CGRect(x: hx , y: hy, width: hw, height: hh)
sectionFrame.origin.y = sectionInset.top
sectionFrame.size.width = sectionFrame.size.width-sectionFrame.origin.x
sectionFrame.size.height = self.collectionView!.frame.size.height - sectionInset.top - sectionInset.bottom
} else {
let vx = (firstItem.frame.origin.x)
let vy = (firstItem.frame.origin.y) - headerSize.height + sectionInset.top
let vw = ((lastItem.frame.origin.x) + (lastItem.frame.size.width))
let vh = ( (lastItem.frame.origin.y) + (lastItem.frame.size.height) ) - sectionInset.bottom
sectionFrame = CGRect(x: vx , y: vy, width: vw, height: vh + 10)
sectionFrame.origin.x = sectionInset.left
sectionFrame.size.width = (self.collectionView?.frame.size.width)! - sectionInset.left - sectionInset.right
sectionFrame.size.height = sectionFrame.size.height - sectionFrame.origin.y
}
let attrs = SectionDecorationViewCollectionViewLayoutAttributes(forDecorationViewOfKind: SectionBackgroundReusableView.BACKGAROUND_CID, with: IndexPath(item: 0, section: section))
let backgroundColor = delegate.collectionView(self.collectionView!, layout: self, decorationColorForSectionAt: section)
attrs.frame = sectionFrame
attrs.zIndex = -1
attrs.backgroundColor = backgroundColor
/// 色を保存する優先順位
self.decorationBackgroundAttrs[section] = attrs
/// 背景イメージが表示されているかどうかを判断する不可視スキップ
let backgroundDisplayed = delegate.collectionView(self.collectionView!, layout: self, decorationImgaeDisplayedForSectionAt: section)
guard backgroundDisplayed == true else {
continue
}
/// nilの背景イメージの名前は、スキップする場合
guard let imageName = delegate.collectionView(self.collectionView!, layout: self, decorationImageForSectionAt: section) else {
continue
}
attrs.imageName = imageName
let displayedFillet = delegate.collectionView(self.collectionView!, layout: self, filletDisplayedForSectionAt: section)
guard displayedFillet == false else {
continue
}
// セクションのレイアウトプロパティを保存する
self.decorationBackgroundAttrs[section] = attrs
}
}
override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let section = indexPath.section
if elementKind == SectionBackgroundReusableView.BACKGAROUND_CID {
return self.decorationBackgroundAttrs[section]
}
return super.layoutAttributesForDecorationView(ofKind: elementKind,
at: indexPath)
}
- 重要なコードを説明するために行われている、あなたはこの効果を必要とすることが起こる場合は、コードを参照してダウンロードすることができますし、変更するには、独自のニーズと組み合わせることができます。コードのアドレスは、以前の
