레이아웃 핵심 프로세스

👋 들어가기 전
이전에 학습한 https://hamp.tistory.com/210 디바이스 회전 포스팅 마지막 질문인
회전 시 UICollectionView와 같은 cell에 대한 레이아웃 갱신을 이번 포스팅에서 이어가보자.
🏁 학습할 내용
- 레이아웃의 core process
- invalidateLayout
🚅 레이아웃 core process

먼저 전체적인 과정은 위와 같다.
각 과정을 차례대로 알아보자.
🚦prepare
⭐️ 정의
레이아웃 계산하기 전 미리 필요한 데이터나 상태를 준비하는 함수
간단히 말하면 레이아웃을 그리기 전 사전 작업(셀 위치 계산, 내부 캐시 초기화 등)을 준비
📞 호출 시기
CollectionView의 내용이 처음 보일 때 또는 layout이 invalidated 될 때 호출된다.
🖼️ collectionViewContentSize
⭐️ 정의
레이아웃에서 제공해주는 내용을 보여줄 Size를 알려준다.
🏛️ layoutAttributesForElements
⭐️ 정의
지정된 rect안에 있는 cell, header, footer등의 레이아웃 속성을 탐색
탐색이 끝나면 탐색 정보를 토대로 CollectionView가 화면을 그린다.
♻️ invalidateLayout()
⭐️ 정의
현재 적용된 layout 무효화 시키는 플래그 함수
여기서 주의할 점은 지금 당장 바꾸는게 아닌 다음 Run Loop에 적용
view의 update cycle에 플래그를 던지는 setNeedsLayout()과 비슷한 역할
⌨️ 실습
버튼을 통해 invalidateLayout 호출 시 Layout에서 어떤 함수들이 호출되는 지 살펴보자.
LoggingFlowLayout.swift
final class LoggingFlowLayout: UICollectionViewFlowLayout {
override func invalidateLayout() {
print("➡️ [invalidateLayout] called")
super.invalidateLayout()
}
override func prepare() {
print("🛠️ [prepare] called")
super.prepare()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
print("📐 [layoutAttributesForElements] called for rect: \(rect)")
return super.layoutAttributesForElements(in: rect)
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
print("🎯 [layoutAttributesForItem] called for indexPath: \(indexPath)")
return super.layoutAttributesForItem(at: indexPath)
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
print("🔄 [shouldInvalidateLayout] called with newBounds: \(newBounds)")
return super.shouldInvalidateLayout(forBoundsChange: newBounds)
}
}
최초 present

invalidateLayout()

정확히 prepare부터 다시 시도 되는 것을 확인할 수 있다.
다시말하면 invalidateLayout은 오직 Layout과 관련 업데이트를 특정 시점에 변경하고 싶을 때
사용할 수 있다.
⚠️ 주의할 점
reload 데이터를 적용했도 cell을 다시 그리기 때문에 Layout 변경을 하는 것 처럼보인다.
여기서 사용목적을 분명히 구분하고 사용해야한다.
reload데이터는 dataSource의 변경을 알림으로써 cell을 다시 그리는 것이고
invalidateLayout은 dataSource 변경과 상관 없이 Layout 변경의 필요성에 의해서 호출해야한다.
가장 단순한 예로는 보여지는 데이터가 같은데 디바이스가 회전함으로써 layout 변경이 필요될 때
호출하여 가로/세로의 UI를 다르게 표현할 수 있다
출처
Creating Custom Layouts
Creating Custom Layouts Before you start building custom layouts, consider whether doing so is really necessary. The UICollectionViewFlowLayout class provides a significant amount of behavior that has already been optimized for efficiency and that can be a
developer.apple.com
https://developer.apple.com/documentation/uikit/uicollectionviewlayout/invalidatelayout()
invalidateLayout() | Apple Developer Documentation
Invalidates the current layout and triggers a layout update.
developer.apple.com