Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- swift documentation
- collectionview
- uitableview
- 클린 코드
- swiftUI
- clean architecture
- combine
- UITextView
- 리펙토링
- UICollectionView
- Clean Code
- HIG
- Human interface guide
- MVVM
- uiscrollview
- SWIFT
- ribs
- 스위프트
- Observable
- Protocol
- 리펙터링
- 리팩토링
- Refactoring
- map
- rxswift
- 애니메이션
- tableView
- ios
- Xcode
- RxCocoa
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. Sticky Header 구현 - 스크롤 시 상단 해더 숨기는 방법 본문
1. Sticky Header 구현 - 스크롤 시 상단 해더 숨기는 방법
2. Sticky Header 구현 - 스크롤 시 상단에 뷰 붙이는 방법
Sticky Header
- 개념: 스크롤 뷰 상단에 마치 붙어있다가 아래로 스크롤하면 뷰가 떼어진다고 하여 Sticky Header라고 명칭
구현 아이디어
- UIScrollView가 가장 하위에 있고, 그 위에 StickyHeaderView가 존재
- UIScrollView의 델리게이트 메소드 중 scrollViewDidScroll(_ scrollView: UIScrollView)를 사용하여 스크롤 offset에 따라 StickyHeader뷰의 alpha값만 조정하면 구현 완료
Sticky Header 구현
* 코드로 오토레이아웃 정의에 편리한 SnapKit 사용
- 레이아웃 정의에 필요한 상수 정의
- statusBarHeight 같은 경우 +6을 따로 해주어야 높이가 맞으므로 주의
- stickyHeaderHeightMin, max같은 경우는 StickyHeader를 두 개 둘것인데, Max는 처음에 보이는 Sticy Header뷰의 높이를 의마하고 min은 두 번째 보이는 Sticy header 뷰를 의미
- stickyHeaderHeightMaxWithoutStatusBar는 scrollview의 오토레이아웃에 사용
import UIKit
import SnapKit
class ViewController: UIViewController {
// MARK: Constants
private enum Metric {
// 1. statusBarHeight에 6을 더해야 진정한 statusBarheight가 구해지므로 주의
static let statusBarHeight = UIApplication.shared.statusBarFrame.height + 6
static let stickyHeaderHeightMin = 80.0
static let stickyHeaderHeightMax = 180.0
static var stickyHeaderHeightMaxWithoutStatusBar: Double {
stickyHeaderHeightMax - statusBarHeight
}
}
}
- 필요한 뷰 선언
// MARK: UIs
private let scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = .lightGray
print(Metric.statusBarHeight)
// 2. contentInset의 top은 위 헤더뷰만큼 떨어져 있어야 스크롤 맨 위가 헤더뷰에 안가려짐
// contentInset은 superview기준이 아니고 safearea기준이므로 statusBar의 높이값 제외해야함
view.contentInset = .init(top: Metric.stickyHeaderHeightMaxWithoutStatusBar, left: 0, bottom: 0, right: 0)
return view
}()
private let beforeHeaderView: UIView = {
let view = UIView()
view.backgroundColor = .systemBlue
return view
}()
private let afterHeaderView: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.alpha = 0
return view
}()
private let stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
return view
}()
private let label: UILabel = {
let label = UILabel()
label.text = "1long text\n\n\n2long text\n\n\n\n\n3long textlong text\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4textlongtextlong"
label.textColor = .white
label.numberOfLines = 0
label.font = .systemFont(ofSize: 36)
return label
}()
- viewDidLoad에서 레이아웃 선언
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
[scrollView, beforeHeaderView, afterHeaderView]
.forEach(view.addSubview(_:))
scrollView.addSubview(stackView)
stackView.addArrangedSubview(label)
beforeHeaderView.snp.makeConstraints {
$0.top.left.right.equalToSuperview()
$0.height.equalTo(Metric.stickyHeaderHeightMax)
}
afterHeaderView.snp.makeConstraints {
$0.top.left.right.equalToSuperview()
$0.height.equalTo(Metric.stickyHeaderHeightMin)
}
scrollView.snp.makeConstraints {
// 3. scorllView는 superview로 top 고정시켜놓으면 처음 offset이 safearea의 top으로 설정해야 offset 조정에 오차가 없음
$0.top.equalTo(view.safeAreaLayoutGuide)
$0.left.right.bottom.equalToSuperview()
}
stackView.snp.makeConstraints {
$0.edges.width.equalToSuperview()
}
}
- 델리게이트 구현
- 위에서 scrollView의 contrentInset.top값을 설정했으므로, scrollView.contentOffset의 초기값도 contentInset.top과 동일하며, contentOffset.y는 뷰의 (0,0) 좌표에서 멀어질수록 - 값을 띄우므로 (0,0)까지 남은 topSapcing은 -값을 부여하여 선언
- 남은 top sacping의 비율값은 처음에 top spacing으로 정의했던 Metric.stickyHeaderHeightMaxWithoutStatusBar으로 나누어주어서 계산
- alpha값을 이용하여 앞으로 숨길 뷰와 숨겨질 뷰를 표현
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let remainingTopSpacing = -scrollView.contentOffset.y
let reaminingTopSpacingRatio = remainingTopSpacing / Metric.stickyHeaderHeightMaxWithoutStatusBar
beforeHeaderView.alpha = reaminingTopSpacingRatio
afterHeaderView.alpha = 1 - reaminingTopSpacingRatio
}
}
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments