← 홈으로
UIKit · Scrolling · Q&A

ScrollView · TableView · CollectionView — 내 질문 Q&A

2026-06-21 스크롤뷰 학습 중 주인님이 던진 질문 + 답변을 그대로 모았다. 정리된 지식은 원리 & 심화 문서 참고.

Q&A 152026-06-21

Q&A

UIScrollView는 재사용을 안 하나? "데이터소스 기반 lazy 생성"은 cellForItem?

ScrollView는 재사용·dataSource 없음. 직접 addSubview로 넣은 뷰가 전부 메모리 상주. 재사용·dataSource·lazy 생성은 Table/Collection이 추가한 기능. "lazy 생성"=cellForItemAt이 보일 때만 dequeue+구성(보일 셀만큼만 그때그때 호출). ✅ cellForItem이 그 지점.

TableView는 레이아웃을 누가 관리해? (CollectionView는 객체에 위임이라며)

UITableView 자기 내부. 세로 1열 누적 배치가 내장(별도 객체 없음). "높이 정보"는 delegate/제약이 주고, "실제 배치(frame 계산)"는 테이블뷰 내부. CollectionView는 그 배치 로직을 UICollectionViewLayout 객체로 빼서 교체 가능하게 일반화.

bounds.origin (0,100)은 스크롤 내린 거야? 콘텐츠 고정·bounds만 이동?

✅ 아래로 100 스크롤한 상태(iOS y는 아래로 증가). 콘텐츠는 제자리 고정, scroll view가 보는 창(bounds)만 이동. 움직이는 건 frame이 아니라 bounds. contentOffset == bounds.origin. 비유: 콘텐츠=긴 두루마리(고정), bounds=미끄러지는 창문.

왼쪽20·위30에 배치하면 frame=(20,30), bounds=(0,0)?

✅ frame.origin=(20,30)(부모 기준 위치), bounds.origin=(0,0)(스크롤 전). frame과 bounds는 size가 같고 origin만 다름. contentSize는 별개(전체 콘텐츠, bounds보다 커야 스크롤).

originsize
frame(20,30)(300,500)
bounds(0,0)(300,500) 동일
contentSize—(300,2000)

contentInset은 스크롤뷰와 콘텐츠 사이 여백? 최하단이 왜 530?

✅ 콘텐츠 둘레 여백 → 스크롤 범위를 늘림. (bounds500, content1000, top50·bottom30 기준)

최상단 offset.y = -50 // top inset만큼 더 위로 최하단 offset.y = 1000 - 500 + 30 = 530 // 끝에 여백30 덧붙여 더 내림

bottom inset = 콘텐츠 끝에 빈 종이 30 덧붙여 30 더 스크롤 가능. 키보드(300)가 가리면 bottom=300으로 마지막 콘텐츠를 위로 올림. iOS 11+ adjustedContentInset=contentInset+safeArea.

contentLayoutGuide/frameLayoutGuide 동작 원리 (SnapKit)? 마지막 bottom 핀이 왜 필요?

contentLayoutGuide=contentSize 결정(콘텐츠 4변), frameLayoutGuide=보이는 폭(width 고정→가로 스크롤 방지).

contentView.snp.makeConstraints { $0.edges.equalTo(scrollView.contentLayoutGuide) // contentSize $0.width.equalTo(scrollView.frameLayoutGuide) // 가로 스크롤 방지 } lastView.snp.bottom.equalToSuperview() // ★ 높이 확정

footer.bottom == contentView.bottom은 참조가 아니라 방정식 — footer 위치가 거꾸로 contentView 높이를 정의(bottom-up). 빠뜨리면 높이 ambiguous → 스크롤 안 됨.

automaticDimension + estimatedRowHeight 자세히. 왜 contentSize를 알아야? estimated 없으면? systemLayoutSizeFitting? contentOffset 보정?

  • 왜 contentSize? 스크롤바 길이·범위·셀 위치에 필요(추정이라도)
  • automaticDimension = "고정값 말고 셀 제약으로 높이 계산". frame/고정값으로 주면 Auto Layout 계산 스킵(빠름·수동)
  • estimated 없으면(0) 로드 시 전부 계산(1만 개=1만 번)→멈춤. 주면 추정 후 보이는 셀만 lazy
  • systemLayoutSizeFitting = 제약을 풀어 "최적 높이" 계산(폭 고정·높이 최소). top~bottom 제약 완결 필요
  • contentOffset 보정 = 위쪽 셀이 추정80→실제120 되면 콘텐츠 좌표가 커짐 → 같은 셀 보려면 offset도 옮겨야. 안 맞으면 점프(위로 스크롤 시 심함)

UITableViewDataSourcePrefetching은 클래스야?

❌ 프로토콜. prefetchDataSource로 연결. prefetchRowsAt(필수)/cancelPrefetchingForRowsAt(선택). cellForRow보다 일찍 데이터만 미리 준비(셀 생성 아님) → 빈 셀 깜빡임 감소. CollectionView도 동일 프로토콜.

batch update vs Diffable Data Source 쉽게? performBatchUpdates? 지금은 Diffable 쓰나?

전통: "어디 바뀜(인덱스)" 직접 알림 → 데이터 개수 안 맞으면 크래시. performBatchUpdates=여러 변경을 한 묶음·한 애니로 동시 적용.

Diffable(iOS 13+): "최종 상태(스냅샷)"를 통째로 주면 시스템이 차이(diff) 자동 계산 → 인덱스 계산·크래시 없음. Item은 Hashable 필요. 비유: 사서에게 "최종 책장 모습"을 주면 알아서 정리.

신규는 Diffable 권장(표준화 추세), 레거시엔 전통(reloadData/performBatchUpdates) 혼재 → 5년차면 둘 다.

moveRow(at: c, to: d)에서 c, d는? 예시?

둘 다 IndexPath(section+row). c=원래 위치(from), d=목표 위치(to).

// ["사과","바나나","체리","딸기"] — 바나나(1)를 3으로 let moved = items.remove(at: 1); items.insert(moved, at: 3) // 데이터 먼저 tableView.moveRow(at: IndexPath(row:1,section:0), to: IndexPath(row:3,section:0))

드래그 재정렬이면 tableView(_:moveRowAt:to:) delegate에서 배열만 맞춤(테이블은 자동 이동). 배열도 같이 안 바꾸면 크래시.

UICollectionViewLayout의 prepare/contentSize/layoutAttributesForElements/Item? zIndex? attributes 정체? frame은 CGRect?

  • prepare() 모든 요소 위치 미리 계산·캐싱 / collectionViewContentSize 전체 크기
  • layoutAttributesForElements(in rect) 그 영역 요소들 attributes(재사용 판단 주력) / layoutAttributesForItem(at:) 특정 셀(애니·scrollToItem 등 필요 시)
  • UICollectionViewLayoutAttributes = 한 요소의 배치 속성 묶음(frame·alpha·zIndex·transform). frame은 CGRect(위치+크기)
  • zIndex = 겹칠 때 앞뒤 순서(큰 값이 위). 예: sticky header가 셀 위에, 카드 덱 겹침

frame 직접 계산하면 텍스트 길어질 때 frame을 직접 늘려야 해? boundingRect·sizeThatFits가 뭐야?

✅ frame 직접 방식은 텍스트 길면 높이를 내가 계산해 frame을 직접 키워야(자동 X). 그 크기를 미리 재는 도구:

  • boundingRect(NSString): "이 글자를 폭 300에 넣으면 높이 얼마?" 계산
  • sizeThatFits(UIView): 라벨에게 "이 폭이면 너 얼마 필요해?" 물어봄

둘 다 표시 전에 크기를 미리 재는 줄자. 폭 고정 → 필요 높이 반환.

Auto Layout 하면 이 크기 계산을 알아서 해주나? intrinsic은 인셋도 포함? 텍스트 위아래 간격은?

✅ Auto Layout(self-sizing)이 라벨의 intrinsic content size로 자동 계산 → boundingRect 수동 불필요(numberOfLines=0 + 제약 완결 + automaticDimension 조건).

intrinsic에 포함?
텍스트 위아래 간격(폰트 line height 여유)✅ 포함(라벨 자체 높이)
라벨-셀 inset(top==contentView.top+8)❌ 별개(제약)

그 위아래 간격은 폰트 line height(ascender/descender 여유) — 16pt 폰트면 글자 ~12, line height ~20. 줄이려면 attributed string의 lineHeightMultiple 조정. 최종 셀 높이 = intrinsic(텍스트) + 제약(inset).

관련 문서