ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 확장 축소 가능한 동적 높이 TableView 구현 하기
    개발/Swift 2023. 4. 16. 13:46

    Modern Collection View 와 MVVM 패턴 가이드

     

    [iOS] Modern Collection View & MVVM 패턴 가이드 - 인프런 | 강의

    MVVM 패턴과 Modern Collection View를 사용해 네트워킹을 구현하고, 다양하고 동적인 Collection View를 자유자재로 다룰 수 있게 됩니다., - 강의 소개 | 인프런

    www.inflearn.com

    동적 높이 셀

    동적 높이 테이블뷰

    확장 축소 가능 테이블뷰

    Table view높이 구하기

     

    테이블뷰의 높이가 컨텐츠에따라 유동적으로 설정이 되려면 높이를 지정해주지 않고 제약조건을 잘 설정 해주면 된다.

    titleLabel.snp.makeConstraints { make in
                make.top.equalTo(9)
                make.leading.equalToSuperview()
                make.trailing.equalTo(expandButton.snp_leading).offset(-8)
            }
            borderView.snp.makeConstraints { make in
                make.top.equalTo(titleLabel.snp_bottom).offset(9)
                make.height.equalTo(1)
                make.leading.trailing.equalToSuperview()
            }
            descLabel.snp.makeConstraints { make in
                make.top.equalTo(borderView.snp_bottom).offset(8)
                make.leading.trailing.bottom.equalToSuperview()
            }
    

    Label 같은 경우는 높이가 컨텐츠에따라 유동적으로 변하므로

    Top Bottom 에 관련한 제약조건을 잘 설정해 줘야하고

    그렇지 않은 View는 높이를 지정해줘서 세로 제약조건이 모두 설정이 된다면

    별다른 설정 없이도 Dynamic Height Cell을 구현 할수 있다.

    Row Height , Estimate row height 이런거 안써도 됨

    그리고 역시 TableView에 대한 제약조건도 역시 설정이 되어야한다.

    TableView에 제약조건이 제대로 먹혀있지 않으면 테이블 셀 자체가 그려지지 않는다.

    Expandable Cell 을 구현 하기 위해 Title Label , Desc Label 두개로 나누었으며

    Collapse 된 상황에서는 Desc Label의 text를 빈값으로 바꾸고

    Expand 상황에서는 text 를 올바르게 넣어줌으로써 확장 축소를 구현했다.

    public func configure(title: String, desc: String, expand: Bool?) {
            titleLabel.text = title
            if let expand = expand, expand {
                setExpandedUI(desc: desc)
            } else {
                setCollapseUI()
            }
        }
        private func setCollapseUI() {
            titleLabel.numberOfLines = 1
            expandButton.setImage(UIImage(named: "icDownArrow16"), for: .normal)
            descLabel.text = ""
        }
        
        private func setExpandedUI(desc: String) {
            titleLabel.numberOfLines = 2
            expandButton.setImage(UIImage(named: "icUpArrow16"), for: .normal)
            descLabel.text = desc
    
        }
    

    그러기 위해서 확장 축소 이벤트가 발생할 때마다

    configure() 함수를 호출 시키고 expand 값을 전달 해줘야한다.

    Observable.combineLatest(Observable.just(data), expandIndex.asObservable())
                .map { list, expandIndex in
                    var arr: [Data] = []
                    list.enumerated().forEach {
                        arr.append(Data(title: $0.element.title, desc: $0.element.desc,
                                        expanded: $0.offset == expandIndex))
                    }
                    return arr
                }.bind(to: expandableTableView.rx
                    .items(cellIdentifier: ExpandableDialogTableViewCell.id)) {[weak self] index, data, cell in
                        guard let cell = cell as? ExpandableDialogTableViewCell else { return }
                        cell.configure(title: data.title, desc: data.desc, expand: data.expanded)
                        cell.expandButton.rx.tap.bind { _ in
                            self?.expandIndex.accept(index)
                        }.disposed(by: cell.disposeBag)
    
                }.disposed(by: disposeBag)
    

    Rx + TableView 를 사용해 구현헀다.

    data 에는 전달할 컨텐츠 데이터가 들어있다.

    expandIndex는 PublishRelay인데 int값을 전달한다.

    확장 버튼을 누르면 해당 셀의 인덱스가 들어오고 그것에따라 expand 값을 넣어주었다.

    TableView를 구현 하기 위해서 TableView 의 제약조건이나

    Tableview의 부모 제약조건이 잘 설정 되어 있어야 한다.

    그렇지 않으면 Cell이 하나도 그려지지 않는다.

    위의경우 Tableview가 ContentView로 감싸져 있는 상황이었다.

    ContentView 를 모달 처럼 투명 검정 배경에 띄우려는데

    모달 안에 Table view 가있는 상황이다.

    만약 여기서 더 나아가서

    Cell 이아니라 Table View 자체의 높이가 유동적으로

    변경되어야 한다면?

    그러면 Content View이건 Table view이건 둘중 하나라도

    고정 적인 높이가 지정되어 있어야 한다

    contentView.snp.makeConstraints { make in
                make.leading.equalToSuperview().offset(16)
                make.trailing.equalToSuperview().offset(-16)
                make.centerY.equalToSuperview()
            }
            tableView.snp.makeConstraints { make in
                make.top.equalTo(closebutton.snp_bottom).offset(24)
                make.leading.equalTo(16)
                make.trailing.equalTo(-16)
                make.bottom.equalToSuperview().offset(-56)
                make.height.equalTo(500)
            }
    

    나중에 Cell을 그리고 높이를 계산해서 높이를 다시 계산한다 한들

    처음에 높이가 지정되어 있지 않으면

    Cell 자체가 그려지지 않기 때문이다.

    Cell의 높이를 구해서 유동적으로 Table view의 높이를 지정해줘야함
     -> Cell 이 그려져야함
     -> TableView 의 높이가 지정되야함
     -> ?? 높이 모름
    

    별짓 다해서 구현해본 결과 방법은 이것 뿐이었다.

    결론은 Default 높이를 지정해준뒤 일단 table view를 그리고

    높이를 다시 계산해서 다시 높이를 지정해주는것이다.

    그래서 위처럼 테이블뷰 높이를 500으로 고정 시켜둔 것이다. 그렇게 해서 우선

    table view의 셀들이 그려지게 한후

    높이를 다시 받아서 사용하는 것이다.

    높이를 받아오는 여러방법들 다 써봤는데 다 이상하게 나왔고

    결국 Observe 를 쓰는게 제일 정확했다. (정확한 이름 까먹음)

    tableView.rx.observe(CGSize.self, #keyPath(UITableView.contentSize)).bind { [weak self] size in
                guard let s = self, let height = size?.height else { return }
                s.tableView.snp.remakeConstraints { make in
                    make.top.equalTo(s.closebutton.snp_bottom).offset(24)
                    make.leading.equalTo(16)
                    make.trailing.equalTo(-16)
                    make.bottom.equalToSuperview().offset(-56)
                    make.height.equalTo(height)
                }
                
            }.disposed(by: disposeBag)
    

    이렇게 사이즈를 재조정 하면 됨!!

    댓글

Designed by Tistory.