Collapsible Header 구현기

Date:     Updated:

Categories:

Tags:

구현한 기능

yogiyo-1

위와 같은 스크롤 기능을 구현 하기위한 방법을 고민해봤습니다.

위로, 또는 아래로 일정한 만큼 이상을 스크롤하면, 가장 위에있는 헤더 뷰가 애니메이션과 함께 자동으로 이동하는 것을 볼 수 있습니다.

위 기능을 구현하기 위해서는 다음 두 가지 스텝이 필요하다고 생각하고 개발에 임했습니다.

  1. 스크롤을 감지하는 기능
  2. 스크롤이 감지됐을때 애니메이션이 트리거 되는 기능

하나씩 구현한 방법을 소개하겠습니다.

스크롤을 감지하는 기능

스크롤이 얼만큼 됐는지 감지하는 기능이 필요한데, UIScrollViewDelegate 에서 제공하는 scrollViewDidScroll 이라는 메서드를 사용했습니다.

애플 공식문서를 보면

Screen Shot 2022-07-04 at 1 30 08 AM

Tells the delegate when the user scrolls the content view within the receiver

해당 기능을 구현하기위해 필요한 메서드라고 판단했습니다.

사용자가 스크롤을 얼마나 했는지 판단하기 위해 다음과 같이 구현했습니다.

yogiyo-1 Screen Shot 2022-06-07 at 5 33 43 PM

scrollViewDidScroll 를 이용해서 사용자가 얼만큼 스크롤을 했는지 알아내서, 그만큼 headerconstraint 에 적용을 하는 방법으로 구현했습니다.

contentOffset

스크롤이 어느 방향으로 얼만큼 됐는지는 contentOffset 이라는 scrollView 프로퍼티를 이용하면 됩니다.

Screen Shot 2022-06-10 at 12 59 22 AM

contentOffset 이란 scrollViewcontentView frame 과의 상대적인 위치입니다.

그림으로 표현하면 다음과 같습니다.

Screen Shot 2022-06-10 at 1 09 03 AM

https://stackoverflow.com/a/39062209 참고

contentOffsety 좌표 값을 이용해서 스크롤을 계산할 수 있습니다.

스크롤에 따른 UI 변경

contentOffset 의 스크롤이 위로 됐는지, 또는 아래로 됐는지, 그리고 얼만큼 스크롤이 됐는지를 판별했으면, 그에 따른 UI 변경을 수행하는 로직은 다음과 같습니다.

Screen Shot 2022-06-10 at 12 26 30 AM

scrollView.contentOffset.y 에 따라서 스크롤이 위로 갔는지 아래로 갔는지 판단할 수 있는데

  • 30 을 초과했으면 shouldSnapUp
    • 이때 header 뷰를 위로 움직이는 애니메이션을 실행시킵니다
  • 0 이하이면 밑으로isSwipingDown
    • 이때 headeralpha 값을 이용해서 뷰를 투명하게 변경시킵니다.
    • 변경하지않으면 다음과 같은 부자연스러운 현상이 일어납니다.

Screen Shot 2022-06-10 at 1 27 57 AM

shouldSnapUp 일때는 다음 두가지 일이 일어나는데

  1. headerView 를 위로 이동시킨다

     private func snapUpHeaderView(by height: CGFloat, when shouldSnap: Bool) {
         self.headerViewTopConstraint?.constant = shouldSnap ? -height : 0
     }
    
  2. navigationBar 를 숨긴다

     private func hideNavigationBar(when shouldHide: Bool) {
         self.navigationController?.navigationBar.isHidden = shouldHide
     }
    

이 모든 UI 변경을 애니메이션과 함께 수행하는데

UIViewPropertyAnimator.runningPropertyAnimator 를 사용하여 한번에 해결했습니다.

Screen Shot 2022-06-10 at 1 28 10 AM

UIViewPropertyAnimator 는 뷰에 변경사항을 애니메이션과 함께 적용시킬 수 있고, 애니메이션에게 동적으로 변경사항을 줄 수 있는 클래스이며

Screen Shot 2022-06-10 at 1 45 59 AM

runningPropertyAnimator 는 애니메이션을 바로 수행시키는 클래스를 리턴하는 메서드입니다.

최종적으로 다음과 같이 애니메이션을 실행시켰습니다.

UIViewPropertyAnimator.runningPropertyAnimator(
            withDuration: 0.3,
            delay: 0,
            options: [],
            animations: {
            self.snapUpHeaderView(by: headerViewHeight, when: shouldSnap)
            self.hideNavigationBar(when: !shouldSnap)
            self.view.layoutIfNeeded()
        })

isSwipingDown 일때는 headerView 를 투명하게 만듭니다

private func applyAlpha(when isSwipingDown: Bool) {
    let transparencyDegree = 1.0
    
    UIView.animate(withDuration: 0.3) {
        self.headerView.alpha = isSwipingDown ? transparencyDegree : 0
    }
}

UIView.animate 를 사용해서 headeralpha 값을 변경시켰습니다.

uikit 카테고리 내 다른 글 보러가기