[iOS]CollectionView Layout

이슈 💣

  • 기존 Collection View Cell이 데이터 개수에 맞춰 동적으로 높이 변경이 이뤄져야 했다.
  • 쓴이의 구현은 Layout이 겹침으로 오류가 발생해 원활하게 적용이 되질 않았다.

쓴이의 구현 👨🏻‍💻

  • sizeForItemAt을 이용하여 Cell의 기본 높이 값을 부여하고, cellForItemAt에서 Cell의 데이터 개수에 맞게 Height 값을 Cell로 보내서 Cell의 높이를 바꿔주려 했다.
  • 아래는 CollectionView의 extension 코드로 sizeForItemAt & cellForItemAt의 해당 내용 부분만 보여줍니다. ▾
// cellForItemAt
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
	guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "timelineCell", for: indexPath) as? OverallTimeLineCollectionViewCell else { return UICollectionViewCell() }

	let oneHeight = CGFloat(157)
	let defaultHeight = CGFloat(88)
    
	if reportDataLst?.reports.count == 1 {
	    cell.configureHeight(with: oneHeight)
	    cvHeight.constant = self.studyAnalysisCV.collectionViewLayout.collectionViewContentSize.height
	} else if reportDataLst?.reports.count == 2 {
	    let height = oneHeight + defaultHeight
	    cell.configureHeight(with: height)
	    cvHeight.constant = self.studyAnalysisCV.collectionViewLayout.collectionViewContentSize.height + height
	    cvHeight.constant = self.studyAnalysisCV.collectionViewLayout.collectionViewContentSize.height + 216
	} else {
	    let height = oneHeight + defaultHeight * 2
	    cell.configureHeight(with: height)
	    cvHeight.constant = self.studyAnalysisCV.collectionViewLayout.collectionViewContentSize.height + 333
	}

	cell.configure(with: reportDataLst!)
	
	return cell
}
// sizeForItemAt
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = self.studyAnalysisCV.frame.width
    if reportDataLst?.reports.count != 0 {
        switch indexPath.row {
        case 0:
            return CGSize(width: width, height: 157)
        case 1:
            return CGSize(width: width, height: 157)
        default:
            return CGSize(width: width, height: 130)
        }
    } else {
        return CGSize(width: width, height: 130)
    }
}
  • 해당 Cell에서는 아래와 같이 구현했다. ▾
func configureHeight(with height: CGFloat) {
	bgView.heightAnchor.constraint(equalToConstant: height).isActive = true
}

문제 ⚠️

"<NSLayoutConstraint:0x600000f5dfe0 UIView:0x138d1b1f0.height == 157   (active)>",
"<NSLayoutConstraint:0x600000f541e0 UIView:0x138d1b1f0.height == 333   (active)>"
  • 위와 같이 구현을 하면 아래와 같은 결과 화면과 함께 오류 문구를 확인할 수 있습니다.
  • 위 오류는 "Cell의 크기는 157 값과 같아야 하며, 333 값과도 같아야 한다"라는 내용입니다.
  • 위 코드에서 알 수 있듯 Layout을 157값으로 주었지만 데이터 개수에 따라 Cell 높이 값을 변경해주었고, 다른 날을 선택하면 Collection View 역시 새로고침이 되므로 데이터가 3개 이상일 경우, 계속해서 Layout 오류가 발생하게 됩니다.

해결하기 ✅

  • 이 문제에 핵심은 Layout 값이 다른 값으로 중복이 되는 부분이라고 생각을 했다.
  • Cell에서 높이를 지정해주며, Collection View FlowLayout에서도 높이를 지정해주고 있습니다.
  • 그래서 높이 지정을 한 곳에서 한번만 해줄 수 있게 수정해보았습니다.
// cellForItemAt
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
	guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "timelineCell", for: indexPath) as? OverallTimeLineCollectionViewCell else { return UICollectionViewCell() }
	cell.configure(with: reportDataLst!)
	return cell
}


// sizeForItemAt
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = self.studyAnalysisCV.frame.width
    if reportDataLst?.reports.count != 0 {
        switch indexPath.row {
        case 0:
            return CGSize(width: width, height: 157)
        case 1:
			if reportDataLst?.reports.count == 1 {
                return CGSize(width: width, height: 157)
            } else if reportDataLst?.reports.count == 2 {
                return CGSize(width: width, height: 216)
            } else {
                return CGSize(width: width, height: 333)
            }
        default:
            return CGSize(width: width, height: 130)
        }
    } else {
        return CGSize(width: width, height: 130)
    }
}
  • 단순한 문제로 보일 수 있으나 Collection View와 Cell안에도 Collection View를 구현하고, UISegmentedControl을 이용해 2개의 화면에 다른 뷰를 보이게 하는 부분과 전반적인 Scroll View의 스크롤과 Collection View의 스크롤도 동기화시켜주어야 했기에 쓴이(iOS 초보)는 결코 단순한 문제로 여길 수 없었다.
  • 아무튼!! 위와 같이 구현을 함으로써 정상적인 UI 구현을 맞칠 수 있었다.

Layout 해결

어디까지나 쓴이의 경험이며, 다른 방법으로도 구현이 가능한 부분입니다.
다른 의견이 있으실 경우 댓글로 남겨주시면 감사합니다. 😀

'iOS_Swift.zip' 카테고리의 다른 글

[iOS]카카오 로그인(소셜)  (0) 2022.01.25
[iOS]면접 정리(1)  (1) 2022.01.20
[iOS]HIG(Human Interface Guide)  (0) 2022.01.10
[iOS]SOLID 원칙  (0) 2022.01.07
[iOS]옵저버 패턴(Observer Pattern)  (0) 2022.01.05