ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 도 변하기에 알맞게 비교 할수도 있다.

    자동적으로 호출 된다.

    댓글

Designed by Tistory.