-
[iOS] UICollectionView와 UICollectionViewFlowLayout의 관계: 간단하게 2단리스트를 만들어보자!앱등이에게 살충제를 뿌린다./iOS 2017. 8. 3. 01:16
Swift3기준으로 작성되었습니다.
UICollectionView가 UICollectionViewFlowLayout와 함께 레이아웃을 만드는 과정을 간단히 알아보겠다.
- 샘플앱을 보고,
- 컬렉션뷰의 레이아웃 과정을 알아본 뒤,
- 샘플앱을 수정해보자.
1. 샘플앱컬렉션뷰를 하나 만들고 셀에는 UIImageView하나만 추가되어 있다. 이 쯔위앱을 만들기 위해서는 아래코드가 필요하다.
import UIKit
// 편의상 전역변수
let imagesCount = 21
var images: [UIImage] { /*...*/ return images }
class ViewController: UIViewController, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell // Dequeueing생략
cell.imageView.image = images[indexPath.row]
return cell
}
}
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
}
class CollectionViewFlowLayout: UICollectionViewFlowLayout {
// 여기를 수정할 예정
override func prepare() {
super.prepare()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return super.layoutAttributesForElements(in: rect)
}
}
2. 코드를 수정하기에 앞서 몇 가지 내용을 짚어보자.
아래 그림은 UICollectionView가 Layout객체와 대화하며 레이아웃을 만들어가는 과정을 보여준다.
1. prepareLayout() swift3에서는 prepare(): 레이아웃관련 연산이 일어날 때마다 호출된다. 이 메소드에서 셀의 위치/크기 등을 계산하기 위한 사전처리를 할 수 있다.
2. collectionViewContentSize() : 이 메소드에서는 컬렉션뷰의 크기를 리턴해야 한다. 0번째 셀부터 마지막셀까지를 담는 전체 영역을 알려주어야 하는 것이다.
3.layoutAttributesForElementsInRect(_:) : 이 메소드에서는 rect파라미터 안에 있는 모든 셀들의 속성들을 배열로 리턴해야한다. 이 속성은 UICollectionViewLayoutAttributes타입이고, frame, indexPath등의 값을 갖고 있다.
3. 어떤식으로 override & implement해야할까?
우리는 더 많은 공간에서 쯔위를 보길 원한다. 그래서 빈틈을 매워주는 핀터레스트 방식의 UI를 구현할 것이다.
위 샘플코드에서 CollectionViewFlowLayout 클래스만 수정해보자.
class CollectionViewFlowLayout: UICollectionViewFlowLayout {
var layoutCache: [UICollectionViewLayoutAttributes]? = nil
override func prepare() {
super.prepare()
let width = (collectionView?.bounds.size.width ?? 375) / 2 - 5
// attribute 만드는 작업은 한번만 합니다.
guard layoutCache == nil else { return }
var attrsList: [UICollectionViewLayoutAttributes] = []
for (index, image) in images.enumerated() {
let isOdd = index % 2 == 0
let attrs = UICollectionViewLayoutAttributes(forCellWith: IndexPath(item: index, section: 0))
let ratio = image.size.height / image.size.width
let height = width * ratio
var frame = CGRect(x: isOdd ? 0 : width + 10, y: 0, width: width, height: height)
if index > 1 {
let upperImage = attrsList[index-2]
frame.origin.y = upperImage.frame.origin.y + upperImage.frame.size.height + 10
}
attrs.frame = frame
attrsList.append(attrs)
}
layoutCache = attrsList
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let layoutCache = layoutCache else { return super.layoutAttributesForElements(in: rect) }
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in layoutCache {
if attributes.frame.intersects(rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
}
prepare메소드에서 모든 이미지에 대한 비율을 구하고, UICollectionViewLayoutAttributes타입의 배열을 생성해둔다.
그럼 layoutAttributesForElementes(in:)메소드에서는 해당 rect안에 있는 attributes만 찾아내서 리턴해주면 되는 것이다.
참으로 간단하다~!
이 외에도 많은 메소드들이 상호작용한다.
하지만 오늘은 이 정도만 알아두자.
참고 : https://www.raywenderlich.com/107439/uicollectionview-custom-layout-tutorial-pinterest
끝
화창한 여름날 슈퍼칼퇴잼
'앱등이에게 살충제를 뿌린다. > iOS' 카테고리의 다른 글
[iOS-Rich Push] Notification Service Extension, Notification Content Extension을 알아보자. (푸쉬에 이미지 넣기) (0) 2017.08.31 [iOS] UIActivityViewController에 Instagram이 나오지 않는다? (0) 2017.08.16 Adhoc과 in-House distribution의 차이 (0) 2017.07.24 [iOS] Kingfisher의 AnimatedImageView(UIImageView의 GIF재생)에 대하여 (0) 2017.07.16 [Raywenderlich - iOS] Universal Link 적용하기 (0) 2017.07.02