관리 메뉴

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

[iOS - swift] 2. Sticky Header 구현 - 스크롤 시 상단에 뷰 붙이는 방법 본문

UI 컴포넌트 (swift)

[iOS - swift] 2. Sticky Header 구현 - 스크롤 시 상단에 뷰 붙이는 방법

jake-kim 2023. 1. 11. 20:53

1. Sticky Header 구현 - 스크롤 시 상단 해더 숨기는 방법

2. Sticky Header 구현 - 스크롤 시 상단에 뷰 붙이는 방법

구현된 Sticky Header - 스크롤 시 상단에 뷰 붙이기

구현 아이디어

  • sticky할 뷰와 동일한 형태 뷰 준비 + 숨김 상태로 초기화
  • sticky 뷰는 scroll 안에 넣지 않고 맨 상단에 보이도록 scrollView보다 위에 있도록 addSubview
  • scrollView의 top은 topView의 하단에 붙이기 (topView의 하단에 sticky도 붙일것)
  • sticky 뷰는 topView하단에 붙이기 + stikcy의 높이는 sticky할 뷰의 높이와 동일하게 설정
  • 스크롤 뷰의 델리게이트 메소드인 scrollViewDidScroll에서 stikcy 타이밍을 계산하여 stikcyHeaderView를 hide/show

구현

* 예제에는 코드로 오토레이아웃 정의에 편리한 SnapKit 사용 

  • Sticky가 없는 기본 뷰 구현
    • 크게 topView와 scrollView가 있고, 스크롤 되는것을 테스트하기위해 scrollView안에 stackView를 넣은 후 구현
import UIKit
import SnapKit

class ViewController: UIViewController {
    private let topView: UIView = {
        let view = UIView()
        view.backgroundColor = .gray
        return view
    }()
    private let scrollView: UIScrollView = {
        let view = UIScrollView()
        return view
    }()
    private let stackView: UIStackView = {
        let view = UIStackView()
        view.axis = .vertical
        return view
    }()
    private let headerView: UIView = {
        let view = UIView()
        view.backgroundColor = .green
        return view
    }()
    private let label1: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.text = "long text \n\n text \n\n\n text \n\n text \n text \n\n\n texttexttext"
        label.textColor = .black
        return label
    }()
    private let label2: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.text = "long text \n\n text \n\n\n text \n\n text \n text \n\n\n texttexttext \n\n\n\n text \n\n\n\n\n\n\n text \n\n\n\n\n\n\n\n\n\n text \n\n\n\n \n\n\n\n \n\n\n\n \n\n\n\n text"
        label.textColor = .black
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(topView)
        view.addSubview(scrollView)
        scrollView.addSubview(stackView)
        stackView.addArrangedSubview(label1)
        stackView.addArrangedSubview(headerView)
        stackView.addArrangedSubview(label2)
        
        topView.snp.makeConstraints {
            $0.top.left.right.equalToSuperview()
            $0.height.equalTo(120)
        }
        scrollView.snp.makeConstraints {
            $0.top.equalTo(topView.snp.bottom)
            $0.left.bottom.right.equalToSuperview()
        }
        stackView.snp.makeConstraints {
            $0.edges.width.equalToSuperview()
        }
        headerView.snp.makeConstraints {
            $0.height.equalTo(56)
        }
    }
}

  • stikcy할 뷰와 동일한 형태의 뷰 준비
    // 1. sticky할 뷰와 동일한 형태 뷰 준비 + 숨김 상태로 초기화
    private let stickyHeaderView: UIView = {
        let view = UIView()
        view.backgroundColor = .green
        view.isHidden = true
        return view
    }()
  • sticky 뷰 삽입
// 2. sticky 뷰는 scroll 안에 넣지 않고 맨 상단에 보이도록 scrollView보다 위에 삽입
view.addSubview(stickyHeaderView)
  • 스크롤뷰의 top이 어디에 있는지 확인 후, sticky뷰의 top도 동일하게 붙이기
// 3. scrollView의 top은 topView의 하단에 붙이기 (topView의 하단에 sticky도 붙일것)
scrollView.snp.makeConstraints {
    $0.top.equalTo(topView.snp.bottom)
    $0.left.bottom.right.equalToSuperview()
}

// 4. sticky 뷰는 topView하단에 붙이기 + stikcy의 높이는 sticky할 뷰의 높이와 동일하게 설정
stickyHeaderView.snp.makeConstraints {
    $0.top.equalTo(topView.snp.bottom)
    $0.left.right.equalToSuperview()
    $0.height.equalTo(56)
}
  • scrollViewDidScroll 델리게이트에서 stikcy를 언제 hide/show 할지 판단
    • 핵심) sticky할 뷰의 frame.minY와 contentOffset.y를 구분하면 쉽게 파악이 가능
scrollView.delegate = self

...

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // contentOffset.y: 손가락을 위로 올리면 + 값, 손가락을 아래로 내리면 - 값
        print(scrollView.contentOffset.y, headerView.frame.minY)
        
        // 5. 핵심 - frame.minY를 통해 sticky 타이밍을 계산
        let shouldShowSticky = scrollView.contentOffset.y >= headerView.frame.minY
        stickyHeaderView.isHidden = !shouldShowSticky
    }
}

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

Comments