Skip to content

Commit a8fd239

Browse files
authored
[Feature] 베지어 다이어로그 스펙 구현 (#10)
* [Feature] 베지어 다이어로그 스펙 구현 * [Improvement] priority와 dismiss 로직 처리 추가 * [Etc] 리뷰 적용 * [Improvement] 애니메이션 추가
1 parent 0ccd48e commit a8fd239

File tree

5 files changed

+223
-20
lines changed

5 files changed

+223
-20
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import PackageDescription
66
let package = Package(
77
name: "BezierSwift",
88
platforms: [
9-
.iOS(.v13),
9+
.iOS(.v14),
1010
],
1111
products: [
1212
// Products define the executables and libraries a package produces, and make them visible to other packages.

Sources/BezierSwift/MasterComponent/BezierDialog/BezierDialog.swift

Lines changed: 216 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,224 @@
66
//
77

88
import SwiftUI
9+
import Dispatch
910

10-
struct BezierDialog: View {
11-
var body: some View {
12-
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
13-
}
11+
private enum Metric {
12+
static let dimSideMinPadding = CGFloat(40)
13+
14+
static let dialogMaxWidth = CGFloat(480)
15+
static let dialogPadding = CGFloat(16)
16+
17+
static let upperStackContainerTop = CGFloat(4)
18+
static let upperStackSpace = CGFloat(8)
19+
20+
static let middleStackTop = CGFloat(16)
21+
static let middleStackSpace = CGFloat(12)
22+
23+
static let belowStackTop = CGFloat(20)
24+
static let belowStackSpace = CGFloat(8)
25+
}
26+
27+
public enum BezierDialogButtonInfo {
28+
case vertical([BezierButton])
29+
case horizontal([BezierButton])
30+
}
31+
32+
public struct BezierDialogParam {
33+
var id = UUID()
34+
35+
// TODO: priority 일정 enum으로 제한두기. by jam 2023.02.24
36+
var priority: Int
37+
var title: String
38+
var description: String
39+
var buttonInfo: BezierDialogButtonInfo?
40+
var dismissCompletion: (() -> Void)?
41+
42+
public init(priority: Int, title: String?, description: String?, buttonInfo: BezierDialogButtonInfo?, dismissCompletion: (() -> Void)?) {
43+
self.priority = priority
44+
self.title = title ?? ""
45+
self.description = description ?? ""
46+
self.buttonInfo = buttonInfo
47+
self.dismissCompletion = dismissCompletion
48+
}
49+
}
50+
51+
public struct BezierDialogManager {
52+
public static func show(with param: BezierDialogParam) {
53+
BezierDialogSingleton.shared.viewModel.update(param: param)
54+
}
55+
56+
public static func dismiss() {
57+
BezierDialogSingleton.shared.viewModel.dismiss()
58+
}
1459
}
1560

16-
struct BezierDialog_Previews: PreviewProvider {
17-
static var previews: some View {
18-
BezierDialog()
61+
extension View {
62+
@available(iOS 15.0, *)
63+
public func initBezierDialog() -> some View {
64+
self.modifier(BezierDialog())
65+
}
66+
}
67+
68+
class DialogViewModel: ObservableObject {
69+
@Published private(set) var isPresented: Bool = false
70+
71+
@Published var title: String = ""
72+
@Published var description: String = ""
73+
@Published var buttons: [BezierButton] = []
74+
@Published var isButtonStackVertical = false
75+
76+
var currentParamId: UUID = UUID()
77+
var currentDismissCompletion: (() -> Void)?
78+
var currentPriority: Int = Int.min
79+
80+
func update(param: BezierDialogParam) {
81+
guard param.priority > self.currentPriority else {
82+
param.dismissCompletion?()
83+
return
84+
}
85+
86+
let delayTime: DispatchTime = .now() + (self.isPresented ? 0.3 : 0)
87+
88+
self.dismiss()
89+
90+
DispatchQueue.main.asyncAfter(deadline: delayTime) {
91+
self.currentParamId = param.id
92+
self.currentDismissCompletion = param.dismissCompletion
93+
self.currentPriority = param.priority
94+
95+
self.title = param.title
96+
self.description = param.description
97+
98+
if let buttonInfo = param.buttonInfo {
99+
switch buttonInfo {
100+
case .vertical(let buttons):
101+
self.buttons = buttons
102+
self.isButtonStackVertical = true
103+
case .horizontal(let buttons):
104+
self.buttons = buttons
105+
self.isButtonStackVertical = false
106+
}
107+
}
108+
109+
withAnimation(.easeInOut(duration: 0.3)) {
110+
self.isPresented = true
111+
}
19112
}
113+
}
114+
115+
func dismiss() {
116+
self.currentDismissCompletion?()
117+
118+
self.currentDismissCompletion = nil
119+
self.currentParamId = UUID()
120+
self.currentPriority = Int.min
121+
122+
withAnimation(.easeInOut(duration: 0.3)) {
123+
self.isPresented = false
124+
}
125+
}
126+
}
127+
128+
class BezierDialogSingleton {
129+
var viewModel: DialogViewModel
130+
131+
static let shared = BezierDialogSingleton()
132+
133+
init() {
134+
self.viewModel = DialogViewModel()
135+
}
136+
}
137+
138+
@available(iOS 15.0, *)
139+
struct BezierDialog: ViewModifier, Themeable {
140+
@Environment(\.colorScheme) public var colorScheme
141+
142+
@StateObject private var viewModel: DialogViewModel = BezierDialogSingleton.shared.viewModel
143+
144+
145+
func body(content: Content) -> some View {
146+
147+
content
148+
.overlay {
149+
if viewModel.isPresented {
150+
self.palette(.bgtxtAbsoluteBlackLighter)
151+
.edgesIgnoringSafeArea(.all)
152+
.frame(maxWidth: .infinity, maxHeight: .infinity)
153+
.overlay {
154+
VStack(alignment: .center, spacing: .zero) {
155+
HStack(spacing: .zero) {
156+
HStack(spacing: .zero) {
157+
VStack(alignment: .center, spacing: Metric.belowStackTop) {
158+
if !viewModel.title.isEmpty || !viewModel.description.isEmpty {
159+
VStack(alignment: .center, spacing: Metric.upperStackSpace) {
160+
Text(viewModel.title)
161+
.applyBezierFontStyle(.bold16)
162+
Text(viewModel.description)
163+
.applyBezierFontStyle(.normal14)
164+
}
165+
.padding(.top, Metric.upperStackContainerTop)
166+
}
167+
168+
if !viewModel.buttons.isEmpty {
169+
if viewModel.isButtonStackVertical {
170+
VStack(spacing: Metric.belowStackSpace) {
171+
ForEach(viewModel.buttons.prefix(4).indices, id: \.self) { idx in
172+
viewModel.buttons[idx]
173+
}
174+
}
175+
} else {
176+
HStack(spacing: Metric.belowStackSpace) {
177+
ForEach(viewModel.buttons.prefix(2).indices, id: \.self) { idx in
178+
viewModel.buttons[idx]
179+
}
180+
}
181+
}
182+
}
183+
184+
}
185+
.padding(.all, Metric.dialogPadding)
186+
.frame(maxWidth: .infinity)
187+
}
188+
.background(self.palette(.bgWhiteHigh))
189+
.applyBezierCornerRadius(type: .round16)
190+
.applyBezierElevation(self, type: .mEv3)
191+
.frame(maxWidth: Metric.dialogMaxWidth)
192+
.padding(.horizontal, Metric.dimSideMinPadding)
193+
}
194+
}
195+
}
196+
}
197+
}
198+
}
20199
}
200+
201+
// TODO: 프리뷰 살리기(현재 xcode 크러시 남) by jam 2023.02.24
202+
//struct BezierDialog_Previews: PreviewProvider {
203+
// static var previews: some View {
204+
// if #available(iOS 15.0, *) {
205+
// Color.clear
206+
// .initBezierDialog()
207+
// .task {
208+
// BezierDialogManager.show(
209+
// with: BezierDialogParam(
210+
// title: "testTitle",
211+
// description: "testDescription",
212+
// buttonInfo: .horizontal(
213+
// [
214+
// BezierButton(size: .large, type: .primary(.blue), resizing: .fill, title: "버튼1") {
215+
// print("")
216+
// },
217+
//
218+
// BezierButton(size: .large, type: .primary(.monochromeDark), resizing: .fill, title: "버튼2") {
219+
// print("")
220+
// }
221+
// ]
222+
// ))
223+
// )
224+
// }
225+
// } else {
226+
// EmptyView()
227+
// }
228+
// }
229+
//}

Sources/BezierSwift/MasterComponent/BezierToast/BezierToast.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,13 @@ struct BezierToast_Previews: PreviewProvider {
133133
title: "Bezier Toast"
134134
)
135135

136-
if #available(iOS 14.0, *) {
137-
return VStack {
138-
BezierToast(viewModel: onlyTitleViewModel)
139-
BezierToast(viewModel: withImageViewModel)
140-
BezierToast(viewModel: withIconViewModel)
141-
}
142-
// If you want a global toast, use it
143-
.bezierToast(viewModel: .constant(onlyTitleViewModel))
144-
} else {
145-
return EmptyView()
136+
return VStack {
137+
BezierToast(viewModel: onlyTitleViewModel)
138+
BezierToast(viewModel: withImageViewModel)
139+
BezierToast(viewModel: withIconViewModel)
146140
}
141+
// If you want a global toast, use it
142+
.bezierToast(viewModel: .constant(onlyTitleViewModel))
147143
}
148144
}
149145

Sources/BezierSwift/MasterComponent/BezierToast/BezierToastViewModifier.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import SwiftUI
99

10-
@available(iOS 14.0, *)
1110
struct BezierToastViewModifier: ViewModifier {
1211
@Binding private var viewModel: BezierToastViewModel?
1312

Sources/BezierSwift/MasterComponent/BezierToast/View+BezierToast.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import SwiftUI
99

1010
extension View {
11-
@available(iOS 14.0, *)
1211
public func bezierToast(viewModel: Binding<BezierToastViewModel?>) -> some View {
1312
modifier (
1413
BezierToastViewModifier(viewModel: viewModel)

0 commit comments

Comments
 (0)