ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Compositional Layout + Diffable DataSource - 1 (배너타입, 작은 리스트)
    카테고리 없음 2022. 11. 25. 18:14

    쿠팡이츠 메인 피드 화면처럼 콜렉션뷰를 구현해보려 한다.

    맨위에 배너가 있고 그 아래 작은 리스트, 그 아래 사각형의 리스트가있다.

    그리고 특이점은 모든 형태가 다 다른 타입의 데이터를 가지고 있다

    배너 - 배너

    작은 리스트 - 음식종류(category)

    사각형 리스트 - 식당이름(restaurant)

    Section으로 섹션 타입을 구분하지 않고 Item 으로 구분하기로 했다.

    struct Section: Hashable {
        let id: String
    }
    
    enum Item: Hashable {
        case banner(HomeItem)
        case normalCarousel(HomeItem)
        case circleCarousel(HomeItem)
    }
    
    struct HomeItem: Hashable {
        var text: String? = ""
        var name: String? = ""
        let imageUrl: String
    }
    

    데이터는 HomeItem으로 피드 리스트 에서는 거의 이미지 + 텍스트 구조이고 섹션별로 크게 다르지않은 데이터구성이라 이렇게 했다.

    우선은 고정적인 섹션 타입만 구현해볼것이다. 첫번쨰 섹션에는 배너 두번째는 카테고리 타입을 넣는다.

    private func createLayout() -> UICollectionViewCompositionalLayout{
            return UICollectionViewCompositionalLayout(sectionProvider: {[weak self] sectionIndex, environment in
                
                switch sectionIndex {
                case 0:
                    return self?.createBannerSection()
                case 1:
                    return self?.createCategorySection()
    
                default:
                    return self?.createBannerSection()
                }
                
            })
        }
    

    문제점?

    만약 실제로 0번인덱스에 배너타입이 들어가지 않고 동적으로 알맞는 섹션을 그려줘야하면 어떻게 해야할까?

    layout을 지정해줄때

    UICollectionViewCompositionalLayout(sectionProvider: { sectionIndex, environment in
    

    섹션의 index에만 접근할수있다. item에도 접근 할수가 없다.

    지금까지 찾은 방법은 개별적으로 섹션 타입을가진 배열을 가지고있다가 쓰는 방법밖에 떠오르지 않는다 추후에 구현해보자.

    레이아웃

    두가지 레이아웃은 이렇게 구현했다.

    private func createBannerSection() -> NSCollectionLayoutSection {
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(200))
    
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    
            let section = NSCollectionLayoutSection(group: group)
            section.orthogonalScrollingBehavior = .groupPaging
            return section
        }
        
        private func createCategorySection() -> NSCollectionLayoutSection {
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.25), heightDimension: .fractionalHeight(1.0))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            item.contentInsets = NSDirectionalEdgeInsets(top: 15, leading: 25, bottom: 15, trailing: 0)
    
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(150))
    
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            section.orthogonalScrollingBehavior = .continuous
            return section
        }
    

     

     

    DataSource구현내용이다. 여기서 item을 받아와 해당 item의 타입에따라 맞는 셀을 그려준다. 

    private func setDataSource() {
            
            dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { (collectionView, indexPath, item ) -> UICollectionViewCell? in
                print("item \\(item)")
                switch item {
                case .banner(let data):
                    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BannerCell", for: indexPath) as? BannerCollectionViewCell else {fatalError()}
                    if let text = data.text {
                        cell.configure(text: text, url: data.imageUrl)
                    }
                    return cell
                case .normalCarousel(let data):
                    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCell", for: indexPath) as? CategoryCollectionViewCell else {fatalError()}
                    if let name = data.name {
                        cell.configure(name: name, url: data.imageUrl)
                    }
                    return cell
                default:
                    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BannerCell", for: indexPath) as? BannerCollectionViewCell else {fatalError()}
                    return cell
                }
            }
            
            snapshot()
    
        }
    

    아직 하나라 default도 같은 셀이다 나중에 다른 셀 넣을예정~

    데이터소스 구현후 snapshot() 호출한다 여기서 스냅샷만들어서 apply()하면 셀이 그려질것이다.

    private func snapshot() {
            var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
            snapshot.appendSections([Section(id: "banner")])
            snapshot.appendItems([
                Item.banner(HomeItem(text: "Banner One", imageUrl: placeHolderUrl)),
                Item.banner(HomeItem(text: "Banner Two", imageUrl: placeHolderUrl)),
                Item.banner(HomeItem(text: "Banner Three", imageUrl: placeHolderUrl))
                ])
            
            snapshot.appendSections([Section(id: "category")])
            snapshot.appendItems([
                Item.normalCarousel(HomeItem(name: "포장", imageUrl: placeHolderUrl)),
                Item.normalCarousel(HomeItem(name: "신규맛집", imageUrl: placeHolderUrl)),
                Item.normalCarousel(HomeItem(name: "1인분", imageUrl: placeHolderUrl)),
                Item.normalCarousel(HomeItem(name: "한식", imageUrl: placeHolderUrl)),
                Item.normalCarousel(HomeItem(name: "치킨", imageUrl: placeHolderUrl)),
                Item.normalCarousel(HomeItem(name: "분식", imageUrl: placeHolderUrl)),
                ])
            
            dataSource?.apply(snapshot)
        }
    

    실제 API 호출한다면 api 응답을 받아올때 해당 함수를 실행시켜야 할것이다.

    이후에는 다른 유형의 섹션 UI를 구현해보고 

    추가적으로 영화 API와 연동 하여 구현할 계획이다.

     

    이미지가 없어서 허접해 보이긴 한다. 

    댓글

Designed by Tistory.