iOS 개발자가 많이 하는 실수 - UIScrollView + Auto Layout에서 contentSize / 제약을 잘못 설정해 스크롤이 안 되는 실수
Dev Study/iOS 2025. 12. 4. 20:34반응형
iOS 개발자가 많이 하는 실수 - UIScrollView + Auto Layout에서 contentSize / 제약을 잘못 설정해 스크롤이 안 되는 실수
UIScrollView와 Auto Layout은 조합이 까다로운 편이라
초보자뿐 아니라 경력자도 자주 다음과 같은 문제를 겪습니다.
- 스크롤이 전혀 안 됨
- 스크롤이 되긴 하는데 컨텐츠가 잘림
- 하단 여백이 이상하게 남음
- 특정 기기/회전에서만 스크롤 범위가 꼬임
1. 문제 패턴: Auto Layout + 수동 contentSize 혼용
대표적인 안티 패턴:
scrollView.addSubview(contentView)
// Auto Layout 제약 설정
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
])
// ❌ 수동으로 contentSize를 설정
scrollView.contentSize = CGSize(width: view.bounds.width, height: 2000)
문제:
- Auto Layout이 이미 스크롤 콘텐츠의 크기를 계산하려고 하는데
- 개발자가 강제로 contentSize를 덮어씀
- 기기/회전/컨텐츠 크기가 변할 때 계속 불일치 발생
Auto Layout을 쓸 거면 contentSize를 직접 만지지 않는 것이 원칙입니다.
2. ScrollView + Auto Layout의 기본 개념
iOS 11 이후, UIScrollView에는 두 가지 중요한 레이아웃 가이드가 생겼습니다.
- contentLayoutGuide
- 스크롤 가능한 콘텐츠 영역의 크기/위치
- 내부 콘텐츠(view들)에 이 가이드를 기준으로 제약을 건다.
- frameLayoutGuide
- ScrollView 자체의 프레임(보이는 영역)
- 상위 뷰와의 제약에 사용한다.
이 두 가지를 올바르게 사용하면
contentSize를 코드로 건드릴 필요 없이
Auto Layout만으로 자연스럽게 스크롤 영역이 계산됩니다.
3. 올바른 ScrollView + Auto Layout 구성 패턴
3-1. 기본 구조
일반적인 패턴은:
- ScrollView 안에 contentView 하나를 추가
- contentView 안에 나머지 UI 요소들을 배치
코드 예시:
let scrollView = UIScrollView()
let contentView = UIView()
view.addSubview(scrollView)
scrollView.addSubview(contentView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// 1) ScrollView를 상위 뷰에 고정 (frameLayoutGuide 사용)
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// 2) contentView를 contentLayoutGuide에 고정
contentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
// 3) contentView의 width를 scrollView의 frameLayoutGuide에 맞춤
contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor)
])
포인트:
- contentView가 가로 방향으로는 scrollView와 같은 폭을 가지게 하고
- 세로 방향으로는 내부 콘텐츠의 높이에 따라 늘어나도록 구성
- Auto Layout이 contentLayoutGuide를 통해 contentSize를 자동 계산
3-2. 내부 콘텐츠 배치
이제 contentView 안에 StackView 등을 활용해 콘텐츠를 배치:
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 16
contentView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
])
이렇게 하면:
- stackView의 내용에 따라 contentView의 height가 결정
- 그 높이가 다시 contentLayoutGuide → contentSize로 반영
- 결과적으로 Auto Layout에 의해 스크롤 영역이 자연스럽게 정해짐
4. 흔한 실수 패턴과 해결책
4-1. contentView의 width 제약 누락
다음 코드에서:
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
// ❌ width 제약이 없음
])
이 경우:
- Auto Layout은 contentView의 가로 크기를 확정하지 못함
- “ambiguous width” 혹은 제대로 된 스크롤 동작을 하지 않을 수 있음
해결:
contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor)
를 반드시 추가.
4-2. ScrollView 자체를 상위 뷰에 Auto Layout으로 안 묶음
view.addSubview(scrollView)
scrollView.frame = view.bounds // ❌ frame 직접 설정 + auto layout 혼용
Auto Layout 환경에서는 frame을 직접 설정하기보다는
제약으로 위치/크기를 정하는 것이 권장됩니다.
4-3. contentSize를 임의로 덮어씀
Auto Layout을 쓰고 있음에도:
scrollView.contentSize = CGSize(width: 0, height: 2000) // ❌
이렇게 임의의 값을 설정하면:
- 기기 회전/Insets 변경/컨텐츠 변동 시
Auto Layout이 계산한 크기와 충돌 - 예상치 못한 스크롤 범위 문제 발생
가능하면 Auto Layout만으로 해결하고,
정말 특수 케이스에서만 contentSize를 수동으로 건드리길 권장합니다.
4-4. 수평/수직 ScrollView 혼동
가로 스크롤이 필요한 경우:
contentView.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor)
처럼 축을 반대로 잡아야 합니다.
- 세로 스크롤: contentView.width == scrollView.frame.width
- 가로 스크롤: contentView.height == scrollView.frame.height
5. Storyboard / Interface Builder에서의 구성 팁
IB에서도 개념은 동일합니다.
- ScrollView 추가
- 그 안에 Content View 하나 추가
- ScrollView ↔ 상위 뷰: 4방향 constraint
- Content View ↔ ScrollView:
- contentLayoutGuide에 top/leading/trailing/bottom
- frameLayoutGuide에 width(또는 height) 맞추기
Xcode 11+에서는 IB에서도
Content Layout Guide, Frame Layout Guide를 명시적으로 선택해서
제약을 설정할 수 있습니다.
6. 실무에서 자주 겪는 증상 정리
- 스크롤이 전혀 안 됨
- contentLayoutGuide에 bottom 제약 누락
- 내부 콘텐츠의 bottom이 contentView.bottom까지 연결되지 않음
- contentView 크기가 ScrollView와 같아서 추가 스크롤 영역이 없음
- 스크롤은 되는데 여백이 이상하게 큼
- 잘못된 bottom constant
- contentSize를 임의로 크게 설정함
- 일부 기기에서만 스크롤이 안 됨
- Safe Area, notch, bottom inset 등을 고려하지 않은 제약
- 기기별 레이아웃 차이로 content height가 달라짐
7. 실무용 체크리스트
ScrollView + Auto Layout 쓰기 전에 다음을 확인합니다.
- ScrollView는 상위 뷰에 Auto Layout 제약으로 고정했는가?
- ScrollView 안에는 Content View 하나로 감싸고 있는가?
- Content View는:
- contentLayoutGuide에 상/하/좌/우 제약이 연결되어 있는가?
- frameLayoutGuide에 width(또는 height)가 맞춰져 있는가?
- 내부 콘텐츠(레이블, 버튼, 스택뷰)는 Content View의 top~bottom에
끊김 없이 연결되어 있는가? - Auto Layout을 쓰고 있는데 contentSize를 수동으로 건드리고 있지 않은가?
8. 요약
- ScrollView + Auto Layout 조합에서 스크롤이 안 되거나 깨지는 이유의 대부분은
contentLayoutGuide, frameLayoutGuide, contentView 제약이 잘못되었거나,
contentSize를 수동으로 건드려서 생긴다. - 올바른 패턴:
- ScrollView 안에 Content View 하나
- Content View ↔ contentLayoutGuide (4방향 제약)
- Content View ↔ frameLayoutGuide (width 또는 height 제약)
- 내부 콘텐츠는 Content View 안에서 Auto Layout으로 자연스럽게 배치
- 원칙: > Auto Layout을 사용한다면, ScrollView의 contentSize를 직접 설정하지 않는 것이 기본이다.
반응형

