컬렉션 뷰(Collection View)란 위의 그림에서 볼 수 있듯이 테이블처럼 일정한 가로 세로 개수를 가진 목록 오브젝트를 뜻합니다. 이 테이블 뷰를 생성하는 방법을 스위프트(Swift), 스토리보드(Storyboard) 기준으로 알아보겠습니다.
1) 컬렉션 뷰(Collection View) 오브젝트 추가
- 오른쪽 상단의
[+]
버튼을 클릭합니다. Collection View
오브젝트를 메인 뷰로 끌어와 추가합니다.
- 컬렉션 뷰를 양쪽으로 잡아당겨 화면에 꽉 채웁니다.
2) 컬렉션 뷰에 오토 레이아웃 설정
- 컬렉션 뷰를
마우스 오른쪽
버튼 또는control + 마우스 왼쪽
버튼을 누른 채로View
(위 그림의 파란색 영역)으로 드래그합니다.
shift
버튼을 누른 채 메뉴를 클릭해 위의 그림처럼 체크박스를 표시합니다. 이후 제약(constraints
)가 생기며 자동 레이아웃이 설정됩니다.
3) 뷰 컨트롤러에서 UICollectionViewDataSource, UICollectionViewViewDelegate 프로토콜 상속받기
먼저 컬렉션 뷰를 표시하기 위해 컨트롤러에서 반드시 제공해야 하는 정보가 2가지 있습니다.
-
컬렉션 뷰의 셀은 총 몇 개? (
UICollectionViewDataSource
) -
컬렉션 뷰를 어떻게 보여줄 것인가 ? (
UICollectionViewDelegate
)
이러한 정보를 제공하기 위해
UICollectionViewDataSource
, UICollectionViewDelegate
프로토콜을 구현해야 합니다. 뷰 컨트롤러 클래스의 최상단 부분에 다음 두 개 프로토콜을 추가합니다.
import UIKit class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } }
위 부분을 추가하면 아래와 같은 에러가 발생할 것입니다.[Fix]
버튼을 눌러 프로토콜 조각(protocol stub)을 추가합니다.
눌렀다면 다음 부분이 추가될 것입니다.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // ... code ... } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // ... code ... }
두 함수 모두 UICollectionViewDataSource
와 관련된 부분입니다.
첫 번째 함수는 콜렉션 뷰에 총 몇 개의 셀(cell
)을 표시할 것인지를 구현합니다. Int
값을 리턴하라고 나와 있으므로 return 10
등으로 작성합니다.
두 번째 함수는 해당 cell
에 무슨 셀을 표시할 지를 결정합니다. UICollectionViewCell
을 리턴하라고 합니다.
예제 코드를 작성하면 아래와 같습니다.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 10 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? UICollectionViewCell else { return UICollectionViewCell() } return cell }
dequeReusableCell
은 재활용 가능한 셀을 큐(queue
)의 형태로 추가/제거합니다. 만약 재사용 큐에 셀이 존재한다면 현재 재사용 큐에 있는 셀을dequeue
(꺼냄)해주고, 재사용 큐에 셀이 없으면 새로운 셀을 생성합니다.for:
는 셀의 위치를 지정하는 색인 경로입니다. 항상 데이터 원본 개체에서 제공하는 인덱스 경로를 지정해야 합니다.indexPath
를 적으면 됩니다.cell:
은 아까 추가한 테이블 뷰 셀의 구분자(identifier
)를 적는 곳입니다. 이 구분자를 통해 셀을 가져오고 그 셀을 재활용합니다. 구분자 추가 방법은 밑에서 설명하겠습니다.
4) 컬렉션 뷰 셀에 구분자(identifier) 추가
- 메인 스토리보드에서 컬렉션 뷰 셀을 선택한 뒤, 오른쪽에서
Show the Attributes Inspector
(위 그림의 빨간 박스 아이콘)를 선택한 뒤,Identifier
에 아까 프로토콜 조각에서 지정했던cell
구분자를 입력합니다. - 셀이 표시되는지 구분하기 위해
Background
에 색상을 추가합니다.
5) 컬렉션 뷰와 메인 뷰 컨트롤러에 Datasource, Delegate 연결
- 테이블 뷰에서
control + 마우스 왼쪽
또는마우스 오른쪽
버튼을 이용에 파란색 영역(View Controller
)으로 드래그합니다.
- 위와 같은 메뉴가 나타나는데
dataSource
,delegate
메뉴를 클릭하여 체크해줍니다.
- 컬렉션 뷰를 선택한 다음 오른쪽 옵션 창에서
Show the Connection Inspector
버튼을 클릭해 위와 같이 연결되었는지 확인합니다.
동작 확인
추가1) 커스텀 셀 작성
커스텀 셀의 작성 원리는 iOS 프로그래밍: 테이블 뷰 (Swift, 스토리보드) 2 – 커스텀 셀(custom cell) 추가 에서 나온 것과 매우 유사합니다. 이 부분의 내용을 알고 있다고 가정하고 설명하겠습니다.
먼저 Assets.xcassets
에 사진 파일들을 저장합니다.
컬렉션 뷰 내의 셀의 사이즈를 조정하고, Image
오브젝트와 Label
오브젝트를 추가합니다.
사이즈를 작성한 뒤, 새로운 클래스를 생성합니다.
class Cell: UICollectionViewCell { @IBOutlet weak var imgView: UIImageView! @IBOutlet weak var nameLabel: UILabel! func update(info: PictureInfo) { imgView.image = info.image nameLabel.text = info.name } }
커스텀 셀을 선택한 후, Identity Inspector
메뉴에서 Class
를 위에서 작성한 Cell
클래스로 지정합니다.
메인 스토리보드에서 Assistant
를 활성화한 뒤, 오브젝트와 @IBOutlet
을 연결합니다.
다음, ImageInfo
구조체를 생성합니다.
struct ImageInfo { let name: String var image: UIImage? { return UIImage(named: "\(name).jpg") } init (name: String) { self.name = name } }
이미지 데이터를 담는 ViewModel
을 생성합니다.
// view model class ImageViewModel { let imageInfoList: [ImageInfo] = [ ImageInfo(name: "Chrysanthemum"), ImageInfo(name: "Desert"), ImageInfo(name: "Hydrangeas"), ImageInfo(name: "Koala"), ImageInfo(name: "Lighthouse"), ImageInfo(name: "Penguins"), ImageInfo(name: "Tulip"), ] var countOfImageList: Int { return imageInfoList.count } func imageInfo(at index: Int) -> ImageInfo { return imageInfoList[index] } }
컬렉션 뷰 표시와 관련된 코드를 편집합니다.
let viewModel = ImageViewModel() // 뷰모델 변수를 추가 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return viewModel.countOfImageList // 뷰모델에서 카운트 가져옴 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else { return UICollectionViewCell() } let imageInfo = viewModel.imageInfo(at: indexPath.item) // indexPath.item을 기준으로 뷰모델에서 ImageInfo 가져옴 cell.update(info: imageInfo) // 해당 셀을 업데이트 return cell } // 셀이 선택되었을 때 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { print(indexPath.row) }
그리고 마지막으로 해당 셀을 클릭하면, 파일 이름이 나오는 경고창을 만들어 보도록 하겠습니다.
// 셀이 선택되었을 때 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let imageInfo = viewModel.imageInfo(at: indexPath.item) let dialog = UIAlertController(title: "\(imageInfo.name)", message: "", preferredStyle: .alert) let action = UIAlertAction(title: "확인", style: UIAlertAction.Style.default) dialog.addAction(action) self.present(dialog, animated: true, completion: nil) }
이것들을 종합한 ViewController.swift
의 내용은 다음과 같습니다.
import UIKit struct ImageInfo { let name: String var image: UIImage? { return UIImage(named: "\(name).jpg") } init (name: String) { self.name = name } } class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate { let viewModel = ImageViewModel() // 뷰모델 변수를 추가합니다. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return viewModel.countOfImageList // 뷰모델에서 카운트 가져옴 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else { return UICollectionViewCell() } let imageInfo = viewModel.imageInfo(at: indexPath.item) // indexPath.item을 기준으로 뷰모델에서 ImageInfo 가져옴 cell.update(info: imageInfo) // 해당 셀을 업데이트 return cell } // 셀이 선택되었을 때 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let imageInfo = viewModel.imageInfo(at: indexPath.item) let dialog = UIAlertController(title: "\(imageInfo.name)", message: "", preferredStyle: .alert) let action = UIAlertAction(title: "확인", style: UIAlertAction.Style.default) dialog.addAction(action) self.present(dialog, animated: true, completion: nil) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } } class Cell: UICollectionViewCell { @IBOutlet weak var imgView: UIImageView! @IBOutlet weak var nameLabel: UILabel! func update(info: ImageInfo) { imgView.image = info.image nameLabel.text = info.name } } // view model class ImageViewModel { let imageInfoList: [ImageInfo] = [ ImageInfo(name: "Chrysanthemum"), ImageInfo(name: "Desert"), ImageInfo(name: "Hydrangeas"), ImageInfo(name: "Jellyfish"), ImageInfo(name: "Koala"), ImageInfo(name: "Lighthouse"), ImageInfo(name: "Penguins"), ImageInfo(name: "Tulips"), ] var countOfImageList: Int { return imageInfoList.count } func imageInfo(at index: Int) -> ImageInfo { return imageInfoList[index] } }
추가2) 디바이스 크기에 따른 셀 사이즈 조정 예제
크기를 임의로 조정하려면 뷰 컨트롤러 클래스에 UICollectionViewDelegateFlowLayout
를 추가하고 스토리보드에서 컬렉션 뷰의 Estimate Size
를 None
으로 설정합니다.
case 1 – 가로 2칸, 기기 사이즈에 맞추기
// 사이즈 결정 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) }
case 2 – 가로 세로 칸수를 정하고 싶을 때 (출처)
let sectionInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = collectionView.frame.width let height = collectionView.frame.height let itemsPerRow: CGFloat = 2 let widthPadding = sectionInsets.left * (itemsPerRow + 1) let itemsPerColumn: CGFloat = 3 let heightPadding = sectionInsets.top * (itemsPerColumn + 1) let cellWidth = (width - widthPadding) / itemsPerRow let cellHeight = (height - heightPadding) / itemsPerColumn return CGSize(width: cellWidth, height: cellHeight) }
0개의 댓글