구현 과제
햄버거 키오스크에서 상단의 카테고리와 타이틀 로고의 구현을 진행
카테고리바는 UICollectionView로 구현할 생각이었기 때문에 우선 커스텀 UICollectionViewCell을 만들기로 했다.
컬렉션뷰는 레이블 단일 구성이다. 때문에 레이블에 대한 정의와 세팅 메소드를 구현해준다.
그리고 셀에 대한 값을 init을 이용하여 셀에 대해 별도로 설정하지 않아도 되도록 설정했다.
// 카테고리의 레이블 값 정의
private let categoryLabel: UILabel = {
let label = UILabel()
label.text = "Vegan"
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 15, weight: .regular)
label.textColor = UIColor.categoryText
label.backgroundColor = UIColor.category
label.clipsToBounds = true
label.layer.cornerRadius = 20
return label
}()
/// 컬렉션뷰 셀 아이템 기본 설정
/// - Parameter frame: 컬렉션뷰 셀 아이템의 frame 값 = .zero 설정
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = UIColor.clear
contentView.addSubview(categoryLabel)
let labelWidthSize = categoryLabel.intrinsicContentSize.width // 레이블의 intrinsicContentSize
let labelHeightSize = categoryLabel.intrinsicContentSize.height // 레이블의 intrinsicContentSize
categoryLabel.sizeToFit()
categoryLabel.snp.makeConstraints {
$0.center.equalToSuperview()
$0.width.equalTo(labelWidthSize + 40)
$0.height.equalTo(labelHeightSize + 20)
}
}
우리팀은 각자 한 가지 이상의 기능을 구현한 뒤에 하나의 ViewController에 UIStackView등을 활용하여 합치기로 하였다.
때문에 컬렉션뷰나 레이블에 대해 합칠 때 정의하게 되면 일이 늘어나게 되니, 컬렉션 뷰와 레이블에 대한 세팅을 가지는 커스텀 뷰를 만들기로 하였다.
우선 컬렉션 뷰에 대한 정의를 한다.
private var collectionView: UICollectionView
/// 컬렉션뷰의 세팅 메소드
func setupCollectionView() {
collectionView.delegate = self
collectionView.dataSource = self
// 커스텀 셀을 아이템으로 등록
collectionView.register(CategoryCollectionViewCell.self, forCellWithReuseIdentifier: CategoryCollectionViewCell.identifier)
collectionView.backgroundColor = UIColor.clear
// 스크롤 인디케이터 hidden
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
self.addSubview(collectionView)
collectionView.snp.makeConstraints {
$0.top.equalTo(titleLogo.snp.bottom)
$0.leading.equalToSuperview().offset(20)
$0.trailing.equalToSuperview()
$0.height.equalTo(50)
}
}
그리고 컬렉션뷰의 Delegate, DataSource를 이용하여 카테고리를 선택했을 때의 액션을 구현하였다.
addSubView도 모두 작성했고, 오토레이아웃도 모두 설정하였으니 문제 없이 빌드 되리라 생각하고 빌드를 했는데...

왜 에러가 날까...
혹시 오토레이아웃에 대해 잘못 설정한걸까 싶어서 열심히 수정을 하고,
인터넷을 서치하며 무엇이 잘못된지 찾아봤는데 답은 컬렉션뷰의 layout에 있었다.
컬렉션뷰는 인스턴스로 초기화를 할 때 collectionViewLayout이라는 매개변수를 필요로 한다. 물론 나는 이 설정을 해주었지만, 메소드를 통해 구현했고, 에러는 메소드를 호출하기 전에 발생하는 상황이었다.
커스텀 UIView를 초기화하여 호출하면 내부의 컬렉션뷰와 레이블뷰가 생성되는데, 이 때 컬렉션뷰의 layout 값이 없다고 오류가 발생한 것이다.
분명 생성자에서 컬렉션뷰를 세팅해주는(레이아웃 값이 있는)메소드를 호출하게 했는데도 오류는 계속 발생했다.
override init(frame: CGRect) {
super.init(frame: frame)
// 컬렉션뷰와 레이블뷰를 세팅하는 메소드
setupUIConfig()
}
그럼 어떻게 하면 좋을까... 고민했는데 생성자에서부터 레이아웃값을 주면 괜찮지 않을까? 생각했다.
override init(frame: CGRect) {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 90, height: 50)
layout.minimumLineSpacing = 5
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
super.init(frame: frame)
setupUIConfig()
}
그리고 빌드를 해봤는데...

...!!! 성공이다!!!
그리고 카테고리를 클릭해서 메소드가 제대로 작동하는지 확인해봤는데...

넌 또 왜이러니...
카테고리를 하나 선택하면 선택한 카테고리만 강조가 되어야 하는데 다른 셀들까지 함께 강조가 되어버린다...
이번엔 왜 이럴까 원인을 조사해보자
Swift에서 UICollectionView는 성능을 최적화하기 위해 셀을 재사용한다. 화면에서 벗어난 셀은 재사용 큐에 들어가며, 새로운 셀을 생성하지 않고 기존 셀을 재활용한다.
즉, 셀이 재사용될 때 이전 상태(강조효과 등)가 남아있을 수 있다는 뜻이다.
그럼 이러한 현상을 해결하려면 어떻게 해야할까??
첫 번째 방법은 셀의 기본 상태를 초기화 하는 것이다.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCell", for: indexPath) as! CategoryCell
// 셀 초기화: 기본 상태 설정
cell.backgroundColor = UIColor.lightGray // 기본 색상
cell.label.textColor = UIColor.black // 기본 텍스트 색상
// 현재 선택된 셀 강조
if indexPath == selectedIndexPath {
cell.backgroundColor = UIColor.blue // 강조 색상
cell.label.textColor = UIColor.white
}
return cell
}
이렇게하면 셀이 화면에 표시될 때마다 기본 상태로 설정하게 된다. 때문에 이전 선택 상태가 재사용되지 않기 때문에 중복 선택되는 일이 없어지게 된다.
두 번째 방법은 prepareForReuse() 메소드를 오버라이드하여 사용하여 셀이 재사용되기 전의 상태로 초기화하는 것이다.
class CategoryCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
override func prepareForReuse() {
super.prepareForReuse()
// 셀 상태 초기화
backgroundColor = UIColor.lightGray // 기본 배경색
label.textColor = UIColor.black // 기본 텍스트 색상
}
}
이렇게 하면 셀이 재사용될 때, 선택 상태나 강조 효과를 초기 상태로 되돌리게 된다. 이 방법은 주로 커스텀 셀을 정의할 때 매우 효과적이다.
나의 경우 커스텀으로 셀을 만들기 때문에 후자의 방법이 좋다고 생각했고, 바로 코드에 적용시켜 보았다.
/// 컬렉션 뷰의 셀을 재사용하지 못하도록 리셋
override func prepareForReuse() {
super.prepareForReuse()
categoryLabel.text = "Vegan"
categoryLabel.textAlignment = .center
categoryLabel.font = UIFont.systemFont(ofSize: 15, weight: .regular)
categoryLabel.textColor = UIColor.categoryText
categoryLabel.backgroundColor = UIColor.category
categoryLabel.clipsToBounds = true
categoryLabel.layer.cornerRadius = 20
}

드디어 원하는 대로 앱이 구동이 되었다...
아직 남은 숙제가 남아있긴 하지만... 오늘은 이쯤에서 마무리 하기로 했다.
오늘은 팀 프로젝트의 일환으로 카테고리의 구현을 진행하였다.
컬렉션 뷰의 사용은 이번이 두 번째인데 여전히 어려운 것 같다...
뷰와 뷰 사이의 관계에 대해서도 많은 생각을 하게 된 것 같다.
상경님 글 읽다보면 상경님 목소리 들려요 신기방기. 우리의 든든한 코드리뷰어 상경님 화이팅이에여!!!!1