배경
프로젝트를 진행하던 도중..
CollectionViewCell안 CollectionViewCell의 내용이 재사용되어 정확한 셀의 내용을 포함하지 않게 되는 버그를 발견하였습니다.
#관광지는 아래 해시태그원에 관광지가 포함되어야 하고,
#음식은 음식, #숙박은 숙박이 포함되어야 합니다.
하지만 처음 검색 후 데이터를 불러왔을 때, 생성된 셀을 바탕으로 재사용된 버그를 발견하였습니다.
시도해 본 방법
CollectionViewCell은 커스텀 셀을 사용하기 위해 재사용 셀을 사용했습니다.
// 재사용 셀 지정
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SearchCollectionViewCell.identifier, for: indexPath) as? SearchCollectionViewCell else { return UICollectionViewCell() }
이는 셀이 불러오는 과정에서 이전에 사용됐던 셀을 큐에 넣고 재사용한다는 것을 의미합니다.
그 결과 위와 같은 버그가 발생했던 것입니다.
이 버그는 셀이 재사용되므로 재사용되는 셀 안에 있는 UI Property의 초기값들을 미리 지정해 두는 방법으로 해결했었습니다.
// 셀이 재사용될 준비가 되었을 때 함수 실행
// 셀안의 UI들의 기본값을 지정
override func prepareForReuse() {
self.rankImageView.image = nil
self.rankImageView.snp.remakeConstraints { make in
make.width.height.equalTo(0)
}
}
UI Property는 초기값을 지정해주었지만
CollectionView는 어떻게 초기값을 지정해 줄지 고민이 되었습니다.
Cell의 구성단계를 다시 훑어보았습니다.
Custom Cell의 UI 구성 단계
1. 재사용 셀을 지정한다. (셀 안의 UI Property 초기화)
// 재사용 셀 지정
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SearchCollectionViewCell.identifier, for: indexPath) as? SearchCollectionViewCell else { return UICollectionViewCell() }
2. 셀의 indexPath에 맞춰 UI를 구성하는 configure 함수 실행한다.
// configure 실행
cell.configure(model: viewModel.filteredAddressArray[indexPath.row])
3. configure 함수 내에서는 UI를 구성한다. 여기서 Cell 안 Cell을 지정하기 위해 값을 전달합니다.
// configure 함수 내부
func configure(model: TripItem) {
self.areaAddressLabel.text = model.relatedAreaAddress
self.areaCategoryLabel.text = "# \(model.relatedLargeCategoryName) \(model.areaName)"
let separatedRelatedAreaText = model.relatedAreaName.split(separator: "/").map{String($0)}
if separatedRelatedAreaText.count == 1 {
self.relatedAreaLabel.text = separatedRelatedAreaText.first
} else {
self.relatedAreaLabel.text = "\(separatedRelatedAreaText[0]) (\(separatedRelatedAreaText[1]))"
}
switch model.rankNum {
case "1":
self.rankImageView.image = UIImage(named: "firstPlaceRibbon")
showRankImage()
case "2":
self.rankImageView.image = UIImage(named: "secondPlaceRibbon")
showRankImage()
case "3":
self.rankImageView.image = UIImage(named: "thirdPlaceRibbon")
showRankImage()
default:
self.rankImageView.image = nil
hideRankImage()
}
let labels = [model.relatedLargeCategoryName, model.relatedMediumCategoryName, model.relatedSmallCategoryName]
let labelSet = Array(Set(labels))
// Cell 안의 CollectionViewCell의 Custom Cell을 구성하기 위한 데이터 전달
categoryCollectionView.updateLabels(labels: labelSet)
}
4. Cell 안의 CollectionView는 전달받은 데이터를 이용해 위와 같이 Custom Cell을 구성한다.
셀 안의 셀의 내용을 바꾸려면 데이터를 바꿔줘야 합니다.
이를 셀 구성할 때마다 데이터를 변경해 주면 어떨까 해서 적용해 보았습니다.
CustomCell을 구성하여 데이터 바인딩을 했을 때, 데이터가 변경돼도 셀의 내용은 바뀌지 않으니 항상 reloadData함수와 함께 사용되었습니다.
이를 값이 변경됨을 탐지하여 reloadData를 호출하도록 다음과 같이 구성하였습니다.
override func prepareForReuse() {
self.rankImageView.image = nil
self.rankImageView.snp.remakeConstraints { make in
make.width.height.equalTo(0)
}
// 재사용이 되기전에 데이터를 초기화 해준다.
categoryCollectionView.updateLabels(labels: [])
}
var categorys: [String] = [] {
didSet {
collectionView.reloadData()
}
}
결과
셀을 구성할 때마다 셀 안의 셀이 새로고침되어 정확한 데이터가 바인딩이 되었습니다
생각보다 단순하게 해결할 수 있는 버그였던 거 같습니다!
'iOS' 카테고리의 다른 글
URL Loading System 번역 (2) | 2024.12.10 |
---|---|
SwiftGen 사용 (0) | 2024.11.26 |
[iOS] Alamofire 문서번역 (0) | 2024.11.06 |
iOS - JSON파일 추가해서 불러오기 (0) | 2024.11.04 |
[iOS][버그 해결] TableViewCell안에 Lottie애니메이션 재생오류 (0) | 2024.08.23 |