[UIKit] UICollectionView: Custom Cell using Preview

Junyoung Park·2022년 12월 3일
0

UIKit

목록 보기
108/142
post-thumbnail

UICollectionView From Scratch [1] - CollectionViewCell & Supplementary View

UICollectionView: Custom Cell using Preview

구현 목표

  • 주어진 모델을 커스텀 셀로 받아 UI를 그리기

구현 태스크

  • 커스텀 컬렉션 뷰 셀
  • 커스텀 컬렉션 뷰 헤더 셀 구현
  • SwiftUI의 프리뷰 사용

핵심 코드

private lazy var verticalStack: UIStackView = {
        let stackView = UIStackView(arrangedSubviews: [imageView, textLabel])
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.spacing = 8
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()
  • 이미지 뷰와 텍스트 라벨을 통해 수직 스택 뷰를 구현
private func setUI() {
        contentView.addSubview(verticalStack)
        let verticalStackViewConstraints = [
            verticalStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            verticalStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
            verticalStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 8),
            verticalStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 8)
        ]
        NSLayoutConstraint.activate(verticalStackViewConstraints)
        let imageViewConstraints = [
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
        ]
        NSLayoutConstraint.activate(imageViewConstraints)
    }
  • self-sizing 뷰를 구현하기 위해 오토 레이아웃 적용
  • contentView 기준으로 셀프 사이징하려는 뷰의 레이아웃을 조정
struct CharacterCellViewRepresentable: UIViewRepresentable {
    let character: Character
    
    func makeUIView(context: Context) -> some UIView {
        let cell = CharacterCell()
        cell.configure(with: character)
        return cell
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
}
  • UIKit 프레임워크를 통해 선언된 컴포넌트를 SwiftUI에서 사용하기 위해 감싸주는 UIViewRepresentable 클래스
  • 해당 클래스 내에서 mkaeUIView 함수를 통해 표현하고자 하는 UIKit 컴포넌트를 리턴
  • 특정 파라미터로 함수를 적용해야 한다면 let character와 같은 구조체 내 값을 통해 표현할 수 있음
  • 즉 해당 구조체가 이니셜라이즈될 때 전달되는 데이터를 통해 UIKit 컴포넌트의 해당 함수를 실행할 수 있다는 뜻
struct CharacterCell_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ScrollView {
                LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]) {
                    ForEach(Universe.ff7r.stubs) { character in
                        CharacterCellViewRepresentable(character: character)
                            .frame(width: 120, height: 150)

                    }
                }
            }
        }
    }
}
  • 위에서 UIKit 컴포넌트를 SwiftUI로 감싸주는 UIViewRepresentable을 사용하는 까닭
  • 프리뷰를 통해 해당 뷰를 볼 수 있기 때문에 사용성 높아짐

소스 코드

import UIKit
import SwiftUI

class CharacterCell: UICollectionViewCell {
    private let imageView: RoundedImageView = {
        let imageView = RoundedImageView()
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()
    private let textLabel: UILabel = {
        let label = UILabel()
        label.font = .preferredFont(forTextStyle: .caption2)
        label.adjustsFontForContentSizeCategory = true
        label.textAlignment = .center
        return label
    }()
    private lazy var verticalStack: UIStackView = {
        let stackView = UIStackView(arrangedSubviews: [imageView, textLabel])
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.spacing = 8
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        textLabel.text = nil
        imageView.image = nil
    }
    
    private func setUI() {
        contentView.addSubview(verticalStack)
        let verticalStackViewConstraints = [
            verticalStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            verticalStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
            verticalStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 8),
            verticalStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 8)
        ]
        NSLayoutConstraint.activate(verticalStackViewConstraints)
        let imageViewConstraints = [
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
        ]
        NSLayoutConstraint.activate(imageViewConstraints)
    }
    
    func configure(with model: Character) {
        textLabel.text = model.name
        imageView.image = UIImage(named: model.imageName)
    }
}
  • 스택 뷰를 통해 이미지 뷰, 텍스트 라벨을 그리기
  • 셀프 사이징을 위한 컨텐츠 뷰와의 오토 레이아웃이 특징
import UIKit
import SwiftUI

class HeaderView: UICollectionReusableView {
    private let textLabel: UILabel = {
        let label = UILabel()
        label.font = .preferredFont(forTextStyle: .headline)
        label.adjustsFontForContentSizeCategory = true
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setUI() {
        addSubview(textLabel)
        let textLabelConstraints = [
            textLabel.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            textLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            textLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            textLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16)
        ]
        NSLayoutConstraint.activate(textLabelConstraints)
    }
    
    func configure(with title: String) {
        textLabel.text = title
    }
}
  • 커스텀 셀과 마찬가지로 셀프 사이징 가능한 텍스트 라벨이 선언된 컬렉션 뷰의 커스텀 헤더 뷰

구현 화면

profile
JUST DO IT

0개의 댓글