Archive/패캠(올인원)

ch11 현상금 랭킹앱 코드리뷰(CollectionView)

lgvv 2021. 6. 22. 16:29

  ✅ CollectionView

 

Apple developer Document

https://developer.apple.com/documentation/uikit/uicollectionview

 

Apple Developer Documentation

 

developer.apple.com

 

이번 시간에는 드디어 CollectionView에 대해서 알아보았다.

테이블 뷰는 자신 있었는데, CollectionView가 너무나도 궁금했는데, 드디어 시작!!

 

✅ 컬렉션 뷰도 결국은 테이블 뷰와 비슷한 형식을 띄고 있다.

❗️주의사항❗️

 - 스토리보드에서 CollectionView Controller가 아닌 CollectionView를 선택해서 UIView에 넣는 방식으로 만들 시, CollectionView를 Controller에 delegate및 dataSource를 연결해줘야 사용할 수 있다. 

 - 이는 테이블 뷰에서도 마찬가지로, 내가 가끔 이걸 하지 않아서 헤매인 적이 많다. 꼭꼭 기억하자.

 

1️⃣ UICollectionViewDataSource 

여기에는 기본적으로 "몇개를 보여줄건지?", "셀은 어떻게 표현할건지?"에 대해서 프로토콜을 상속받아서 사용해야 한다.

🆚 TableViewDataSource와의 차이점

1. indexPath.row가 아닌 indexPath.item을 사용한다.

    // UICollectionViewDataSource
    // 몇개를 보여줄까요?
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return viewModel.numOfBountyInfoList
    }
    // 셀은 어떻게 표현할까요?
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GridCell", for: indexPath) as? GridCell else {
            return UICollectionViewCell()
        }
        
        let bountyinfo = viewModel.bountyInfo(at: indexPath.item)
        cell.update(info: bountyinfo)
        
        return cell
    
    }

2️⃣ UICollectionViewDelegate

여기에는 "셀이 클릭되었을 때 어떻게 할거야?" 라고 담고 있다.

메소드는 TableView와 비슷해서 didSelectItemAt 이라고 작성되어 있어서 이해하기가 쉽다. 

    // UICollectionViewDelegate
    // 셀이 클릭되었을 때 어떻게 할까요?
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("-->\(indexPath.item)")
        performSegue(withIdentifier: "showDetail", sender: indexPath.item)
    }

⭐️ 3️⃣ UICollectionViewDelegateFlowLayout

이 부분은 테이블뷰와 가장 크게 두드러진 차이점을 보여주는 지점이다.

UICollectionView의 경우 테이블 뷰 형식이 아니라 가로, 세로 등 여러개의 아이템을 배치할 수 있는데, 이에 따라서 레이아웃을 기기의 변화에 맞게 대응해줘야 한다. 

이 부분을 온전히 이해하기 위해서는 레이아웃에 대한 이해와 더불어 수학적 사고가 요구된다.

아래의 코드를 같이 보자.

    // UICollectionViewDelegateFlowLayout
    // 셀의 사이즈가 디바이스 마다 조금씩 변경되어야 할 필요가 있어서 - 가로폭이 다르기 때문에
    // cell size 계산할거다( 목표 : 다양한 디바이스에서 일관적인 디자인 보여주기 위해 )
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let itemSpacing : CGFloat = 10
        let textAreaHeight : CGFloat = 65
        
        let width : CGFloat = (collectionView.bounds.width - itemSpacing) / 2
        let height : CGFloat = width * 10/7 + textAreaHeight
        
        return CGSize(width: width, height: height)
    }

위의 코드를 기반으로 레이아웃을 잡으면 아래와 같은 이미지의 형태를 띄게 된다. 그럼 한줄한줄 코드에 대해서 리뷰해 보자.

위의 코드에 따라 레이아웃을 잡은 이미지 - iphone 12 mini

 

✅ 우선 위의 함수에서 return 값을 보면 CGSize(width, height)를 반환하고 있는데, 이를 테이블 셀의 각 아이템 컨테이너의 크기라고 생각하자.

 

bounds의 경우에는 자신만의 좌표를 가짐

frame의 슈퍼 뷰(상위 뷰)를 기준으로 좌표를 가짐

 

        let itemSpacing : CGFloat = 10 // 아이템 간의 간격
        let textAreaHeight : CGFloat = 65 // 글자가 위치할 간격
        
        let width : CGFloat = (collectionView.bounds.width - itemSpacing) / 2 // 컬렉션뷰의 자신만의 자신의 좌표를 넓이로 삼아, 아이템 간의 간격을 뺴준 후 나누기 2 

-> 여기 코드가 조금 봐야하는 지점인데, 나누기 2하는 이유는 기기의 전체 아이템을 2개 넣을 생각이라.

-> 이 부분에서 수학적인 사고가 요구되는데, Q. 만약에 3개를 넣고 싶으면 코드를 어떻게 수정해야 할까?

-> A. let width : CGFloat = (collectionView.bounds.width - 2 * itemSpacing) / 3

       여백 공간도 셀이 늘어나는 만큼 조정되야 한다.

아이템을 3개 넣은 후 실행


        let height : CGFloat = width * 10/7 + textAreaHeight // width가 정해졌으면, 7:10 비율로 높이를 정하고, 글자가 들어갈 공간만큼 높이를 더 확보한다.

❗️여기서 주의할 점 : :Label을 크기를 고려해서 적절하게 잡아야 한다.
        
        return CGSize(width: width, height: height) // 마지막으로 이 값을 리턴하면 끝!

 

여기까지가 UICollectionViewDelegateFlowLayout에 대한 기본적인 정보였다. 
    

 

(추가)

✅ 간혹 우리가 코드를 올바르게 입력했음에도 뷰 구성이 제대로 되지 않는 경우가 있다.

❗️이건 흔히하는 실수인데, 우리가 코드를 작성할때, 

Esitimate Size

Estimate Size 부분이 기본적으로 오토매틱으로 설정되어 있는데, 이 부분은 논으로 바꿔주어야 한다.

 

 - 참고

https://zeddios.tistory.com/203