-
Collection View가 들어있는 동적 높이의 Table view Cell 구현하기개발/Swift 2023. 6. 18. 17:48
Table view cell 안에 Collection view를 넣어 Grid나 Horizontal 형식의 리스트를 구현해야 할 때가 있다.
기본적으로 Collection view Compositional layout 을 써도 되지만
회사에서 신기술을 쓰기 어려울 상황도 있기에...
이런 상황에 table view cell에 리스트 뿐만 아니라 하당 셀을 접고 펴는등의 액션을 구현해야 할때
결국 상황에 따라 높이가 변하는 Dynamic Height 는 피할수 없다.
Cell 을 그릴때 제약조건을 변경해서 높이를 지정해주기
접고 펴는 상태 값, 리스트에 들어가야할 데이터들 등등은 cellForRowAt이나 Rx에서 데이터 바인딩할때
(VC에서 cell을 리턴하는 시점)
Cell에 특정 함수를 통해 넣어주고 적용시키게 된다.
func apply(cellData: CellData) { ///데이터 적용 }
그럼 이때 데이터를 받아오기 때문에 데이터를 넣어주고
TableViewCell의 제약조건 높이를 지정해주면 되지 않을까?
func apply(cellData: CellData) { guard case let .birthday(isExpand) = cellData else { return } if isExpand { collectionView.snp.remakeConstraints { make in make.top.equalTo(button.snp.bottom) make.bottom.equalToSuperview() make.leading.trailing.equalToSuperview() make.height.equalTo(100) } } else { collectionView.snp.remakeConstraints { make in make.top.equalTo(button.snp.bottom) make.bottom.equalToSuperview() make.leading.trailing.equalToSuperview() make.height.equalTo(0) } } }
cellData 에서 확장상태 값을 받아와서 높이를 지정해줬다.
이런경우 동작은 잘 한다.
확장 축소 둘다 동작은 하나 축소 상태에서 확장할때
제약조건 충돌 콘솔에러가 발생한다.
기존 높이 Cell의 높이가 33 인데 그 안에 Colletionview의 높이를 100으로 지정해주려 하기 떄문이다.
Table view 의 HeightForRowAt 델리게이트 함수를 써도 마찬가지 였다.
Collection view의 높이를 지정해주지 않고 Collection view Cell에 데이터를 넣어주기
만약 동적 높이를 사용하기 위해 높이를 지정해 주지 않고 그 안에 데이터를 계속 넣어줘서 cell의 갯수가 늘면
자연스럽게 높이가 높아지지 않을까?
var cellData: [String] = [] { didSet { collectionView.reloadData() } } //cell 에 데이터 적용하는 함수(data) func apply(cellData: CellData) { //cellData를 업데이트 해주면 자동으로 reload Collection view self.cellData.append(contentsOf: data) }
높이를 지정해주지 않은 Collection view (데이터 들어오기전 0) 에 reload를 해도 Cell이 보여지지 않았다
하지만 cell의 크기, cell의 갯수를 지정하는,cell 을 리턴해주는
collection view delegate 함수들은 돌고있는것이 확인이 되었다.
Collection View 커스텀하기
collection view의 사이즈를 재 조정하기 위해
Collection view 를 커스텀 해야한다.
기본적으로 collection view에 데이터를 넣어주고 reloadData()
해주게 되면 instrinsic Content size를 사용해서 높이를 지정해 그려주는데
이때 문제는 데이터가 넣어져도 instrinsic Content size가 0을 리턴한다.
class DynamicHeightCollectionView: UICollectionView { override func layoutSubviews() { super.layoutSubviews() if !(__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize)){ self.invalidateIntrinsicContentSize() } } override var intrinsicContentSize: CGSize { return contentSize } }
ㅇ위 로직에서 layoutSubviews를 보면
bound size 와 intrinsicContentSize 를 비교하는데 이때
collection view의 intrinsicContentSize 가 들어가야할 사이즈와 다르면
invalidateIntrinsicContentSize() 를 호출하는데 이 함수는
intrinsicContentSize 지금 사이즈를 무효화 하고 다시 계산하여 조정한다.
그래서 collection view가 reload 될때 (데이터가 추가, 제거 등등)
collection view layoutSubview 가 호출되고 여기서 비교후에
contentSize 를 재계산하도록 시키는것이다.
근데 재조정할때 TableView cell 의 레이아웃을 다시 잡지 않으면 문제가 생긴다.
collection view의 높이가 늘어나도 table cell의 높이가 그대로가 되어버리기 떄문이다.
var cellData: [String] = [] { didSet { collectionView.reloadData() layoutIfNeeded() } }
이때 reload 후에 layoutIfneed 호출하면
위에서 비교하기 위해 사용했던 bounds.size 도 변하기에 알맞게 비교 할수도 있다.
자동적으로 호출 된다.
'개발 > Swift' 카테고리의 다른 글
Rxswift combineLatest 에러 The compiler is unable to type-check this expression in reasonable time (0) 2023.07.01 커스텀 UISlider 구현하기 (세로 슬라이드, 이벤트) (0) 2023.06.24 Tableview와 스크롤을 다루는 방법 (0) 2023.05.31 Swift Bottom Sheet 바텀시트 구현하는 방법 + 동적 높이 할당 (0) 2023.05.24 Swift Tooltip 구현하는 방법 (2) 2023.05.21