μΌλ¨ μμνκΈ° μμβ¦ μ΄μμ μ ν루νκ³ λ μ λ°μ μμμ μ»μ΄λΈ κ²°κ³Όμ λλ€.
κ·Έλ§νΌ μν μ°©μ€κ° μ λ§ λ§μκ³ λ¨Έλ¦¬λ μνκ³ .. γ μλ¬΄νΌ κ·Έ κ³Όμ λ€μ μκ° ν κΉ ν©λλ€.
νν°λ μ€νΈ UI κ° λ¬΄μμ΄λλ©΄
μμκ°μ μ¬μ§μ²λΌ μ¬μ§μ ν¬κΈ°μ λ°λΌ
UIκ° κ²°μ λλ μ ννλ λ μ΄μμμ΄ μ ν΄μ§λ UIλ₯Ό νν°λ μ€νΈ UIλΌκ³ ν©λλ€.
λ€λ₯Έ μ©μ΄λ‘λMasonry
λ μ΄μμ μ΄λΌκ³ λ ν©λλ€.
κ²°λ‘ λΆν° λ§μ λ리μλ©΄ μ λ μ»΄ν¬μ§μ λλ‘λ μ€ν¨νμ΅λλ€.
μ΄μ κ° κΈ°λ³Έμ μΈ μ»΄ν¬μ§μ λ λ μ΄μμ μ½λλ₯Ό 보며 μ€λͺ νκ² μ΅λλ€.
func createBasicListLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
μμκ°μ΄ μ»΄ν¬μ§μ λ λ μ΄μμμ ꡬμ±νκ² λ ν λ°
μ 보μλ©΄ μ΄κ² νλ νλ μκ² μ£Όλ λ°©μμ΄ μλλΌ
μ 체μ μΌλ‘ μ΄λ΄κ±°μΌ~ λΌλ λ μ΄μμ ꡬμ±λ°©μμ΄λΌ
νλνλμ λΉμ¨μ μ μ© μν€κΈ°μλ λ¬΄λ¦¬κ° μμλ€κ³ μκ°ν©λλ€.
- 4/29 μΆκ°μ¬ν -> μ»΄ν¬μ§μ λλ‘λ ꡬνμ΄ κ°λ₯ν μ 보λ₯Ό μκ²λμ΄ νμ μλ‘μ΄ κΈλ‘ μ λ°μ΄νΈ νκ² μ΅λλ€.
μ΅μ’ μ μΌλ‘λ νλ‘μ° λ μ΄μμμ΄κΈ΄ νλ κ·Έκ³Όμ μ μκ°ν©λλ€.
extension UserProfileViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// λͺ¨λΈ....
let model = viewModel.realModel[indexPath.row]
// λΉμ¨
let asecpct = CGFloat(Double(model.content3) ?? 1)
print("μλμ νλ? \(collectionView.bounds.width)")
// 컬λ μ
λ·° μ 체
let totalWidth = collectionView.bounds.width
let spacing: CGFloat = 5
let itemsRow: CGFloat = 2
let estWidth = totalWidth - (spacing * (itemsRow + 1))
let cellWidth = estWidth / itemsRow
let cellHeight = cellWidth / asecpct
return CGSize(width: cellWidth, height: cellHeight)
}
}
μμ μ½λλ₯Ό 보μλ©΄
content3
κ° λ³΄μ΄μ€ ν λ° μ κ° μλ²μ
μλμ μ΄λ―Έμ§ λΉμ¨μ μ μ‘μ νκ³ κ·Έ κ°μ λ€μ λ°μμ¬λ μ΄λ―Έμ§ λΉμ¨μ κ³μ°ν νμμμ΄
λ°λ‘ μ μ©ν μ μκ² (μλ²μμ λλ μ΄ μ΅μν) νμμ΅λλ€.
νμ§λ§ μ’μ° μ¬λ°±μ μκ° νμ§ λͺ»ν΄μ μΌμλ‘ μ λμ€κ² λλ μΌμ΄ λ°μνκ² λλλ°,
κ·Έκ²μ 보μνκ³ μ μλμ κ°μ΄ μΉμ μΈμ μ κ°μ Έμ μΆκ°νλΌκ³ νμμ΅λλ€.
extension UserProfileViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// λͺ¨λΈμμ λΉμ¨μ κ°μ Έμ΅λλ€.
let model = viewModel.realModel[indexPath.row]
let aspectRatio = CGFloat(Double(model.content3) ?? 1)
// 컬λ μ
λ·°μ μΉμ
μΈμ
μ κ°μ Έμ΅λλ€.
let sectionInsets = (
collectionView.collectionViewLayout as? UICollectionViewFlowLayout
)?.sectionInset ?? UIEdgeInsets.zero
// μ
κ°κ²©μ μ μν©λλ€.
let spacing: CGFloat = 5
// ν μ€μ νμλ μμ΄ν
μ μλ₯Ό μ μν©λλ€.
let itemsPerRow: CGFloat = 2
print(sectionInsets)
// μ¬μ© κ°λ₯ν λλΉλ₯Ό κ³μ°ν©λλ€.
let totalSpacing = (spacing * (itemsPerRow - 1)) + sectionInsets.left + sectionInsets.right
let availableWidth = collectionView.bounds.width - totalSpacing
// μ
μ λλΉμ λμ΄λ₯Ό κ³μ°ν©λλ€.
let cellWidth = availableWidth / itemsPerRow
let cellHeight = cellWidth / aspectRatio
return CGSize(width: cellWidth, height: cellHeight)
}
}
μ½λλ§ λ³΄λ©΄ λ²μ¨ μμ± λκ±° κ°μλ° κ²°κ³Όλ λκ° μμ¬μ΄ κ²°κ³Όλ¬Όμ΄ νμν©λλ€.
ν β¦ λκ° λλκ±° κ°κΈ΄νλ° μ°Έ β¦. μ΄μμ£ ?
μ΄μ κ° λκΉμ?
μ½λκ° μ΄μν κΉμ?
νλ‘μ° λ μ΄μμμ κ°λ μ λ€μ μ΄ν΄ λ΄ λλ€.
νλ‘μ° λ μ΄μμμ νΉμ Line λ°λΌμ Cellμ λ°°μΉνλλ°μ
곡κ°μ΄ λΆμ‘±ν΄μ§λ μλ‘μ΄ λΌμΈμ μμ±ν΄ λ μ΄μμμ 그리λ ꡬ쑰μ λλ€.
κ·Έλμ μμκ°μ μκΈ°λ€ λ§ μΉκ΅¬κ° νμνκ² λ©λλ€.
κ·Έλμβ¦ μ λ°κ²λ€μ μ μν μμλ ν΄λμ€λ₯Ό μμ±νκ² λλλ°μ
κ°μ΄ νλ² λ³΄μ€κΉμ?
final class CustomPinterestLayout: UICollectionViewFlowLayout {
/// ν νμ μμ΄ν
κ°―μ
private let numberOfColums: Int // ν νμ μμ΄ν
κ°―μ
/// μ
μ ν¨λ©
private let cellPadding: CGFloat // μ
μ ν¨λ©
/// νμμ κ²½μ° ν€λμ λμ΄ μ§μ : Int
private
let headerHeight: CGFloat?
// 1.END 컬λ μ
λ·°μ 컨ν
μΈ μ¬μ΄μ¦λ₯Ό μ§μ
override var collectionViewContentSize: CGSize {
let minHeight = collectionView?.bounds.height ?? 0
return CGSize(width: contetnsWidth, height: max(contentsHeight,minHeight))
}
// 2. 컬λ μ
λ·°κ° μ²μ μ΄κΈ°ν νΉμ λ·°κ° λ³κ²½λ λ μ€νλλ λ©μλ
override func prepare()
// 3. λͺ¨λ μ
κ³Ό μλΈ λ·°μ λ μ΄μμ μ 보λ₯Ό 리ν΄ν΄μ€λλ€. (Rect) κΈ°λ°
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
// 4. λͺ¨λ μ
μ λ μ΄μμ μ 보λ₯Ό λ°νν©λλ€.
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
μμκ°μ λ©μλλ€μ μ§μ λ€ μ μν κ±΄λ° μ΄ κ²λ€μ ν¨κ³Όμ μΌλ‘ μ λ¬νκΈ° μν΄μ
λ리κ²μ΄νΈλ₯Ό λ¨Όμ μμ±νλ©΄μ μμν΄ λ³Όκ»μ
protocol CustomPinterestLayoutDelegate: AnyObject {
func collectionView(for collectionView: UICollectionView, heightForAtIndexPath indexPath: IndexPath) -> CGFloat
}
μμ μ½λλ λ리κ²μ΄νΈ ν¨ν΄μΌλ‘ νμ©ν νλ‘ν μ½μ λλ€.
λͺ©μ μ μκΉμμ κ°μ΄ 컬λ μ λ·°μ μΈλ±μ€ν¨μ€λ₯Ό λ°μμ CGFloatμ λ°νν©λλ€.
μ¦ λμ΄λ₯Ό λ°ννλ κ±°μ£
final class CustomPinterestLayout: UICollectionViewFlowLayout {
weak var delegate: CustomPinterestLayoutDelegate?
// ννμ μμ΄ν
κ°―μ
private let numberOfColums: Int
// μ
μ ν¨λ©
private let cellPadding: CGFloat
// νμν κ²½μ°μ ν€λμ λμ΄λ₯Ό μ§μ
private let headerHeight: CGFloat?
init(numberOfColums: Int, cellPadding: CGFloat, headerHeight: CGFloat? = nil) {
self.numberOfColums = numberOfColums
self.cellPadding = cellPadding
self.headerHeight = headerHeight
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
μ΄λΆλΆμ μ’ κ³ λ―Όμ νμλλ° νλ‘μ° λ μ΄μμμ΄λ€ 보λ μ μ κ°―μκ° νλ©΄μ λΉν΄ λΆμ‘±νλ©΄
μ€ν¬λ‘€μ΄ μλλ μ°Έμ¬κ° μ΄λ€μ§κΈ° λλ¬Έμ λ€μκ³Ό κ°μ μ½λλ‘ κ³μ°ν©λλ€.
override var collectionViewContentSize: CGSize {
let minHeight = collectionView?.bounds.height ?? 0
return CGSize(width: contetnsWidth, height: max(contentsHeight, minHeight))
}
μ€ν¬λ‘€ κ°λ₯ν μμμ μ μνκ³ , 컨ν μΈ κ° λ·°μ ν¬κΈ°λ³΄λ€ μμλλ μ€ν¬λ‘€μ΄ κ°λ₯νκ² ν©λλ€.
λ€μ μ½λμ μμμ λμ€κ² λλ
Prepare
λΌλ λ©μλκ° λ±μ₯νλλ°
λ μ΄μμμ μ΄κΈ° κ³μ°κ³Ό μΉ μν λ μ΄μμμ μ 보μ μΈν μ λ΄λΉν©λλ€.
( 컬λ μ λ·° 컨ν μΈ νμνκΈ°μ μ νΈμΆλ¨)
// Options λ€μ λ μ΄μμμ κ³μ°ν νμκ° μλλ‘ λ©λͺ¨λ¦¬μ μ μ₯νλ€.
private
var cache: [UICollectionViewLayoutAttributes] = []
// 2. 컬λ μ
λ·°κ° μ²μ μ΄κΈ°ν λκ±°λ, λ·°κ° λ³κ²½λ λ μ€νλ©λλ€.
// ... ν΄λΉ λ©μλλ λ μ΄μμμ 미리 κ³μ° νκ³ λ©λͺ¨λ¦¬μ μΊμ¬νμ¬
// ... λΆνμν λ°λ³΅μ μΈ μ°μ°μ νλκ²μ λ°©μ§νλλ‘ ν΄μΌνλ€.
override func prepare() {
guard let collectionView = collectionView,
collectionView.numberOfSections > 0,
collectionView.numberOfItems(inSection: 0) > 0,
cache.isEmpty else {
return
}
cache.removeAll()
let cellWidth = contetnsWidth / CGFloat(numberOfColums)
var yOffSet:[CGFloat] = []
if let headerHeight {
// ν€λ λ μ΄μμ μμ±
let headerIndexPath = IndexPath(item: 0, section: 0)
let headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: headerIndexPath)
headerAttribute.frame = CGRect(
x: 0,
y: 0,
width: collectionView.bounds.width,
height: headerHeight
) // ν€λλμ΄ μ§μ
cache.append(headerAttribute)
// cell μ YμμΉλ₯Ό λνλ΄λ λ°°μ΄μ
λλ€.
yOffSet = [CGFloat](repeating: headerAttribute.frame.maxY, count: numberOfColums)
yOffSet[0] = headerAttribute.frame.maxY
} else {
yOffSet = [CGFloat](repeating: 0, count: numberOfColums)
}
// cell μ XμμΉλ₯Ό λνλ΄λ λ°°μ΄μ
λλ€.
let xOffSet:[CGFloat] = [0, cellWidth]
var colum: Int = 0 // νμ¬ νμ μμΉ
for item in 0..<collectionView.numberOfItems(inSection: 0) {
// μΈλ±μ€ ν¨μ€λ₯Ό ν΅ν΄
let indexPath = IndexPath(item: item, section: 0)
// μΈλ±μ€ ν¨μ€μ λ§λ μ
μ ν¬κΈ°λ₯Ό κ³μ°ν©λλ€.
let customContentHeight = delegate?.collectionView(for: collectionView, heightForAtIndexPath: indexPath) ?? 100
// μν ν¨λ©(ν¨λ© 2λ°°) μ 컨ν
νΈ λμ΄λ₯Ό λν κ°μ λμ΄
let height = cellPadding * 2 + customContentHeight
let frame = CGRect(
x: xOffSet[colum],
y: yOffSet[colum],
width: cellWidth,
height: height
)
// μμν dx, dyκ° λμλλ°
// dx: xμΆ λ°©ν₯μ ν¨λ© κ° -> μ’μ° νλ μ μμΌλ‘ μ΄λ
// dy: yμΆ λ°©ν₯μ ν¨λ© κ° -> μν νλ μ μμΌλ‘ μ΄λ
let insetFrame = frame.insetBy(
dx: cellPadding, dy: cellPadding
)
// κ³μ°ν Frameλ₯Ό ν΅ν΄ λ μ΄μμμ 보λ₯Ό λ°μνκ³ μΊμ¬μ μ μ₯
let attribute = UICollectionViewLayoutAttributes(
forCellWith: indexPath
)
// λ μ΄μμμ 보λ₯Ό λ°μ
attribute.frame = insetFrame
cache.append(attribute)
// 컬λ μ
λ·°μ λμ΄λ₯Ό λ€μ μ§μ νλ€.
// frame.maxY -> νμ¬ μ
μ νλ μμ΄ λλλ YμΆ μμΉ -> μ
μλ¨ μμΉμμ μ
λμ΄λ₯Ό λνκ°
contentsHeight = max(contentsHeight, frame.maxY)
// μλ‘μ΄ μ
μ frame.maxYμ€ λν° κ°μ μ ννμ¬ μ»¬λ μ
λ·°μ μ 체 컨ν
μΈ λμ΄λ₯Ό μ
λ°μ΄νΈ
yOffSet[colum] = yOffSet[colum] + height
// λ€λ₯Έ μ΄λ―Έμ§ ν¬κΈ°λ‘ μΈν΄, νμͺ½μ΄μλ§ μ΄λ―Έμ§κ° μΆκ°λ¨μ λ°©μ§
colum = yOffSet[0] > yOffSet[1] ? 1 : 0
// λ§μ½ 첫λ²μ§Έ μ΄μ λμ΄κ° λλ²μ§Έ μ΄μ λμ΄λ³΄λ€ ν¬λ€λ©΄
// μ μ
λ€ λλ²μ§Έ μ΄μ λ°°μΉνλλ°
// μλμ μ μ
μ 첫λ²μ§Έ μ΄μ λ°°μΉνλ€.
}
}
μ΄μ°β¦. λ무 κΈΈμ£ ?
μλΌμ λ³΄κ² μ΅λλ€.
guard let collectionView = collectionView else { return }
if collectionView.numberOfSections == 0 || cache.isEmpty == false {
cache.removeAll()
}
let cellWidth = contetnsWidth / CGFloat(numberOfColums)
var yOffSet:[CGFloat] = []
if let headerHeight {
// ν€λ λ μ΄μμ μμ±
let headerIndexPath = IndexPath(item: 0, section: 0)
let headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: headerIndexPath)
headerAttribute.frame = CGRect(
x: 0,
y: 0,
width: collectionView.bounds.width, // 컬λ μ
λ·° μ체μ λμ΄μ
height: headerHeight // ν€λ λμ΄
)
cache.append(headerAttribute)
// cell μ YμμΉλ₯Ό λνλ΄λ λ°°μ΄μ
λλ€.
yOffSet = [CGFloat](repeating: headerAttribute.frame.maxY, count: numberOfColums)
yOffSet[0] = headerAttribute.frame.maxY
} else {
yOffSet = [CGFloat](repeating: 0, count: numberOfColums)
}
λ μ΄μμμ κ° μ κ³Ό ν€λ(μ νμ ) μμΉμ ν¬κΈ°λ₯Ό κ³μ°νμ¬μ
UICollectionViewLayoutAttributes
κ°μ²΄μ μ μ₯ν©λλ€.
// cell μ XμμΉλ₯Ό λνλ΄λ λ°°μ΄μ
λλ€.
let xOffSet:[CGFloat] = [0, cellWidth]
var colum: Int = 0 // νμ¬ νμ μμΉ
for item in 0..<collectionView.numberOfItems(inSection: 0) {
// μΈλ±μ€ ν¨μ€λ₯Ό ν΅ν΄
let indexPath = IndexPath(item: item, section: 0)
// μΈλ±μ€ ν¨μ€μ λ§λ μ
μ ν¬κΈ°λ₯Ό κ³μ°ν©λλ€.
let customContentHeight = delegate?.collectionView(for: collectionView, heightForAtIndexPath: indexPath) ?? 100
// μν ν¨λ©(ν¨λ© 2λ°°) μ 컨ν
νΈ λμ΄λ₯Ό λν κ°μ λμ΄
let height = cellPadding * 2 + customContentHeight
let frame = CGRect(
x: xOffSet[colum],
y: yOffSet[colum],
width: cellWidth,
height: height
)
// μμν dx, dyκ° λμλλ°
// dx: xμΆ λ°©ν₯μ ν¨λ© κ° -> μ’μ° νλ μ μμΌλ‘ μ΄λ
// dy: yμΆ λ°©ν₯μ ν¨λ© κ° -> μν νλ μ μμΌλ‘ μ΄λ
let insetFrame = frame.insetBy(
dx: cellPadding, dy: cellPadding
)
// κ³μ°ν Frameλ₯Ό ν΅ν΄ λ μ΄μμμ 보λ₯Ό λ°μνκ³ μΊμ¬μ μ μ₯
let attribute = UICollectionViewLayoutAttributes(
forCellWith: indexPath
)
// λ μ΄μμμ 보λ₯Ό λ°μ
attribute.frame = insetFrame
cache.append(attribute)
// 컬λ μ
λ·°μ λμ΄λ₯Ό λ€μ μ§μ νλ€.
// frame.maxY -> νμ¬ μ
μ νλ μμ΄ λλλ YμΆ μμΉ -> μ
μλ¨ μμΉμμ μ
λμ΄λ₯Ό λνκ°
contentsHeight = max(contentsHeight, frame.maxY)
// μλ‘μ΄ μ
μ frame.maxYμ€ λν° κ°μ μ ννμ¬ μ»¬λ μ
λ·°μ μ 체 컨ν
μΈ λμ΄λ₯Ό μ
λ°μ΄νΈ
yOffSet[colum] = yOffSet[colum] + height
// λ€λ₯Έ μ΄λ―Έμ§ ν¬κΈ°λ‘ μΈν΄, νμͺ½μ΄μλ§ μ΄λ―Έμ§κ° μΆκ°λ¨μ λ°©μ§
colum = yOffSet[0] > yOffSet[1] ? 1 : 0
// λ§μ½ 첫λ²μ§Έ μ΄μ λμ΄κ° λλ²μ§Έ μ΄μ λμ΄λ³΄λ€ ν¬λ€λ©΄
// μ μ
λ€ λλ²μ§Έ μ΄μ λ°°μΉνλλ°
// μλμ μ μ
μ 첫λ²μ§Έ μ΄μ λ°°μΉνλ€.
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attribute in cache {
// intersects λ©μλλ λ μ¬κ°νμ΄ κ²ΉμΉλμ§ μ¬λΆλ₯Ό λ°ν
if attribute.frame.intersects(rect){
// μ
νλ μκ³Ό μμ²κ³Ό κ΅μ°¨νλ€λ©΄ λ¦¬ν΄ κ°μ μΆκ°
visibleLayoutAttributes.append(attribute)
}
}
return visibleLayoutAttributes
}
νμ¬ νλ©΄μ 보μ¬μ§ μ , ν€λ, νΈν° λ±μ λ μ΄μμ μμ±μ κ²°μ ν λ νΈμΆ
λμ΄μ§λ λ©μλ μ λλ€.
μ€ν¬λ‘€λ§μ΄ λ°μ λ λλ§λ€ νΈμΆλλ©°, νλ©΄μ 보μ΄λ μμλ€μ λ μ΄μμμ μ λ°μ΄νΈν©λλ€.
// 4. λͺ¨λ μ
μ λ μ΄μμ μ 보λ₯Ό 리ν΄νλ€. IndexPath κΈ°λ°
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
ν΄λΉ λ©μλλ νΉμ μ μ λν λ μ΄μμ μμ±μ λ°ννλ κΈ°λ₯μ κ°μ§κ³ μμ΅λλ€.
κ° μ μ΄ νλ©΄μ νμλ λ ν΄λΉ μ μ μμΉ, ν¬κΈ°, λ³ν λ±μ λ μ΄μμ μμ±μ μ 곡νλ λ° μ¬μ©λ©λλ€.
//
// Created by Jae hyung Kim on 4/27/24.
//
import UIKit
/*
νκ³ ...!
Masonry or Pinterest Style Layout
collectionView.collectionViewLayout.invalidateLayout()
*/
protocol CustomPinterestLayoutDelegate: AnyObject {
func collectionView(for collectionView: UICollectionView, heightForAtIndexPath indexPath: IndexPath) -> CGFloat
}
final class CustomPinterestLayout: UICollectionViewFlowLayout {
weak var delegate: CustomPinterestLayoutDelegate?
/// ν νμ μμ΄ν
κ°―μ
private let numberOfColums: Int // ν νμ μμ΄ν
κ°―μ
/// μ
μ ν¨λ©
private let cellPadding: CGFloat // μ
μ ν¨λ©
/// νμμ κ²½μ° ν€λμ λμ΄ μ§μ : Int
private
let headerHeight: CGFloat?
init(numberOfColums: Int, cellPadding: CGFloat,_ headerHeight: CGFloat? = nil) {
self.numberOfColums = numberOfColums
self.cellPadding = cellPadding
self.headerHeight = headerHeight
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// 1. 컬λ μ
λ·° 컨ν
μΈ μ¬μ΄μ¦λ₯Ό μ§μ ν΄μΌνλ€.
// 1.1 컨ν
μΈ λμ΄μ κ·Έλ¦μ λ§λ€κ³ (νλ‘νΌν°)
private
var contentsHeight: CGFloat = 0
// Options λ€μ λ μ΄μμμ κ³μ°ν νμκ° μλλ‘ λ©λͺ¨λ¦¬μ μ μ₯νλ€.
private
var cache: [UICollectionViewLayoutAttributes] = []
// 1.2 컨ν
μΈ λμ΄λ₯Ό μ νλλ°...
private
var contetnsWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
// μ¬λ°±μ μ 보λ₯Ό λ°μμ
let insets = collectionView.contentInset
// μ’μ°μ¬λ°±μ λνκ°μ
let totalInset = insets.left + insets.right
// 컬λ μ
λ·°λμ΄μ κ°μ λΉΌλ©΄. λμ΄κ° λμ¨λ€.
return collectionView.bounds.width - (totalInset)
}
// 1.END 컬λ μ
λ·°μ 컨ν
μΈ μ¬μ΄μ¦λ₯Ό μ§μ
override var collectionViewContentSize: CGSize {
let minHeight = collectionView?.bounds.height ?? 0
return CGSize(width: contetnsWidth, height: max(contentsHeight,minHeight))
}
// 2. 컬λ μ
λ·°κ° μ²μ μ΄κΈ°ν λκ±°λ, λ·°κ° λ³κ²½λ λ μ€νλ©λλ€.
// ... ν΄λΉ λ©μλλ λ μ΄μμμ 미리 κ³μ° νκ³ λ©λͺ¨λ¦¬μ μΊμ¬νμ¬
// ... λΆνμν λ°λ³΅μ μΈ μ°μ°μ νλκ²μ λ°©μ§νλλ‘ ν΄μΌνλ€.
override func prepare() {
guard let collectionView = collectionView else { return }
if collectionView.numberOfSections == 0 || cache.isEmpty == false {
cache.removeAll()
}
let cellWidth = contetnsWidth / CGFloat(numberOfColums)
var yOffSet:[CGFloat] = []
if let headerHeight {
// ν€λ λ μ΄μμ μμ±
let headerIndexPath = IndexPath(item: 0, section: 0)
let headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: headerIndexPath)
headerAttribute.frame = CGRect(
x: 0,
y: 0,
width: collectionView.bounds.width,
height: headerHeight
) // ν€λλμ΄ μ§μ
cache.append(headerAttribute)
// cell μ YμμΉλ₯Ό λνλ΄λ λ°°μ΄μ
λλ€.
yOffSet = [CGFloat](repeating: headerAttribute.frame.maxY, count: numberOfColums)
yOffSet[0] = headerAttribute.frame.maxY
} else {
yOffSet = [CGFloat](repeating: 0, count: numberOfColums)
}
// cell μ XμμΉλ₯Ό λνλ΄λ λ°°μ΄μ
λλ€.
let xOffSet:[CGFloat] = [0, cellWidth]
var colum: Int = 0 // νμ¬ νμ μμΉ
for item in 0..<collectionView.numberOfItems(inSection: 0) {
// μΈλ±μ€ ν¨μ€λ₯Ό ν΅ν΄
let indexPath = IndexPath(item: item, section: 0)
// μΈλ±μ€ ν¨μ€μ λ§λ μ
μ ν¬κΈ°λ₯Ό κ³μ°ν©λλ€.
let customContentHeight = delegate?.collectionView(for: collectionView, heightForAtIndexPath: indexPath) ?? 100
// μν ν¨λ©(ν¨λ© 2λ°°) μ 컨ν
νΈ λμ΄λ₯Ό λν κ°μ λμ΄
let height = cellPadding * 2 + customContentHeight
let frame = CGRect(
x: xOffSet[colum],
y: yOffSet[colum],
width: cellWidth,
height: height
)
// μμν dx, dyκ° λμλλ°
// dx: xμΆ λ°©ν₯μ ν¨λ© κ° -> μ’μ° νλ μ μμΌλ‘ μ΄λ
// dy: yμΆ λ°©ν₯μ ν¨λ© κ° -> μν νλ μ μμΌλ‘ μ΄λ
let insetFrame = frame.insetBy(
dx: cellPadding, dy: cellPadding
)
// κ³μ°ν Frameλ₯Ό ν΅ν΄ λ μ΄μμμ 보λ₯Ό λ°μνκ³ μΊμ¬μ μ μ₯
let attribute = UICollectionViewLayoutAttributes(
forCellWith: indexPath
)
// λ μ΄μμμ 보λ₯Ό λ°μ
attribute.frame = insetFrame
cache.append(attribute)
// 컬λ μ
λ·°μ λμ΄λ₯Ό λ€μ μ§μ νλ€.
// frame.maxY -> νμ¬ μ
μ νλ μμ΄ λλλ YμΆ μμΉ -> μ
μλ¨ μμΉμμ μ
λμ΄λ₯Ό λνκ°
contentsHeight = max(contentsHeight, frame.maxY)
// μλ‘μ΄ μ
μ frame.maxYμ€ λν° κ°μ μ ννμ¬ μ»¬λ μ
λ·°μ μ 체 컨ν
μΈ λμ΄λ₯Ό μ
λ°μ΄νΈ
yOffSet[colum] = yOffSet[colum] + height
// λ€λ₯Έ μ΄λ―Έμ§ ν¬κΈ°λ‘ μΈν΄, νμͺ½μ΄μλ§ μ΄λ―Έμ§κ° μΆκ°λ¨μ λ°©μ§
colum = yOffSet[0] > yOffSet[1] ? 1 : 0
// λ§μ½ 첫λ²μ§Έ μ΄μ λμ΄κ° λλ²μ§Έ μ΄μ λμ΄λ³΄λ€ ν¬λ€λ©΄
// μ μ
λ€ λλ²μ§Έ μ΄μ λ°°μΉνλλ°
// μλμ μ μ
μ 첫λ²μ§Έ μ΄μ λ°°μΉνλ€.
}
}
// 3. λͺ¨λ μ
κ³Ό μλΈ λ·°μ λ μ΄μμ μ 보λ₯Ό 리ν΄νλ€. (Rect) κΈ°λ°
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attribute in cache {
// intersects λ©μλλ λ μ¬κ°νμ΄ κ²ΉμΉλμ§ μ¬λΆλ₯Ό λ°ν
if attribute.frame.intersects(rect){
// μ
νλ μκ³Ό μμ²κ³Ό κ΅μ°¨νλ€λ©΄ λ¦¬ν΄ κ°μ μΆκ°
visibleLayoutAttributes.append(attribute)
}
}
return visibleLayoutAttributes
}
// 4. λͺ¨λ μ
μ λ μ΄μμ μ 보λ₯Ό 리ν΄νλ€. IndexPath κΈ°λ°
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
κ²°κ³Όμ μΌλ‘ , 컀μ€ν λ μ΄μμμ ν΅ν΄ μνλ UIλ₯Ό ꡬνν μ μμμ§λ§,
μ΄ λ°°μ°λ κ³Όμ μμ λ μ΄μμμ κ³μ° λ‘μ§κ³Ό μ±λ₯ μ΅μ νμ μ€μμ±μ κΉμ΄ κΉ¨λ¬μμ΅λλ€.
λν, UICollectionViewμ λ μ΄μμ μμ€ν μ μ΄ν΄λλ₯Ό λμ΄λ λ° ν° λμμ΄ λμμ΅λλ€.
λ μ΄μμ νλ‘μΈμ€μ μμ± κ΄λ¦¬ λ°©λ²μ λν΄ μ‘°κΈμ΄λλ§ λ κΉμ μ΄ν΄λ₯Ό ν μ μκ² λμ΄μ
κΈ°μ©λλ€. ν루 λ° μ λ μ΄κ²μ ν μ νκ² λμμ§λ§ κ·Έλλ κ²°κ³Όκ° λμμ κΈ°μ©λλ€.!!!