-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBottomSheetViewController.swift
More file actions
151 lines (117 loc) · 4.64 KB
/
Copy pathBottomSheetViewController.swift
File metadata and controls
151 lines (117 loc) · 4.64 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//
// BottomSheetViewController.swift
// Bottomsheet
//
// Created by Globak Maksim on 21.06.2018.
// Copyright © 2018 Globak Maksim. All rights reserved.
//
import UIKit
protocol BottomSheetDataSource: class {
func viewController(for bottomSheet: BottomSheetViewController) -> UIViewController
}
class BottomSheetViewController: UIViewController {
// MARK: - Public
var dataSource: BottomSheetDataSource?
// MARK: - Private variables
private var bottomSheetTransitioninDelegate = BottomSheetTransitioningDelegate()
private var statusBarStyle: UIStatusBarStyle = .default {
didSet {
setNeedsStatusBarAppearanceUpdate()
}
}
private lazy var dragArea: UIView = {
let dragView = UIView()
dragView.backgroundColor = .lightGray
dragView.layer.cornerRadius = 2;
return dragView
}()
// MARK: - Initialization
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
transitioningDelegate = bottomSheetTransitioninDelegate
modalPresentationStyle = .custom
}
// MARK: - ViewController life cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
presentDataSource()
roundTopCorners(for: view)
setupDragArea()
setupPanGestureRecognizer()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
modalPresentationCapturesStatusBarAppearance = true
statusBarStyle = .lightContent
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
statusBarStyle = .default
modalPresentationCapturesStatusBarAppearance = false
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return statusBarStyle
}
// MARK: - Helper functions
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
bottomSheetTransitioninDelegate.isInteractive = true
super.dismiss(animated: flag, completion: completion)
}
private func presentDataSource() {
guard let dataSource = dataSource?.viewController(for: self) else { return }
addChildViewController(dataSource)
view.addSubview(dataSource.view)
dataSource.didMove(toParentViewController: self)
}
private func setupPanGestureRecognizer() {
let gestureRecignizer = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:)))
view.addGestureRecognizer(gestureRecignizer)
}
private func setupDragArea() {
view.addSubview(dragArea)
dragArea.translatesAutoresizingMaskIntoConstraints = false
dragArea.topAnchor.constraint(equalTo: view.topAnchor, constant: 4).isActive = true
dragArea.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dragArea.heightAnchor.constraint(equalToConstant: 4).isActive = true
dragArea.widthAnchor.constraint(equalToConstant: 32).isActive = true
}
private func roundTopCorners(for view: UIView) {
// TODO: - Move to view extension
let cornerRadius: CGFloat = 16
let cornerRadii = CGSize(width: cornerRadius, height: cornerRadius)
let bottomSheetPath = UIBezierPath(roundedRect:view.bounds, byRoundingCorners:[.topLeft, .topRight], cornerRadii: cornerRadii)
let bottomSheetMaskLayer = CAShapeLayer()
bottomSheetMaskLayer.path = bottomSheetPath.cgPath
view.layer.mask = bottomSheetMaskLayer
}
// MARK: - Actions
@objc private func handleGesture(_ sender: UIPanGestureRecognizer) {
let transition = bottomSheetTransitioninDelegate.transition
let translation = sender.translation(in: view)
let verticalMovement = translation.y / view.bounds.height
let progress = min(max(verticalMovement, 0.0), 1.0)
let shouldClose = progress > 0.3
switch sender.state {
case .began:
dismiss(animated: true, completion: nil)
case .changed:
transition.update(progress)
case .cancelled:
transition.cancel()
case .ended:
shouldClose
? transition.finish()
: transition.cancel()
default:
break
}
}
}