[UIKit] UICollection Pinterest Layout - 1
Table of contents
우리의 목표는
위 그림 처럼 UICollectionView에서 Height에 맞게 Cell의 높이를 Dynamic 하게 만드는 것이다.
UICollectionView가 뭔지 모르겠다고? 그래서 준비했음
+ 난 개인적으로 Dynamic Height 이런거 일줄 알았는데 Pinterest Laytout이라 부른다 카더라..
아니 그냥 그렇다고
우리의 목표를 이루기 위해서는 UICollectionViewLayout
에 대해서 알아야한다.
추상 클래스인데 collectionview layout 정보를 만드는 클래스래.
자세한 것을 알고 싶다? 그럼 무조건 공식문서 하지만 여기는 요약글이니
빠르게 알아보자
UICollectionView(이하 CollectionView)
에서UICollectionViewLayout(이하 Layout)
의 prepare 함수를 호출한다.
그럼 prepare 함수에 뭐 작성해야함? 반환값도 없는데?
이후 호출되는 값 및 호출되는 함수 반환값을 준비해야함 왜냐고?
말 그대로 prepare: 준비하다. 준비하는 함수임.
collectionViewContentSize
반환을 해야 함
모두 알다 싶이 CollectionView
는 ScrollView
의 SubClass임.
즉 그 말은 ContentSize를 적절하게 늘려줘야 화면에 보임. 그러니 contentSize를 적절하게 늘려줘야함
layoutAttributesForElements
를 호출해서[UICollectionViewAttribute]
를 반환해야 함
근데 UICollectionViewAttribute가 뭐임? -> 셀의 frame을 정해주는 값.
오호 그러면 저 셀의 위치를 하나하나 정해줘야겠네? -> ㅇㅇ 맞음
prepare()
class DynamicHeightLayout: UICollectionViewLayout {
... 중략 ...
var cellWidth: CGFloat = 0
private let numberOfColumns = 2
private let cellPadding: CGFloat = 10
private var cache: [UICollectionViewLayoutAttributes] = []
override func prepare() {
guard let collectionView = collectionView else { return }
// 1
let columnWidth = contentWidth / CGFloat(numberOfColumns)
var xOffset: [CGFloat] = []
for column in 0..<numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
cellWidth = columnWidth - cellPadding * 2
var yOffset: [CGFloat] = .init(repeating: 0, count: numberOfColumns)
// 2
var column = 0
for item in 0..<collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let imageHeight = delegate?.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath) ?? 0
let height = cellPadding * 2 + imageHeight
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// 3
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
reloadedCount = collectionView.numberOfItems(inSection: 0)
}
}
폭 너비에 대한 설정
columnWidth
: 열의 너비 설정xOffset
: 셀의 열에 따른 x 위치cellWidth
: 셀의 너비 설정
높이에 대한 설정
여기서 눈여겨봐야 할 부분은 cell의 padding도 포함해서 넣는다.
가로 세로에 대한 정보를 저장한다.
layoutAttributesForElements
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
cache에 저장되어있는 attributes들이 rect(CollectionView의 CGSize)와 교차점이 있는지 확인 후에 교차점이 있는 Attribute만 return 한다.
collectionView의 frame과 bounds가 변할 때 마다 호출된다.
명확한 뜻을 담기 위해서 위 코드로 적었는데 조금 더 간략하게 작성 가능
override func layoutAttributesForElements(in rect: CGRect) -> [UICollect
return cache.filter{ $0.frame.intersects(rect) }
}
layoutAttributesForItem
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
이건... 이건... 정말 잘 모르겠다..
아시는 분 Help... 언제 호출되는지 모르겠다. BreakPoint를 걸어도 멈추질 않는다.
아직 전체에 대한 궁금증이 안 갔을 것이다.
내가봐도 그럼 그렇기에 전체코드를 통해서 다시 해석하는 시간 가져볼까함