[ Swift ] CollectionView 기반 그리드 UI 코드 분석
![[ Swift ] CollectionView 기반 그리드 UI 코드 분석](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Fapi%2Fog-custom%3Ftitle%3D%25EB%25A7%25A4%25EC%259D%25BC%2B%25EC%259E%2591%25EC%258B%25AC%25EC%2582%25BC%25EC%259D%25BC%2B%25EC%258A%25A4%25EC%259C%2584%25ED%2594%2584%25ED%258A%25B8%26tag%3DTemplate%2B1%26description%3DCollectionView%2B%25EA%25B8%25B0%25EB%25B0%2598%2B%25EA%25B7%25B8%25EB%25A6%25AC%25EB%2593%259C%2BUI%2B%25EC%25BD%2594%25EB%2593%259C%2B%25EB%25B6%2584%25EC%2584%259D%26template%3D3%26backgroundImage%3Dhttps%253A%252F%252Fsource.inblog.dev%252Fog_image%252Fdefault.png%26bgStartColor%3D%2523ffffff%26bgEndColor%3D%2523ffffff%26textColor%3D%2523000000%26tagColor%3D%2523000000%26descriptionColor%3D%2523000000%26logoUrl%3Dhttps%253A%252F%252Fsource.inblog.dev%252Flogo%252F2025-12-13T10%253A14%253A09.250Z-090d0e96-ea75-4fe5-952f-659a3a0ea16b%26blogTitle%3DRN%2B%25EC%2582%25BD%25EC%25A7%2588%2B%25EC%259D%25BC%25EC%25A7%2580&w=3840&q=75)
오늘은 UIKit의 UICollectionView를 이용해 애플 프레임워크 목록을 3열 그리드 형태로 보여주는 화면을 만들었다.
스토리보드에서 기본 UI를 구성한 뒤, 코드에서 데이터 바인딩과 레이아웃을 직접 제어한 방식이다.
총 코드
//
// FrameworkListViewController.swift
//
import UIKit
class FrameworkListViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
let list:[AppleFramework] = AppleFramework.list
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
navigationController?.navigationBar.topItem?.title = "🌟 Apple Framework"
collectionView.contentInset = UIEdgeInsets(top: 20, left: 16, bottom: 0, right: 16)
}
}
extension FrameworkListViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FrameworkCell", for: indexPath) as? FrameworkCell else {
return UICollectionViewCell()
}
let framework = list[indexPath.item]
cell.configure(framework)
return cell
}
}
extension FrameworkListViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let interItemSpacing: CGFloat = 10
let width = (collectionView.bounds.width - 32 - interItemSpacing * 2) / 3
let height = width * 1.5
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int ) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let framework = list[indexPath.item]
print("\(framework.name)")
}
}
//
// FrameworkCell.swift
//
import UIKit
class FrameworkCell: UICollectionViewCell {
@IBOutlet weak var thumbnailImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.numberOfLines = 1
nameLabel.adjustsFontSizeToFitWidth = true
}
func configure(_ framework: AppleFramework){
thumbnailImageView.image = UIImage(named: framework.imageName)
nameLabel.text = framework.name
}
}
1. Storyboard에서 기본 UI 구성하기
가장 먼저, 화면의 뼈대를 잡기 위해 스토리보드를 사용했다.
Navigation Controller를 추가하고, CollectionView가 포함된 ViewController를 연결했다.
또한 셀 내부에 들어갈 이미지 뷰와 라벨을 배치하여 기본적인 셀 UI를 만들었다.
XCode 62.1.1기준 좌측 하단 + 버튼으로 기본적인 엘리먼트들을 불러올 수 있다.
스토리보드는 SwiftUI처럼 실시간 미리보기가 정확한 편도 아니고, 코드로 치는게 편한 나에게는 정말 불편했지만,
UI 구조를 한 번에 파악할 수 있고 셀을 손으로 그려볼 수 있다는 점에서 시작 단계로 적합하다고 생각한다
2. UI에 실제 데이터가 흘러들어가도록 코드 연결하기
UI만 만들어서는 아무것도 표시되지 않기 때문에, 다음 단계는 코드에서 데이터와 UI를 연결하는 작업이었다.
그 핵심은 CollectionView의 두 가지 필수 속성을 연결하는 것이다.
collectionView.dataSource = self
collectionView.delegate = self
Story보드에서 UI를 만들었더라도 데이터 공급자(DataSource) 와 이벤트·레이아웃 담당자(Delegate) 는
ViewController 코드에서 직접 지정해줘야 한다.
이 작업은 viewDidLoad에서 수행했다.
3. viewDidLoad에서 한 번에 초기 세팅 적용하기
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
navigationController?.navigationBar.topItem?.title = "🌟 Apple Framework"
collectionView.contentInset = UIEdgeInsets(top: 20, left: 16, bottom: 0, right: 16)
}
viewDidLoad는 화면이 메모리에 올라온 직후 단 한 번 실행되기 때문에,
초기 설정을 모아두기에 적합하다.
이 시점에서 해준 작업들
CollectionView의 동작 준비 (dataSource / delegate 연결)
Large Title로 표시될 Navigation Bar 설정
셀이 화면 가장자리에 붙지 않도록 contentInset 적용
이렇게 UI가 ‘보여질 준비’를 해둔 다음에야 실제 데이터가 화면에 나타난다.
4. CollectionView에 몇 개의 셀이 필요한지 알려주기
이제 DataSource가 할 일이다.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
간단하지만 중요하다.
컬렉션뷰는 직접 강제로 셀을 만드는 게 아니라,
“몇 개를 만들어야 하는지”만 알려주면 스스로 셀을 재사용하면서 화면을 구성한다.
오늘은 AppleFramework.list의 개수만큼 셀을 만들었다.
5. 각 셀에 어떤 데이터가 들어갈지 구성하기
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FrameworkCell", for: indexPath) as? FrameworkCell else {
return UICollectionViewCell()
}
let framework = list[indexPath.item]
cell.configure(framework)
return cell
여기에서 재사용 셀(dequeueReusableCell) 의 의미를 이해하게 됐다.
UIKit은 스크롤될 때마다 새 셀을 만드는 방식이 아니라,
보이지 않는 셀을 다시 가져와 데이터만 바꿔 끼우는 재사용 구조를 취한다.
그래서 매번 configure를 호출하여 UI를 최신 데이터로 업데이트하는 방식이 자연스럽다.
6. 3열 그리드를 만들기 위한 셀 크기 계산
let interItemSpacing: CGFloat = 10
let width = (collectionView.bounds.width - 32 - interItemSpacing * 2) / 3
let height = width * 1.5
이 계산은 스토리보드에서는 불가능하고 코드로만 가능한 영역이다.
계산 구조
좌우 inset: 16 + 16 = 32
가끔 이렇게 변수처리하기 귀찮아서 숫자를 직접 넣을때가 잇는데 확실히 좋지 않음 나중에 볼때 이거 왜 이렇게 계산했지? 하고 의문이 들때가 분명히 생김
셀 사이 간격: 10이 두 번
나머지 가용 공간을 3으로 나누면 정확한 셀 너비
이렇게 직접 계산해주면 어떤 기기에서도 항상 3개씩 배치되는 그리드가 만들어진다.
UIKit은 레이아웃 자동화보다 "명시적 제어"가 강하기 때문에 이런 계산이 필수다.
7. 셀을 눌렀을 때 동작 확인
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let framework = list[indexPath.item]
print("\(framework.name)")
}
클릭이 가능한 상태까지만을 구성하기위해 print만 찍어두었다
8. 보충: 오늘 코드에서 등장한 self의 의미
오늘 초기 설정에서 가장 많이 본 코드가 이것이다.
collectionView.dataSource = self
collectionView.delegate = self
여기서 self는 현재 ViewController 인스턴스를 의미한다.
DataSource 역할을 내가 하겠다 → self
Delegate 역할도 내가 하겠다 → self
UIKit에서는 이런 delegate 연결 패턴이 매우 흔하기 때문에
self는 “이 역할의 주체가 누구인가?”를 표현하는 용도로 자주 등장한다.
느낀 점
스토리보드 빠르게 UI를 잡을 수 있다고들 하지만, 지금의 나는 오히려 비효율적으로 느껴졌다.
RN처럼 코드만으로 UI를 구성해온 입장에서, 스토리보드는 작은 설정 하나에도 시간이 많이 들었다.
그렇다고 완전히 무시하기도 애매한 게, 실제로 회사나 기존 프로젝트에서는 스토리보드 기반 구조를 유지·보수해야 할 가능성이 충분히 있다.
그래서 지금은 익숙하지 않아도, 언젠가를 대비해서 최소한 읽고 이해할 정도의 능력은 갖추는 게 좋겠다는 생각을 했다.
그리고 SwiftUI로 넘어가는 흐름이 있다고 들었기 때문에, 익숙해지기만 하면 나도 빠르게 SwiftUI로 전환하고 싶다.