CollectionView Headers & Footers (Swift 5, Xcode 12, 2020) - iOS Development
flowLayout
구현을 통해 아이템 간격, 셀 사이즈 조정configure
함수를 통해 헤더 및 푸터 데이터 바인딩
import UIKit
class CustomHeaderCollectionView: UICollectionReusableView {
static let identifier = "CustomHeaderCollectionView"
private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = bounds
}
private func setUI() {
backgroundColor = .systemPink
addSubview(imageView)
}
func configure(with image: UIImage) {
imageView.image = image
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CustomHeaderCollectionView.identifier, for: indexPath) as? CustomHeaderCollectionView else {
return UICollectionViewCell()
}
if let image = UIImage(named: "header") {
header.configure(with: image)
}
return header
} else {
guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CustomFooterCollectionView.identifier, for: indexPath) as? CustomFooterCollectionView else {
return UICollectionViewCell()
}
if let image = UIImage(named: "footer") {
footer.configure(with: image)
}
return footer
}
}
kind
에 따라 헤더/푸터 뷰를 캐스팅, 커스텀 뷰를 사용하는 코드import UIKit
class CollectionViewController: UIViewController {
private let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 1
layout.minimumInteritemSpacing = 1
layout.scrollDirection = .vertical
layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.identifier)
collectionView.register(CustomHeaderCollectionView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CustomHeaderCollectionView.identifier)
collectionView.register(CustomFooterCollectionView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: CustomFooterCollectionView.identifier)
collectionView.alwaysBounceVertical = true
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
setUI()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
private func setUI() {
view.backgroundColor = .systemBackground
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension CollectionViewController: UICollectionViewDelegate {
}
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.identifier, for: indexPath) as? CustomCollectionViewCell else {
return UICollectionViewCell()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CustomHeaderCollectionView.identifier, for: indexPath) as? CustomHeaderCollectionView else {
return UICollectionViewCell()
}
if let image = UIImage(named: "header") {
header.configure(with: image)
}
return header
} else {
guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CustomFooterCollectionView.identifier, for: indexPath) as? CustomFooterCollectionView else {
return UICollectionViewCell()
}
if let image = UIImage(named: "footer") {
footer.configure(with: image)
}
return footer
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if section == 1 {
return .zero
}
return CGSize(width: view.frame.size.width, height: view.frame.size.width / 2)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: view.frame.size.width, height: view.frame.size.width / 2)
}
}
extension CollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.size.width / 2
return CGSize(width: width - 3, height: width - 3 )
}
}
import UIKit
class CustomCollectionViewCell: UICollectionViewCell {
static let identifier = "CustomCollectionViewCell"
override init(frame: CGRect) {
super.init(frame: frame)
setUI()
}
override func layoutSubviews() {
super.layoutSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setUI() {
contentView.backgroundColor = .link
}
func configure(with model: String) {
}
}
import UIKit
class CustomHeaderCollectionView: UICollectionReusableView {
static let identifier = "CustomHeaderCollectionView"
private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = bounds
}
private func setUI() {
backgroundColor = .systemPink
addSubview(imageView)
}
func configure(with image: UIImage) {
imageView.image = image
}
}
import UIKit
class CustomFooterCollectionView: UICollectionReusableView {
static let identifier = "CustomFooterCollectionView"
private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = bounds
}
private func setUI() {
backgroundColor = .systemYellow
addSubview(imageView)
}
func configure(with image: UIImage) {
imageView.image = image
}
}
헤더 / 푸터 뷰를 사용하는 부분을 다시 한 번 체크!