[UIKit] UICollectionView: Waterfall Layout

Junyoung Park·2022년 10월 29일
0

UIKit

목록 보기
62/142
post-thumbnail

Swift: Waterfall CollectionView Layout (2021, Xcode 12, Swift 5) - iOS Development

UICollectionView: Waterfall Layout

구현 목표

  • 워터폴 형식의 레이아웃을 구현한 컬렉션 뷰
  • CHTCollectionViewWaterfallLayout 라이브러리 사용

구현 태스크

  • 컬렉션 뷰의 레이아웃에 해당 라이브러리 사용하기

핵심 코드

    private let collectionView: UICollectionView = {
        let layout = CHTCollectionViewWaterfallLayout()
        layout.itemRenderDirection = .leftToRight
        layout.columnCount = 2
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.identifier)
        return collectionView
    }()
  • 컬렉션 뷰를 이니셜라이즈할 때 CHTCollectionViewWaterfallLayout 레이아웃을 파라미터로 넘겨주면 이후 델리게이트 함수를 사용 가능
extension CollectionViewController: CHTCollectionViewDelegateWaterfallLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: view.frame.size.width / 2, height: models[indexPath.row].height)
    }
}
  • 원하는 너비의 아이템 사이즈를 그릴 수 있는 함수

소스 코드

import UIKit
import CHTCollectionViewWaterfallLayout

class CollectionViewController: UIViewController {
    private let collectionView: UICollectionView = {
        let layout = CHTCollectionViewWaterfallLayout()
        layout.itemRenderDirection = .leftToRight
        layout.columnCount = 2
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.identifier)
        return collectionView
    }()
    private var models = [ImageModel]()

    override func viewDidLoad() {
        super.viewDidLoad()
        setUI()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }

    private func setUI() {
        view.backgroundColor = .systemBackground
        view.addSubview(collectionView)
        collectionView.dataSource = self
        collectionView.delegate = self
        for _ in 0..<100 {
            let number = Int.random(in: 1...15)
            let height = CGFloat.random(in: 200...400)
            let name = "sf_\(number)"
            let model = ImageModel(imageName: name, height: height)
            models.append(model)
        }
    }
}

extension CollectionViewController: UICollectionViewDelegate {
    
}

extension CollectionViewController: CHTCollectionViewDelegateWaterfallLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: view.frame.size.width / 2, height: models[indexPath.row].height)
    }
}

extension CollectionViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.identifier, for: indexPath) as? CustomCollectionViewCell else {
            return CustomCollectionViewCell()
        }
        cell.configure(with: models[indexPath.row])
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return models.count
    }
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
}
  • 워터폴 라이브러리를 레이아웃으로 넘겨받은 컬렉션 뷰를 사용하는 컨트롤러
import UIKit

class CustomCollectionViewCell: UICollectionViewCell {
    static let identifier = "CustomCollectionViewCell"
    private let imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(imageView)
        contentView.clipsToBounds = true
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        imageView.frame = contentView.bounds
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        imageView.image = nil
    }
    
    func configure(with model: ImageModel) {
        if let image = UIImage(named: model.imageName) {
            imageView.image = image
        }
    }
    
}
  • 이미지를 바인딩하는 커스텀 셀
import Foundation

struct ImageModel {
    let imageName: String
    let height: CGFloat
}
  • 이미지를 바인딩하기 위한 구조체

구현 화면

profile
JUST DO IT

0개의 댓글