문제
UIStackView 내부에 UIScrollView를 넣고, 그 UIScrollView 내부에 또 다른 UIStackView를 배치하여 화면을 구성했습니다. 이러한 구조에서 UIScrollView 내 콘텐츠가 스크롤되지 못하는 문제가 발생했습니다. 이로 인해 사용자는 화면을 스크롤하여 추가 콘텐츠를 볼 수 없는 불편함이 있었습니다.
문제 발생 이유
UIStackView 내부의 UIScrollView가 스크롤되지 않았던 이유를 이해하기 위해서는 먼저 UIStackView와 UIScrollView에 대한 기본적인 이해가 필요합니다.
UIStackView
UIStackView는 Auto Layout 기능을 사용해서 동적으로 UI를 구현할 수 있는 Streamlined Interface입니다.
StackView는 arrangedSubviews에 포함되어 있는 모든 뷰의 레이아웃을 관리하고, 하위 뷰들을 axis에 따라서 순서대로 배치합니다.
StackView axis에 따라서 배열된 View의 레이아웃을 결정하는 것은 distribution 속성입니다.
해당 속성 중 fillEqually를 제외하고 모든 속성들이 StackView 내 arranged 하위 뷰들의 intrinsicContentSize 속성을 사용해서 크기를 계산하게 됩니다.
즉, axis가 vertical이라고 한다면, StackView는 각 ArrangedSubView의 intrinsicContentSize height 값을 사용해서 전체 높이를 계산하게 됩니다.
축이 vertical인 상황에서 vertical에 수직인 horizontal 크기는 어떻게 결정될까요?
축에 수직으로 배열된 View의 레이아웃을 결정하는 것은 alignment 속성입니다.
해당 속성 중 fill을 제외하고 모든 속성들이 StackView 내 arranged 하위 뷰들의 intrinsicContentSize 속성을 사용해서 크기를 계산하게 됩니다.
즉, horizontal의 최대 크기는 StackView의 ArrangedSubView의 intrinsicContentSize width 값을 사용해서 최대 너비를 계산하게 됩니다.
위의 내용을 통해서 StackView가 Auto Layout 기능을 사용해 동적으로 UI를 구현하기 위해서는 StackView 내에 있는 SubView의 intrinsicContentSize가 잡혀 있어야 한다는 걸 알 수 있습니다.
UIScrollView
UIScrollView는 ScrollView 내에 있는 콘텐츠의 scroll, zoom, panning을 관리하는 뷰입니다.
ScrollView는 언제 스크롤을 멈춰야 하는지 알기 위해서 콘텐츠 뷰의 크기를 알아야 합니다.
내부에 있는 콘텐츠 뷰의 크기가 모호하다면 어디까지 스크롤을 해야할 지 몰라서 스크롤을 하지 못할겁니다.
문제 해결
위의 내용을 통해서 UIScrollView 내 콘텐츠가 스크롤되지 못하는 문제는 UIScrollView가 가지고 있는 StackView의 크기가 모호했기 때문에 UIScrollView가 어디까지 스크롤을 해야할 지 몰라서 스크롤이 안되었다고 유추해 볼 수 있습니다.
그럼, 크기가 모호하지 않을 시에는 스크롤이 잘 되는지 확인을 해보겠습니다.
상황에 맞춰서 4가지 UI Component를 만들었습니다.
/// 상위 UIStackView
private let superContainer: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
return stackView
}()
/// 상위 UIStackView SubView인 UILabel
private let titleLabel: UILabel = {
let label = UILabel()
label.text = "타이틀 라벨"
label.font = .systemFont(ofSize: 30)
return label
}()
/// 상위 UIStackView SubView인 UIScrollView
private let scrollView = UIScrollView()
/// UIScrollView에 포함되는 UIStackView
private let subContainer: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
return stackView
}()
Swift
복사
SnapKit 라이브러리를 사용해서 뷰의 Constraint를 잡아 봅시다.
/// ⓵
view.addSubview(superContainer)
superContainer.snp.makeConstraints {
$0.edges.equalTo(view.safeAreaLayoutGuide)
}
superContainer.addArrangedSubview(titleLabel)
superContainer.addArrangedSubview(scrollView)
/// ⓶
scrollView.addSubview(subContainer)
subContainer.snp.makeConstraints {
$0.edges.equalToSuperview()
}
/// ⓷
for _ in 0...20 {
let label = UILabel()
label.text = "subText sub sub\ntext!!!!"
label.font = .systemFont(ofSize: 18)
label.numberOfLines = 0
subContainer.addArrangedSubview(label)
}
Swift
복사
⓵ 상위 뷰는 SafeArea에 맞게끔 레이아웃을 잡고, 그 안에 titleLabel과 scrollView를 추가합니다.
addArrangedSubview는 추가한 SubView의 위치를 자동으로 잡아주기 때문에 SubView의 크기를 따로 잡아주지 않아도 됩니다.
⓶ scrollView는 subContainer를 가지고 있기 때문에 이를 추가해주고 subContainer가 scrollView에 딱 맞게끔 크기를 잡아 줍니다.
⓷ subContainer StackView 내에 UILabel를 추가해줍니다. UILabel은 안에 들어가는 text에 맞춰서 intrinsicContentSize를 잡기 때문에 UIStackView의 높이는 UILabel들의 높이를 더한 값으로 정해집니다.
해당 코드를 실행해보면 스크롤이 잘 되는 걸 확인할 수 있습니다.
이로써 UIScrollView 내부에 들어간 UIStackView의 크기가 모호하지 않으면 UIScrollView가 제대로 동작한다는 걸 알 수 있습니다.
UIScrollView의 동작이 제대로 되지 않을 때, 내부에 있는 Content의 크기가 모호하진 않은지 확인해보면 좋을 것 같습니다.