This repository has been archived by the owner on May 10, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 441
/
PopupView.swift
143 lines (122 loc) · 4.48 KB
/
PopupView.swift
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
/* Copyright 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import SwiftUI
import DesignSystem
/// Presents a SwiftUI view heirarchy in a popup that displays in the center of the screen
public class PopupViewController<Content: View>: UIViewController, UIViewControllerTransitioningDelegate, BasicAnimationControllerDelegate {
private let hostingController: UIHostingController<PopupView<Content>>
private let backgroundView = UIView().then {
$0.backgroundColor = UIColor(white: 0.0, alpha: 0.3)
}
public init(rootView: Content, isDismissable: Bool = false) {
let popup = PopupView({ rootView })
hostingController = UIHostingController(rootView: popup)
super.init(nibName: nil, bundle: nil)
transitioningDelegate = self
modalPresentationStyle = .overFullScreen
addChild(hostingController)
hostingController.didMove(toParent: self)
if isDismissable {
hostingController.rootView.onBackgroundTap = { [unowned self] in
self.dismiss(animated: true)
}
}
}
public override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
hostingController.view.backgroundColor = .clear
view.addSubview(backgroundView)
view.addSubview(hostingController.view)
backgroundView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
hostingController.view.snp.makeConstraints {
$0.edges.equalTo(view.safeAreaLayoutGuide)
}
}
@available(*, unavailable)
required init(coder: NSCoder) {
fatalError()
}
public func animatePresentation(context: UIViewControllerContextTransitioning) {
view.frame = context.finalFrame(for: self)
context.containerView.addSubview(view)
backgroundView.alpha = 0.0
hostingController.view.transform = CGAffineTransform(translationX: 0, y: context.containerView.bounds.height)
UIViewPropertyAnimator(duration: 0.35, dampingRatio: 1.0) { [self] in
backgroundView.alpha = 1.0
hostingController.view.transform = .identity
}.startAnimation()
context.completeTransition(true)
}
public func animateDismissal(context: UIViewControllerContextTransitioning) {
let animator = UIViewPropertyAnimator(duration: 0.25, dampingRatio: 1.0) { [self] in
backgroundView.alpha = 0.0
hostingController.view.transform = CGAffineTransform(translationX: 0, y: context.containerView.bounds.height)
}
animator.addCompletion { _ in
self.view.removeFromSuperview()
context.completeTransition(true)
}
animator.startAnimation()
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
BasicAnimationController(delegate: self, direction: .dismissing)
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
BasicAnimationController(delegate: self, direction: .presenting)
}
}
public struct PopupView<Content: View>: View {
public var content: Content
public var onBackgroundTap: (() -> Void)?
public init(@ViewBuilder _ content: () -> Content) {
self.content = content()
}
public var body: some View {
content
.frame(maxWidth: 400)
.background(Color(.braveBackground))
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
.shadow(color: .black.opacity(0.2), radius: 2, x: 0, y: 1)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
Color.clear
.contentShape(Rectangle())
.onTapGesture {
onBackgroundTap?()
}
)
}
}
#if DEBUG
struct PopupPreviews: PreviewProvider {
static var previews: some View {
Group {
PopupView {
VStack {
Circle()
.frame(width: 100, height: 100)
Text(verbatim: "Title")
.font(.headline)
Text(verbatim: "Subtitle Subtitle Subtitle Subtitle Subtitle")
.font(.subheadline)
.multilineTextAlignment(.center)
Button(action: {}) {
Text(verbatim: "Test")
}
.padding(.top)
.buttonStyle(BraveFilledButtonStyle(size: .normal))
}
}
.previewLayout(.sizeThatFits)
.background(Color.gray)
}
}
}
#endif