이전에 UIScrollView에 대한 개념과 여러 프로터이와 메서드에 대해 알아보았다.
이번에는 직접 스크롤뷰를 사용해보겠다.
iOS 11부터 추가된 기능으로 이 두 가이드는 UIScrollView의 레이아웃을 더 명확하고 직관적으로 설정하기 위한 도구이다. 직접 구현해보기 전에 이 두 개념을 알아보겠다.
frmaLayoutGuide: UIScrollView의 frame에 관련된 제약 조건을 설정하는 데 사용된다. 즉 스크롤 뷰로 보고 있는 실제 크기를 정의한다.
contentLayoutGuide: 스크롤 뷰의 내부 contetn 크기와 관련된 제약 조건을 설정하는데 사용된다. 기존에는 contentSize 속성을 사용하여 스크롤 가능한 영역의 크기를 직접 설정했지만, contentLayoutGuide를 사용하여 오토 레이아웃 제약 조건을 활용하여 스크롤 가능한 영역 크기를 동적으로 설정 가능하다.
구조
컨텐츠 사이즈가 프레임 사이즈보다 큰 경우 스크롤이 가능하다. 만약 컨텐츠 사이즈가 더 작다면 스크롤의 의미가 없다.
세로스크롤
fram layout 너비와 content layout 너비가 동일하면 세로 스크롤만 가능하다.
가로스크롤
반대로 높이가 동일하면 가로스크롤이 가능하다.
스토리보드에서 Frame Layout과 Contet Layout를 활용해서 ScrollView를 구현해보기.
가로스크롤이 불가한 세로 스크롤을 구현.
헷갈리지 않게 배경색상을 미리 적용하였다.
스크롤뷰를 추가하고 드래그하여 어느정도 크기를 잡아준다. (크기를 미리 잡아주지 않은 상태에서 제약조건 등을 설정하면 원하는대로 설정이 안되는 경우가 있었음.)
안전영역을 제외한 전체 크기의 스크롤뷰로 구현하기 위해 제약조건을 모두 0으로 설정한다.
자동으로 Content Layout Guide와 Frame Layout Guide가 생성된 것을 볼 수 있다.
문제 없이 잘 따라 해도 경고문구가 뜨는것을 알 수 있다. 내용을 확인해보면 스크롤 가능한 컨텐츠 사이즈가 모호하다고 한다. 당연히 아직 컨텐츠관련 설정을 해주지 않았기 때문이다.
View를 ScrollView에 추가해준다. 그리고 ScrollView와 마찬가지로 크기를 미리 잡아주는것이 좋다.
새로 추가한 View의 제약조건을 Content Layout Guide로 설정한다. 이 때 제약 조건 수치를 잘 확인해서 의도한대로 설정 되었는지 확인하는것을 권장한다. (이부분 때문에 시간이 좀 지체되었음...)
세로 스크롤만 가능하게 하기 위해 View의 너비를 ScrollView의 프레임 사이즈와 동일하게 설정한다. 그리고 컨텐츠의 높이를 설정한다. (위에서 설명했던 내용대로 프레임 사이즈보다 컨텐츠 사이즈가 커야만 스크롤에 의미가 있음)
여기까지 진행했다면 좀전까지 떠있던 오류는 없어진다.
생성한 뷰 내부에 라벨을 넣어 보겠다.
기존 스토리보드에서 라벨을 추가하는 것과 동일한 방법으로 추가하면 된다. 다른점은 스크롤하여 기존화면에 보이지 않던 위치까지 배치 가능하다는 것이다.
라벨을 A~B 라벨을 추가하고, 가운데 정렬, A~C: top 제약조건은 300, D: bottom 제약조건 0
계획한대로 기본 상태에서 A와 B가 상단 300간격으로 배치되어 있는 것을 볼 수 있고, 스크롤을 내리면 C가 보이고 다음으로 D가 하단에 딱 붙어 있는것을 볼 수 있다.
코드를 통해Frame Layout과 Contet Layout를 활용해서 ScrollView를 구현해보기.
let scrollView: UIScrollView = UIScrollView()
let contentView: UIView = UIView()
let labelA: UILabel = UILabel()
let labelB: UILabel = UILabel()
let labelC: UILabel = UILabel()
let labelD: UILabel = UILabel()
func setupViews() {
scrollView.translatesAutoresizingMaskIntoConstraints = false
contentView.translatesAutoresizingMaskIntoConstraints = false
labelA.translatesAutoresizingMaskIntoConstraints = false
labelB.translatesAutoresizingMaskIntoConstraints = false
labelC.translatesAutoresizingMaskIntoConstraints = false
labelD.translatesAutoresizingMaskIntoConstraints = false
labelA.text = "A"
labelB.text = "B"
labelC.text = "C"
labelD.text = "D"
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(labelA)
contentView.addSubview(labelB)
contentView.addSubview(labelC)
contentView.addSubview(labelD)
}
func setupConstraints() {
NSLayoutConstraint.activate([
// scrollView contstaints
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// contentView constraints
contentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
contentView.heightAnchor.constraint(equalToConstant: 2000),
// labelA constraints
labelA.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 300),
labelA.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// labelB constraints
labelB.topAnchor.constraint(equalTo: labelA.topAnchor, constant: 300),
labelB.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// labelC constraints
labelC.topAnchor.constraint(equalTo: labelB.topAnchor, constant: 300),
labelC.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// labelD constraints
labelD.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -200),
labelD.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)
])
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupConstraints()
}
스토리보드로 구현한것과 동일하게 구현하였다. 일정 수치는 약간 차이가 있게 구현하였음(높이: 2000, D의 bottom제약: 200)
스토리보드를 통해 설정한 대로, 그대로 코드로 설정해주면 된다. 만약 제약조건을 코드로 설정하는방법을 모른다면 NSLayoutConstraint과련 정보를 보고 오는것이 좋다.
여러 프로퍼티와 메서드를 사용하지 않고 아주 간단한 형태로 구현해보면서 Frame Layout과 Content Layout을 배워보았다. 이 두 개념이 업데이트 되기 이전의 방식대로 구현하는것도 가능하지만, Frame Layout과 Content Layout을 활용하면 동적으로 사이즈 조절이가능하고, 유연하고, 손쉽게 스크롤뷰를 구현할 수 있다. 스크롤뷰는 대부분의 앱에서 많이 사용되기 때문에 자주 사용하면서 다양한 기능들에 대해서도 알아봐야 겠다.