UI 컴포넌트 (swift)
[iOS - swift] UIScrollView in UIScrollView (nested scrollView 스크롤 제어 방법, scrollViewDidScroll, contentOffset)
jake-kim
2023. 1. 10. 22:32

nested scrollView
- 안쪽과 바깥쪽 스크롤 뷰 모두 isScrollEnabled가 on인 경우, 아래처럼 안쪽 스크롤이 활성화되어 있는 상태

- 단, 안쪽 스크롤이 상단까지 스크롤된 경우, 더 이상 스크롤 할 내용이 없다면 아래처럼 자동으로 바깥쪽 스크롤이 되는 형태

- 스크롤 뷰 안에 또다른 스크롤 뷰가 있을 경우, 바깥쪽 스크롤이 어느정도 스크롤 되었을 때 안쪽 스크롤 뷰가 스크롤 되도록 하는 방법?
- 1) 안쪽 스크롤의 isScrollEnabled를 false로 초기화
- 2) scrollViewDidScroll()델리게이스 메소드에서 contentOffset을 보고 스크롤을 상단 뷰의 길이만큼 한 경우 스크롤 활성화
구현 방법
* 예제에는 코드로 오토레이아웃 정의에 편리한 SnapKit 사용
- ViewController 준비
- 바깥쪽 scrollView, 안쪽 tableView
- tableView위에 emptyView가 존재
- tableView의 isScorllEnabled = false로 설정 (안쪽 스크롤뷰가 스크롤 안되고 바깥쪽 스크롤이 되도록 설정)
- bounces가 true이면 스크롤을 막아도 스크롤이 바운스되며 순간적으로 더 스크롤을 할 수 있으므로 비활성화 (bounces = false)
class ViewController: UIViewController {
private let scrollView: UIScrollView = {
let view = UIScrollView()
view.bounces = false
view.backgroundColor = .lightGray.withAlphaComponent(0.2)
return view
}()
private let emptyView = UIView()
private let stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
return view
}()
private let tableView: UITableView = {
let view = UITableView()
view.bounces = false
view.backgroundColor = .lightGray
// 1.
view.isScrollEnabled = false
return view
}()
}
- 테이블뷰에 사용할 데이터소스 선언
private let items = (0...500).map(String.init)
- 레이아웃과 테이블뷰 데이터소스 구현
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
scrollView.addSubview(stackView)
stackView.addArrangedSubview(emptyView)
stackView.addArrangedSubview(tableView)
scrollView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
stackView.snp.makeConstraints {
$0.edges.width.equalToSuperview()
}
emptyView.snp.makeConstraints {
$0.height.equalTo(500)
}
tableView.snp.makeConstraints {
$0.height.equalTo(1200)
}
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
scrollView.delegate = self
tableView.delegate = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
- scrollViewDidScroll에서 상단 뷰 (emptyView)의 길이에 가깝게 스크롤 된 경우, 안쪽 스크롤 뷰(tableView)의 스크롤을 활성화 시키도록 구현
extension ViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print(scrollView.contentOffset.y)
let topMargin = 56.0 // 상단 56.0 마진
let topSpacing = emptyView.bounds.height - topMargin
if scrollView == self.scrollView {
// 상단 뷰의 height만큼 스크롤 한 경우, tableView의 스크롤 on
let shouldOnScroll = topSpacing - scrollView.contentOffset.y < 0
tableView.isScrollEnabled = shouldOnScroll
}
}
}
- scrollView의 contentSize, contentOffset을 사용하여 현재 스크롤이 바닥에 닿았는지 확인이 가능하므로 이것을 사용
extension ViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.scrollView {
let remainingScrollHeight = scrollView.contentSize.height - scrollView.frame.size.height
let isBottomReached = scrollView.contentOffset.y >= remainingScrollHeight
tableView.isScrollEnabled = isBottomReached
}
}
}