티스토리 뷰

텍스트의 길이에 따라 가변적으로 변하는 collectionViewCell을 적용하고 싶어 CompositionalLayout을 적용해 보았다.

CompositionalLayout을 사용하기 전에 구현했던 방법은, 이러했다.

 

ShoppingListViewController

 output.searchItems
    .asDriver(onErrorJustReturn: [])
    .drive(collectionView.rx.items(cellIdentifier: SearchItemCollectionViewCell.identifier, cellType: SearchItemCollectionViewCell.self)){ row, element, cell in
        cell.titleLabel.text = element
    }
    .disposed(by: disposeBag)
extension ShoppingListViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = viewModel.searchItems.value[indexPath.row].calculateTextWidth(size: 12) + 20
        return CGSize(width: width, height: 40)
    }
}

 

extension String

extension String {
    func calculateTextWidth(size: CGFloat) -> CGFloat {
        let itemSize = self.size(withAttributes: [
            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: size)
        ])
        return itemSize.width
    }
}

 

구현을 하면서 텍스트의 크기를 알아내려고 viewModel로 접근해 데이터를 가져와 변환해주는게 MVVM패턴에 맞는것일까 의문이 들어 그럴 필요 없이 가변적으로 변경해주는 CompositionalLayout을 적용해보기로 했다. 더불어서 Rx답게 RxdataSource를 사용해보기로 했다.

 

RxDataSources

1. DataSource 설치

RxDataSources를 SPM을 통해 받고 import 해준다.

 

2. SectionModel 구현

리드미를 보면 알 수 있듯이, SectionModel을 구현해줘야한다.

 

items에는 해당하는 Item의 타입을, header에 들어갈 값이 있다면 init에 구현하여 값을 전달해준다.

import 후 SectionModelType을 채택하면 필수로 구현해야 하는 함수 init(original:items:) 를 구현해준다.

import Foundation
import RxDataSources

struct SearchResultSectionModel {
    //var header: String
    var items: [String]
    
    init(items: [String]/* , header: String*/){
        //self.header = header
        self.items = items
    }
}

extension SearchResultSectionModel: SectionModelType {
    init(original: SearchResultSectionModel, items: [String]) {
        self = original
        self.items = items
    }
}

 

3. DataSource 구현

RxCollectionViewSectionReloadDataSource의 제네릭타입에 구현해준 SectionModel을 지정해준다.

RxCollectionViewSectionReloadDataSource말고도 여러개가 있긴하다.

private lazy var dataSource = RxCollectionViewSectionedReloadDataSource<SearchResultSectionModel>(
    configureCell: { dataSource, tableView, indexPath, item in
        let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: SearchItemCollectionViewCell.identifier, for: indexPath) as! SearchItemCollectionViewCell
        cell.titleLabel.text = item
        return cell
})

 

4. 적용

아래처럼 적용해주면 끝! item을 사용해서 구현했을 때보다 간단해졌다.

output.searchItems
    .asDriver()
    .drive(collectionView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)

 

 

CompositionalLayout 사용해보기

  private lazy var collectionView = {
    let object = UICollectionView(frame: .zero, collectionViewLayout: compositionalLayout())
    object.register(SearchItemCollectionViewCell.self, forCellWithReuseIdentifier: SearchItemCollectionViewCell.identifier)
    return object
}()

 

UICollectionViewDelegateFlowLayout 채택을 위해 사용했던 object.delegate = self를 제거해준다.

 

UICollectionViewLayout을 반환하는 compositional함수를 구현해준다.

CompositionalLayout을 구현할때는 return을 적고 차근차근 위로 올라가면서 구현하는 것이 덜 헷갈린다.

private func compositionalLayout() -> UICollectionViewLayout {
    let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(60), heightDimension: .absolute(40))

    let item = NSCollectionLayoutItem(layoutSize: itemSize)

    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(60))

    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    group.interItemSpacing = .fixed(12)

    let section = NSCollectionLayoutSection(group: group)

    let layout = UICollectionViewCompositionalLayout(section: section)

    return layout
}

 

 

layoutSize를 정하는 옵션에는 여러가지가 있다.

 

  • estimated: 항목 크기를 예상할 때
  • absolute: 항목의 절대 크기를 지정
  • fractionalHeight/fractionalWidth: 항목의 높이/너비를 부모 컨테이너의 너비에 대한 비율로 설정

Dynamic하게 너비를 지정할 수 있었던 이유는 estimated로 설정했기 때문이다.

 


CompositionalLayout은 처음 배울 때 낯설게 느껴져서 손이 잘 안갔었는데, 아이템에 대한 설정이 왜 필요한지 올라가면서 구현해 본것이 많은 도움이 된 것 같다. 간단한 구현이었지만 하나두개씩 CompositionalLayout과 RxDataSource를 사용해보면서 사용 방법을 넓혀 가는것이 도움이 많이 될 것 같다. 화이팅!!

'🌱 SeSac' 카테고리의 다른 글

CompositionalLayout + CustomHeader  (0) 2024.09.02
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
글 보관함