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
- Clean Code
- rxswift
- HIG
- 클린 코드
- ribs
- Protocol
- combine
- 리펙터링
- 스위프트
- clean architecture
- map
- ios
- Xcode
- 리펙토링
- 리팩토링
- SWIFT
- UICollectionView
- tableView
- uitableview
- RxCocoa
- swift documentation
- 애니메이션
- uiscrollview
- Observable
- swiftUI
- Human interface guide
- UITextView
- Refactoring
- collectionview
- MVVM
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 고정된 SheetView (TopSheetView, BottomSheetView) 구현 방법 본문
UI 컴포넌트 (swift)
[iOS - swift] 고정된 SheetView (TopSheetView, BottomSheetView) 구현 방법
jake-kim 2022. 3. 28. 23:24
* 애플의 지도앱과 같이 pan 제스처에 따라 sheet가 동적으로 이동되는 뷰는 UISheetPresentationController 포스팅 글 참고
구현에 편리를 위해 사용한 프레임워크
pod 'SnapKit'
pod 'RxSwift'
pod 'RxCocoa'
pod 'RxGesture'
구현 아이디어
- Sheet뷰의 핵심은 autolayout을 이용하여 위에서 나오도록 설정
- 커스텀 뷰 안에 show(), hide()를 구현하여 여기서 autolayout을 변경하여 애니메이션이 동작하도록 구현
- backgroundView와 contentView를 놓고 backgroundView는 dimmed처리용도, contentView는 컨텐츠를 담을 용도
- autolayout 핵심 - topSheet처럼 contentView가 위에서 내려와야하므로, 초기 layout은 아래처럼 설정
self.contentView.snp.makeConstraints {
$0.bottom.equalTo(self.snp.top)
$0.width.equalToSuperview()
}
- 아래로 내려오는 애니메이션에서는 아래처럼 레이아웃 설정 (애니메이션은 뒤에서 계속 설명)
self.contentView.snp.remakeConstraints {
$0.top.left.right.equalToSuperview()
$0.bottom.lessThanOrEqualTo(self.safeAreaLayoutGuide).inset(Metric.contentViewInset).priority(999)
}
구현 방법
- 기본적인 뷰 구현
// SheetView.swift
class SheetView: UIView {
private enum Metric {
static let cornerRadius = 14.0
static let contentViewInset = UIEdgeInsets(top: 0, left: 0, bottom: 120, right: 0)
static let sampleLabelTopSpacing = 120.0
static let bottomLeftBottomRightCornerMask: CACornerMask = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
}
private enum Color {
static let white = UIColor.white
static let dimmedBlack = UIColor.black.withAlphaComponent(0.25)
static let clear = UIColor.clear
}
private lazy var backgroundView: UIView = {
let view = UIView()
view.clipsToBounds = true
return view
}()
private lazy var contentView: UIView = {
let view = UIView()
view.backgroundColor = Color.white
view.layer.cornerRadius = Metric.cornerRadius
view.layer.maskedCorners = Metric.bottomLeftBottomRightCornerMask
view.clipsToBounds = true
return view
}()
private lazy var someButton: UIButton = {
let button = UIButton()
button.setTitle("예시 버튼", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.setTitleColor(.blue, for: .highlighted)
return button
}()
private lazy var sampleLabel: UILabel = {
let label = UILabel()
label.text = "sample top sheet"
label.textAlignment = .center
return label
}()
}
- 핵심인 show와 hide 메소드 구현
- hide할때는 뷰를 삭제하도록 하여, 사용하는 쪽에서 show()할때마다 새로운 인스턴스를 생성하도록 구현
// SheetView.swift
func show(completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
self.contentView.snp.remakeConstraints {
$0.top.left.right.equalToSuperview()
$0.bottom.lessThanOrEqualTo(self.safeAreaLayoutGuide).inset(Metric.contentViewInset).priority(999)
}
UIView.animate(
withDuration: 0.3,
delay: 0,
options: .curveEaseInOut,
animations: {
self.backgroundView.backgroundColor = Color.dimmedBlack
self.layoutIfNeeded()
},
completion: { _ in completion?() }
)
}
}
func hide(completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
self.contentView.snp.remakeConstraints {
$0.bottom.equalTo(self.snp.top)
$0.left.right.equalToSuperview()
}
UIView.animate(
withDuration: 0.3,
delay: 0,
options: .curveEaseInOut,
animations: {
self.backgroundView.backgroundColor = Color.clear
self.layoutIfNeeded()
},
completion: { _ in
completion?()
self.removeFromSuperview()
}
)
}
}
사용하는 쪽
- SheetView 인스턴스 생성
- 레이아웃 설정
- dimmed 효과가 보여야 하므로, 뷰에 꽉차게 레이아웃 설정
- show()하는 순간, 위에서 아래로 내려오는 topSheet형태가 되어야 하므로, 애니메이션 전에 이미 뷰의 레이아웃이 자리잡도록 layoutIfNeeded()호출
// ViewController.swift
@objc private func didTapOpenButton() {
let sheetView = SheetView()
self.view.addSubview(sheetView)
sheetView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
self.view.layoutIfNeeded()
sheetView.show()
}
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments