PinterestTutorial-iOS
μ΄λ―Έμ§ ν¬κΈ°μ λ°λΌμ λμ μΌλ‘ μ μ λ μ΄μμμ μ€μ νλ νν°λ μ€νΈ λ μ΄μμ ꡬνν΄ λ³΄μλ€.
μμ±
μ½λ
- UICollectionViewDelegateFlowLayout μ μλΈν΄λμ€μΈ PinterestLayout μμ±.
// PinterestLayout μμ κ° μ΄λ―Έμ§ λμ΄λ₯Ό μ μ μλλ‘ Delegate μμ±.
protocol PinterestLayoutDelegate: AnyObject {
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath) -> CGFloat
}
class PinterestLayout: UICollectionViewFlowLayout {
// delegateλ‘ ViewController λ₯Ό λνλΈλ€.
weak var delegate: PinterestLayoutDelegate?
private var contentHeight: CGFloat = 0
private var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
// 1. μ½λ μ
λ·°μ μ½ν
μΈ μ¬μ΄μ¦λ₯Ό μ§μ ν©λλ€.
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
// λ€μ λ μ΄μμμ κ³μ°ν νμκ° μλλ‘ λ©λͺ¨λ¦¬μ μ μ₯ν©λλ€.
private var cache: [UICollectionViewLayoutAttributes] = []
// 2. μ½λ μ
λ·°κ° μ²μ μ΄κΈ°νλκ±°λ λ·°κ° λ³κ²½λ λ μ€νλ©λλ€. μ΄ λ©μλμμ λ μ΄μμμ
// 미리 κ³μ°νμ¬ λ©λͺ¨λ¦¬μ μ μ¬νκ³ , νμν λλ§λ€ ν¨μ¨μ μΌλ‘ μ κ·Όν μ μλλ‘ κ΅¬νν΄μΌ ν©λλ€.
override func prepare() {
guard let collectionView = collectionView, cache.isEmpty else { return }
let numberOfColumns: Int = 2 // ν νμ μμ΄ν
κ°―μ
let cellPadding: CGFloat = 5
let cellWidth: CGFloat = contentWidth / CGFloat(numberOfColumns)
let xOffSet: [CGFloat] = [0, cellWidth] // cell μ x μμΉλ₯Ό λνλ΄λ λ°°μ΄
var yOffSet: [CGFloat] = .init(repeating: 0, count: numberOfColumns) // // cell μ y μμΉλ₯Ό λνλ΄λ λ°°μ΄
var column: Int = 0 // νμ¬ νμ μμΉ
for item in 0..<collectionView.numberOfItems(inSection: 0) {
// IndexPath μ λ§λ μ
μ ν¬κΈ°, μμΉλ₯Ό κ³μ°ν©λλ€.
let indexPath = IndexPath(item: item, section: 0)
let imageHeight = delegate?.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath) ?? 180
let height = cellPadding * 2 + imageHeight
let frame = CGRect(x: xOffSet[column],
y: yOffSet[column],
width: cellWidth,
height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// μμμ κ³μ°ν Frame μ κΈ°λ°μΌλ‘ cache μ λ€μ΄κ° λ μ΄μμ μ 보λ₯Ό μΆκ°ν©λλ€.
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// μ½λ μ
λ·°μ contentHeight λ₯Ό λ€μ μ§μ ν©λλ€.
contentHeight = max(contentHeight, frame.maxY)
yOffSet[column] = yOffSet[column] + height
// λ€λ₯Έ μ΄λ―Έμ§ ν¬κΈ°λ‘ μΈν΄μ, νμͺ½ μ΄μλ§ μ΄λ―Έμ§κ° μΆκ°λλ κ²μ λ°©μ§ν©λλ€.
column = yOffSet[0] > yOffSet[1] ? 1 : 0
}
}
// 3. λͺ¨λ μ
κ³Ό 보좩 λ·°μ λ μ΄μμ μ 보λ₯Ό 리ν΄ν©λλ€. νλ©΄ νμ μμ κΈ°λ°(Rect)μ μμ²μ΄ λ€μ΄μ¬ λ μ¬μ©ν©λλ€.
override func layoutAttributesForElements(in rect: CGRect)
-> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in cache {
if attributes.frame.intersects(rect) { // μ
frame κ³Ό μμ² Rect κ° κ΅μ°¨νλ€λ©΄, λ¦¬ν΄ κ°μ μΆκ°ν©λλ€.
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
// 4. λͺ¨λ μ
μ λ μ΄μμ μ 보λ₯Ό 리ν΄ν©λλ€. IndexPath λ‘ μμ²μ΄ λ€μ΄μ¬ λ μ΄ λ©μλλ₯Ό μ¬μ©ν©λλ€.
override func layoutAttributesForItem(at indexPath: IndexPath)
-> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
- MainVC μμ PinterestLayoutDelegate λ₯Ό μ±ν.
// MARK: - UICollectionViewDelegateFlowLayout
extension MainVC: PinterestLayoutDelegate {
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath) -> CGFloat {
let cellWidth: CGFloat = (view.bounds.width - 4) / 2 // μ
κ°λ‘ ν¬κΈ°
let imageHeight = imageList[indexPath.item].image.size.height
let imageWidth = imageList[indexPath.item].image.size.width
// μ΄λ―Έμ§ λΉμ¨
let imageRatio = imageHeight/imageWidth
return imageRatio * cellWidth
}
}