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 | 31 |
Tags
- clean architecture
- map
- Protocol
- Refactoring
- 애니메이션
- collectionview
- tableView
- Clean Code
- swiftUI
- 클린 코드
- ios
- swift documentation
- uiscrollview
- 리펙터링
- MVVM
- RxCocoa
- Xcode
- uitableview
- rxswift
- Human interface guide
- HIG
- combine
- 리팩토링
- SWIFT
- ribs
- UICollectionView
- Observable
- 리펙토링
- 스위프트
- UITextView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. ContainerView 활용 방법 - UIView를 present, dismiss 트랜지션처럼 구현 방법, layering 본문
iOS 응용 (swift)
[iOS - swift] 2. ContainerView 활용 방법 - UIView를 present, dismiss 트랜지션처럼 구현 방법, layering
jake-kim 2022. 5. 13. 23:271. ContainerView 활용 방법 - UINavigationView와 View hierarchy
2. ContainerView 활용 방법 - UIView를 present, dismiss 트랜지션 애니메이션 구현 방법
* 1번에서 구현한 뷰 계층 관계: VC1 < VC2, VC3 < VC1 위에 떠있는 버튼
구현할 present, dismiss 애니메이션
- ViewController지만, 이전 화면을 덮지 않는 view hierarchy를 사용하고 싶은 경우 해결 (이전 1번 포스팅 글에서의 내용)
- present하지 않고 단순히 애니메이션 만으로 present, dismiss처럼 트랜잭션을 구현
- 오토 레이아웃을 사용하면 손쉽게 구현 가능
예제에 사용한 프레임워크
- 오토레이아웃을 손쉽게 다루기 위해 SnapKit 사용
present, dismiss 애니메이션 구현 아이디어
- 오토레이아웃 + UIView.animte를 사용하면 손쉽게 뷰가 이동되는 애니메이션이 가능하므로 사용
self.containerView.snp.remakeConstraints {
$0.edges.equalToSuperview()
}
UIView.animate(
withDuration: 0.3,
delay: 0,
options: .curveEaseInOut,
animations: { self.view.layoutIfNeeded() },
completion: { _ in completion() }
)
- VC1에서 VC2의 뷰를 present처럼 올라오는 애니메이션을 사용하고 싶은 경우 구조
- VC2.view의 backgroundColor를 clear로 설정
- VC2.view 뷰의 레이아웃 -> VC1.view와 동일하도록 (꽉 차도록 설정)
- VC2.view에 containerView를 하나 만들어서 이 뷰를 show()메소드가 불릴때 위로 올라오도록 구현
present처럼 보여질 VC2 구현
- 필요한 UI 준비
- present처럼 보여져야 하므로 containerView
- 제목을 표시할 titleLabel
- 버튼 클릭 시 push가 되어야 하므로 button
import UIKit
import SnapKit
class VC2: UIViewController {
private let containerView: UIView = {
let view = UIView()
return view
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.text = "VC2"
label.textColor = .black
return label
}()
private let button: UIButton = {
let button = UIButton()
button.setTitle("다음 화면 push", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.setTitleColor(.blue, for: .highlighted)
button.addTarget(self, action: #selector(pushVC), for: .touchUpInside)
return button
}()
- viewDidLoad()에서 레이아웃을 작성하지 않고 init에서 작성
- viewDidLoad가 아닌 init()에서 설정안하면, show()메소드가 호출되었을때 containerView가 아직 addSubview되지 않은 상태라 크래시가 발생
- containerView의 레이아웃 시작점은 바닥에 위치해야하므로 top이 현재 뷰의 bottom과 동일하게 작성
init() {
super.init(nibName: nil, bundle: nil)
self.view.backgroundColor = .clear
self.containerView.backgroundColor = .systemGreen
self.view.addSubview(self.containerView)
self.containerView.addSubview(self.titleLabel)
self.containerView.addSubview(self.button)
self.containerView.snp.makeConstraints {
$0.left.right.equalToSuperview()
$0.top.equalTo(self.view.snp.bottom).priority(999)
}
self.titleLabel.snp.makeConstraints {
$0.top.equalToSuperview().inset(30)
$0.centerX.equalToSuperview()
}
self.button.snp.makeConstraints {
$0.top.equalToSuperview().inset(70)
$0.centerX.equalToSuperview()
}
// layoutIfNeeded호출 안해줄 경우, 좌측 상단에서 레이아웃이 시작
self.view.layoutIfNeeded()
}
- 버튼을 탭한 경우 푸시
@objc private func pushVC() {
let vc = VC3()
self.navigationController?.pushViewController(vc, animated: true)
}
- show와 hide 메소드 정의
- show - containerView는 바닥에 위치하고 있으므로, show일때는 화면에 꽉차게 설정해놓을 경우 위로 올라오는 애니메이션 동작
- hide - 화면에 지금 꽉차고 있는 상태이므로, 밑으로 내려가는 애니메이션 동작
func show(completion: @escaping () -> Void = {}) {
self.containerView.snp.remakeConstraints {
$0.edges.equalToSuperview()
}
UIView.animate(
withDuration: 0.3,
delay: 0,
options: .curveEaseInOut,
animations: { self.view.layoutIfNeeded() },
completion: { _ in completion() }
)
}
func hide(completion: @escaping () -> Void = {}) {
self.containerView.snp.remakeConstraints {
$0.left.right.equalToSuperview()
$0.top.equalTo(self.view.snp.bottom).priority(999)
}
UIView.animate(
withDuration: 0.3,
delay: 0,
options: .curveEaseInOut,
animations: { self.view.layoutIfNeeded() },
completion: { _ in completion() }
)
}
사용하는 쪽
- 사용하는쪽에서는 필요할때만 VC2인스턴스를 생성하고, 다시 뷰를 닫을때는 removeFromSuperview하도록 설계
- 주의할점은 navigationController는 strong으로 잡아주지 않으면 VC2에서 push할때 navigationController가 nil이 되므로 주의
// ViewController.swift
extension ViewController {
@objc private func openView() {
guard self.vc2 == nil else { return }
let vc2 = VC2()
self.vc2 = vc2
let nav = UINavigationController(rootViewController: vc2)
self.nav = nil
self.nav = nav
self.view.insertSubview(nav.view, belowSubview: self.aboveButton)
nav.view.snp.makeConstraints {
$0.edges.equalToSuperview()
}
vc2.show()
}
@objc private func closeView() {
guard self.vc2 != nil else { return }
self.vc2?.hide { [weak self] in
self?.vc2?.view.removeFromSuperview()
self?.vc2 = nil
}
}
}
* 전체 코드: https://github.com/JK0369/ExPresentDismissAnimation
'iOS 응용 (swift)' 카테고리의 다른 글
Comments