부드럽게 잘 동작하는 모습
Sticky header 구현
extension HomeViewController: UIScrollViewDelegate {
//뷰가 정지해야할 임계점
var threshold: CGFloat = 386
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//스크롤뷰의 현재 위치
let offset = scrollView.contentOffset.y
//MARK: Sticky header 스크롤 뷰 안의 컬렉션 뷰 구현했던 경험 살려서 수정할 것.
if offset > self.threshold {
scrollView.contentOffset = CGPoint(x: 0, y: self.threshold)
//상위 뷰 스크롤 정지.
scrollView.isScrollEnabled = false
}
}
}
Swift
복사
이렇게만 해두면, 사용성이 매우 안좋다.
•
상위 스크롤에서 하위 스크롤을 내렸을 때, 반응이 느리다
•
하위 스크롤도중 상위 스크롤을 올리고자할 때, 상위 스크롤을 터치해서 스크롤해야만 반응한다.
위 두가지 문제를 해결하기 위해 필요한 알고리즘을 고안해 보았다.
상위 스크롤 > 하위 스크롤 넘어갈 때
1.
상위 스크롤이 임계점에 닿으면 상위 스크롤 비활성화
2.
하위 스크롤 재활성화
하위 스크롤 > 상위 스크롤 넘어갈 때
3.
하위 스크롤이 맨 위에 닿으면 하위 스크롤 비활성화
4.
상위 스크롤 재활성화
위 동작이 하나의 뷰컨트롤러 내에서 일어나는 경우에는 조건문 만으로 어렵지 않게 구현이 가능하다.
하지만 내 경우엔 하위 스크롤이 embed된 다른 뷰컨트롤러에 존재했기 때문에
뷰컨틀러간의 신호 전달이 필요했다.
따라서 delegate 패턴을 사용했다.
다만 2번 동작의 경우 재활성화를 위해 delegate 를 서로 엮는 것 보다는
타이머를 설정해 자동으로 재활성화 하는 것이
스파게티 코드를 피하는 방법이라 생각했다.
상위 스크롤 뷰 컨트롤러
protocol NestedScrollDelegate {
func scrollDidEndTop()
}
extension HomeViewController: NestedScrollDelegate {
func scrollDidEndTop() {
scrollView.isScrollEnabled = true
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toItem" {
let viewController: HomeItemViewController = segue.destination as! HomeItemViewController
viewController.scrollDelegate = self
}
}
}
Swift
복사
하위 스크롤 뷰 컨트롤러
//MARK: - 중첩 스크롤 해결
extension RecommendViewController: UIScrollViewDelegate {
var scrollDelegate: NestedScrollDelegate?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < -5 {
//하위 스크롤이 맨위에 닿는 다면 delegate 메서드 실행
scrollDelegate?.scrollDidEndTop()
//스크롤이 맨위까지 닿는다면 하위 스크롤 잠금
collectionView.isScrollEnabled = false
//0.4 초 뒤에 바로 해제
Timer.scheduledTimer(withTimeInterval: 0.4, repeats: false) { timer in
self.collectionView.isScrollEnabled = true
}
}
}
}
Swift
복사