내일배움캠프 iOS

UIKit) TIL # 51 앱개발 심화주차 개인과제 - Lv 1,2 컬렉션뷰 CompositinalLayout

yjuni22 2024. 12. 31. 21:06

Lv 1 화면 구성

1-1 파일 및 디렉토리 분리

 

우선 기획에 맞게 대략적인 프로젝트의 파일과 폴더를 생성하였다.

 

1-2 SceneDelegate 에서 탭바 생성

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: windowScene)
        
        let tabBarVC = UITabBarController()
        
        let vc1 = SearchTapViewController()
        let vc2 = SavedTapViewController()
        
        vc1.title = "검색 탭"
        vc2.title = "담은 책 리스트 탭"
        
        tabBarVC.setViewControllers([vc1, vc2], animated: false)
        tabBarVC.modalPresentationStyle = .fullScreen
        
        window?.rootViewController = tabBarVC
        window?.makeKeyAndVisible()
    }

 

필요한 탭이 두개이고 그에 맞게 뷰컨트롤러 두개를

SceneDelegate 에서 탭바를 생성한 뒤 설정해 주었다.

 

1-3 검색 탭 UI 구현

검색 탭에는 서치바와 컬렉션뷰 (최근검색, 검색결과) 가 들어간다.

 

SearchTapView 에 서치바와 컬렉션뷰를 구현하였다.

 

컬렉션뷰는 CompositionalLayout을 사용하였다.

 

FlowLayout VS CompositionalLayout

컬렉션뷰의 레이아웃은 두가지가 있다.
FlowLayout: 일렬로 깔끔하게 줄 맞춰 정렬하는 자동 정리 도구, 단순한 구현에 쉽다.
CompositionalLayout: 다양한 크기의 박스들을 조합해서 원하는 형태를 만드는 레고, 복잡한 레이아웃 구현을 간단하게 할 수 있다.

 

이번 프로젝트에서는 컬렉션뷰에 다른 모양의 두가지 섹션을 구현할 예정이므로

각각에 맞는 복잡한 레이아웃 구현에 더 적합한 CompositionalLayout 을 사용해보기로 하였다.

 

컬렉션뷰의 구성

 

컬렉션 뷰는 위와같이 섹션>그룹>아이템 으로 구성되어있다.

 

 lazy var searchCollectionView: UICollectionView = {
        let layout = createCompositionalLayout()
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        
        collectionView.register(SearchListCell.self, forCellWithReuseIdentifier: SearchListCell.id)
        collectionView.register(SectionHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: SectionHeader.id)
        
        
        collectionView.backgroundColor = .white
        return collectionView
    }()

 

컬렉션뷰 생성 클로저에서 layout을 섹션에 맞게 생성해주는 메서드를 활용하여

sectionIndex에 따라 다른 모양의 섹션을 만들 수 있도록 구현하였다.

 private func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
        return UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
            
            switch sectionIndex {
                
            case 0:
                return nil
                
            case 1:
                return self.createSearchListSection()
                
            default:
                return nil
            }
        }
    }

 

아직은 섹션 0 이 필요 없기 때문에 nil 로 리턴하게 두어 표시되지 않도록 해보았다.

 

 /// 검색결과섹션 레이아웃
    private func createSearchListSection() -> NSCollectionLayoutSection {
        
        // 아이템 크기
        let itemSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(60)
        )
        
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        
        // 그룹 크기
        let groupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(60)
        )
        
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
        
        // 아이템 사이의 간격
        group.interItemSpacing = .fixed(16)
        group.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
        
        // 섹션
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = 10
        section.contentInsets = .init(top: 10, leading: 10, bottom: 10, trailing: 10)
        
        // 헤더 추가
        let headerSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(30)
        )
        
        let header = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: headerSize,
            elementKind: UICollectionView.elementKindSectionHeader,
            alignment: .top
        )
        section.boundarySupplementaryItems = [header]
        
        return section
    }

 

섹션으로 리턴해주는 createSearchListSection 메서드 안에서 컬렉션뷰의 레이아웃을 설정해줄 수 있다.

 

아이템, 그룹, 섹션에 대한 크기 설정을 각각 해줄 수 있다.

 

.fractionalWidth : 화면 비율로 크기 설정

.absolute : 값을 직접 주어 설정

 

그룹, 섹션

group.interItemSpacing = .fixed : 아이템 사이의 간격 설정 

.contentInsets : 인셋 설정

 

section.interGroupSpacing = 10 : 그룹의 spacing 설정

 

섹션 헤더 설정

섹션에는 헤더, 푸터 가 있다. 위 아래에 들어가는 title 같은 것
이 프로젝트에서는 헤더만 사용하기 때문에 헤더의 크기도 설정해주어야 한다.

 

헤더뷰 

- 섹션의 헤더, 푸터는 UICollectionReusableView 을 상속하여 생성 후 

UICollectionViewDataSource 에서 적용해주어야 한다.

class SectionHeader: UICollectionReusableView {
    static let id = "SectionHeader"
    
    let label: UILabel = {
        let label = UILabel()
        label.font = UIFont.boldSystemFont(ofSize: 24)
        label.textColor = .black
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func configureUI() {
        addSubview(label)
        label.snp.makeConstraints { make in
            make.leading.equalToSuperview().inset(10)
        }
    }
}

 

서치바를 추가한 모습

 

서치바와 컬렉션뷰 탭바가 잘 적용된 모습이다.

 

 


 

컬렉션 뷰 레이아웃 참고

 

https://hackmd.io/@8J6ycQ90Qne5m_5VeNQWBA/rJCNBBGNa

 

CollectionView - FlowLayout vs CompositionalLayout - HackMD

<h1><center> CollectionView - FlowLayout vs CompositionalLayout </center></h1> ###### title: `Colle

hackmd.io

https://velog.io/@ddophi98/Compositional-Layout

 

Compositional Layout

Compositional Layout에 대해 정리한 내용입니다

velog.io