Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UIScrollView in UIScrollView (nested scrollView 스크롤 제어 방법, scrollViewDidScroll, contentOffset) 본문

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
        }
    }
}

* 전체 코드: https://github.com/JK0369/ExScrollInScroll

Comments