[iOS] 짱 쉬운 Auto Width Tag CollectionView 구현하기 with Compositional Layout

kimdocs...📄·2022년 7월 19일
1

iOS

목록 보기
5/22
post-thumbnail
결과 먼저..

우리는 이렇게 생긴 태그뷰를 만들고자 한다.

compositional layout을 이용하면 자동으로 width를 조절할 수 있고 여백에 따라 아래 행으로 줄바꿈되므로 쉽게 layout을 잡을 수 있다.

우선 기존 방법부터 살펴보자.

1. CollectionView 정의

	private let collectionView: UICollectionView = {
        let layout = LeftAlignedCollectionViewFlowLayout()
        layout.minimumLineSpacing = 12
        layout.minimumInteritemSpacing = 8
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.backgroundColor = Color.bbangWhite
        collectionView.showsVerticalScrollIndicator = false
        collectionView.register(cell: TagCell.self)
        collectionView.contentInset = UIEdgeInsets(top: .zero, left: 16, bottom: 76, right: 16)
        return collectionView
    }()

2. Delegation

collectionView.delegate = self

3. UICollectionViewDelegateFlowLayout

extension MakeBreadTagViewController: UICollectionViewDelegateFlowLayout {
   func collectionView(
       _ collectionView: UICollectionView,
       layout collectionViewLayout: UICollectionViewLayout,
       sizeForItemAt indexPath: IndexPath
   ) -> CGSize {
       let label = UILabel().then {
           $0.font = NotoSansKR.regular.of(size: 16)
           $0.text = tagList[indexPath.item]
           $0.sizeToFit()
       }
       let size = label.frame.size
       return CGSize(width: size.width + 60, height: size.height + 32)
   }
}

cell 사이즈를 동적으로 계산해줄 수 없기 때문에 해당 폰트의 크기를 계산, 좌우 여백을 더하여 각각의 Cell을 지정해준다.

4. LeftAlignedCollectionViewFlowLayout

그러나 우리는 왼쪽으로 정렬된 Tag View를 만들고 싶으므로 UICollectionViewFlowLayout을 상속받아서 custom해준다.

private class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)
        var leftMargin = sectionInset.left
        var maxY: CGFloat = -1.0
        attributes?.forEach { layoutAttribute in
            if layoutAttribute.representedElementCategory == .cell {
                if layoutAttribute.frame.origin.y >= maxY {
                    leftMargin = sectionInset.left
                }
                layoutAttribute.frame.origin.x = leftMargin
                leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
                maxY = max(layoutAttribute.frame.maxY, maxY)
            }
        }
        return attributes
    }
}

끝! 구현은 했지만 뭔가 찝찝하다. 필요 없는 UILabel를 선언하여 크기를 구하고, 왼쪽 정렬하는 layout을 따로 정의하고.. 다른 방법이 있을 게 분명해!!

Compositional layout을 써보자

1. CollectionView 정의

	private lazy var collectionView: UICollectionView = {
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: createTagLayout())
        collectionView.backgroundColor = Color.bbangWhite
        collectionView.showsVerticalScrollIndicator = false
        collectionView.register(cell: TagCell.self)
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.showsVerticalScrollIndicator = false
        return collectionView
    }()

2. layout을 위한 Delegation은 필요없다

3. createTagLayout()

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

       let item = NSCollectionLayoutItem(layoutSize: itemSize)

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

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

       let section = NSCollectionLayoutSection(group: group)
       section.interGroupSpacing = 12
       section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 76, trailing: 16)

       let config = UICollectionViewCompositionalLayoutConfiguration()
       config.scrollDirection = .vertical

       let layout = UICollectionViewCompositionalLayout(section: section)
       layout.configuration = config
       return layout
   }
}

너무 간단하게 끝!
자동으로 사이즈 계산이 되기때문에 Cell만 AutoLayout을 잘 잡아준다면, 자동으로 잘 잡힐것이다.

profile
👩‍🌾 GitHub: ezidayzi / 📂 Contact: ezidayzi@gmail.com

3개의 댓글

comment-user-thumbnail
2023년 4월 29일

Hi I am using a solution similar to your post. But I'm having issues. This is my stackoverflow link, can you check it out?

1개의 답글
comment-user-thumbnail
2023년 10월 6일

안녕하세요
테그 컬렉션 뷰에 추가로 다른 사이즈의 셀이 필요하게 되어 LeftAlignFlowLayout -> compositional Layout으로 변경하고 있습니다.
compositional layout에서는 어떤 코드로 left align이 적용되는 건가요? 만약 left align이 아니라 center align을 하고 싶다면 어떤 부분을 변경해야 할까요?

답글 달기