8
8
9
9
import SwiftUI
10
10
11
+ class ObservableBool : ObservableObject {
12
+ @Published var value : Bool {
13
+ didSet {
14
+ didSet ? ( )
15
+ }
16
+ }
17
+
18
+ var didSet : ( ( ) -> ( ) ) ?
19
+
20
+ init ( _ value: Bool = false ) {
21
+ self . value = value
22
+ }
23
+ }
24
+
25
+ class ObservableInt : ObservableObject {
26
+ @Published var value : Int {
27
+ didSet {
28
+ didSet ? ( )
29
+ }
30
+ }
31
+
32
+ var didSet : ( ( ) -> ( ) ) ?
33
+
34
+ init ( _ value: Int = 0 ) {
35
+ self . value = value
36
+ }
37
+ }
38
+
11
39
public struct ConcentricOnboardingView : View {
12
40
public var animationWillBegin = { }
13
41
public var animationDidEnd = { }
14
42
public var didGoToLastPage = { }
43
+ public var didPressNextButton : ( ( ) -> ( ) ) ? // replaces default button action with user's custom closure
15
44
public var currentPageIndex : Int {
16
- return currentIndex
45
+ return currentIndex. value
17
46
}
18
47
19
48
let radius : Double = 30
@@ -29,20 +58,12 @@ public struct ConcentricOnboardingView : View {
29
58
let bgColors : [ Color ]
30
59
let duration : Double // in seconds
31
60
32
- @State var currentIndex = 0
33
- @State var nextIndex = 1
61
+ @ObservedObject var currentIndex = ObservableInt ( )
62
+ @ObservedObject var nextIndex = ObservableInt ( 1 )
63
+ @ObservedObject var isAnimating = ObservableBool ( )
64
+ @ObservedObject var isAnimatingForward = ObservableBool ( true )
34
65
35
66
@State var progress : Double = 0
36
- @State var isAnimating = false {
37
- didSet {
38
- if isAnimating && ( pages. count < 2 || bgColors. count < 2 ) {
39
- isAnimating = false
40
- return
41
- }
42
- isAnimating ? animationWillBegin ( ) : animationDidEnd ( )
43
- }
44
- }
45
- @State var isAnimatingForward = true
46
67
@State var bgColor = Color . white
47
68
@State var circleColor = Color . white
48
69
@@ -65,27 +86,46 @@ public struct ConcentricOnboardingView : View {
65
86
print ( " Add more bg colors " )
66
87
}
67
88
68
- if bgColors. count > currentIndex {
69
- bgColor = bgColors [ currentIndex]
89
+ if bgColors. count > currentIndex. value {
90
+ bgColor = bgColors [ currentIndex. value ]
70
91
}
71
- if bgColors. count > nextIndex {
72
- circleColor = bgColors [ nextIndex]
92
+ if bgColors. count > nextIndex. value {
93
+ circleColor = bgColors [ nextIndex. value ]
73
94
}
74
95
let width = CGFloat ( radius * 2 )
75
96
shape = AnyView ( Circle ( ) . foregroundColor ( circleColor) . frame ( width: width, height: width, alignment: . center) )
97
+
98
+ isAnimating. didSet = {
99
+ if self . isAnimating. value && ( self . pages. count < 2 || self . bgColors. count < 2 ) {
100
+ self . isAnimating. value = false
101
+ } else {
102
+ self . isAnimating. value ? self . animationWillBegin ( ) : self . animationDidEnd ( )
103
+ }
104
+ }
105
+
106
+ currentIndex. didSet = {
107
+ if self . currentIndex. value == self . pages. count - 1 {
108
+ self . didGoToLastPage ( )
109
+ }
110
+ }
76
111
}
77
112
113
+
78
114
public var body : some View {
79
115
80
116
let mainView = ZStack {
81
117
bgColor
82
118
83
119
ZStack {
84
120
Button ( action: {
85
- self . isAnimating = true
121
+ if let block = self . didPressNextButton {
122
+ block ( )
123
+ } else {
124
+ self . goToNextPageAnimated ( )
125
+ }
86
126
} ) { shape }
87
127
88
- if !isAnimating {
128
+ if !isAnimating. value {
89
129
Image ( " arrow " )
90
130
. resizable ( )
91
131
. frame ( width: 7 , height: 12 )
@@ -99,10 +139,10 @@ public struct ConcentricOnboardingView : View {
99
139
}
100
140
. edgesIgnoringSafeArea ( . vertical)
101
141
. onReceive ( timer) { _ in
102
- if !self . isAnimating {
142
+ if !self . isAnimating. value {
103
143
return
104
144
}
105
- self . isAnimatingForward ? self . refreshAnimatingViewsForward ( ) : self . refreshAnimatingViewsBackward ( )
145
+ self . isAnimatingForward. value ? self . refreshAnimatingViewsForward ( ) : self . refreshAnimatingViewsBackward ( )
106
146
}
107
147
108
148
return mainView
@@ -140,17 +180,17 @@ public struct ConcentricOnboardingView : View {
140
180
func refreshAnimatingViewsForward( ) {
141
181
progress += step
142
182
if progress < limit {
143
- bgColor = bgColors [ currentIndex]
144
- circleColor = bgColors [ nextIndex]
183
+ bgColor = bgColors [ currentIndex. value ]
184
+ circleColor = bgColors [ nextIndex. value ]
145
185
shape = createGrowingShape ( progress)
146
186
}
147
187
else if progress < 2 * limit {
148
- bgColor = bgColors [ nextIndex]
149
- circleColor = bgColors [ currentIndex]
188
+ bgColor = bgColors [ nextIndex. value ]
189
+ circleColor = bgColors [ currentIndex. value ]
150
190
shape = createShrinkingShape ( progress - limit)
151
191
}
152
192
else {
153
- isAnimating = false
193
+ isAnimating. value = false
154
194
progress = 0
155
195
goToNextPageUnanimated ( )
156
196
}
@@ -160,17 +200,17 @@ public struct ConcentricOnboardingView : View {
160
200
progress += step
161
201
let backwardProgress = 2 * limit - progress
162
202
if progress < limit {
163
- bgColor = bgColors [ currentIndex]
164
- circleColor = bgColors [ nextIndex]
203
+ bgColor = bgColors [ currentIndex. value ]
204
+ circleColor = bgColors [ nextIndex. value ]
165
205
shape = createShrinkingShape ( backwardProgress - limit)
166
206
}
167
207
else if progress < 2 * limit {
168
- bgColor = bgColors [ nextIndex]
169
- circleColor = bgColors [ currentIndex]
208
+ bgColor = bgColors [ nextIndex. value ]
209
+ circleColor = bgColors [ currentIndex. value ]
170
210
shape = createGrowingShape ( backwardProgress)
171
211
}
172
212
else {
173
- isAnimating = false
213
+ isAnimating. value = false
174
214
progress = 0
175
215
goToPrevPageUnanimated ( )
176
216
}
@@ -181,22 +221,22 @@ public struct ConcentricOnboardingView : View {
181
221
let maxYOffset = 40.0
182
222
let currentPageOffset = easingOutProgressFor ( time: progress/ limit/ 2 )
183
223
let nextPageOffset = easingInProgressFor ( time: 1 - progress/ limit/ 2 )
184
- let coeff : CGFloat = isAnimatingForward ? - 1 : 1
224
+ let coeff : CGFloat = isAnimatingForward. value ? - 1 : 1
185
225
186
226
var reverseScaleFactor = 1 - nextPageOffset/ 3
187
227
if reverseScaleFactor == 0 {
188
228
reverseScaleFactor = 1
189
229
}
190
230
191
231
return ZStack {
192
- if pages. count > 0 { pages [ currentIndex]
232
+ if pages. count > 0 { pages [ currentIndex. value ]
193
233
//swap effects order to create another animation
194
234
. scaleEffect ( CGFloat ( 1 - currentPageOffset/ 3 ) )
195
235
. offset ( x: coeff * CGFloat( maxXOffset * currentPageOffset) ,
196
236
y: CGFloat ( maxYOffset * currentPageOffset) )
197
237
}
198
238
199
- if pages. count > 1 { pages [ nextIndex]
239
+ if pages. count > 1 { pages [ nextIndex. value ]
200
240
. scaleEffect ( CGFloat ( reverseScaleFactor) )
201
241
. offset ( x: - coeff * CGFloat( maxXOffset * nextPageOffset) ,
202
242
y: CGFloat ( maxYOffset * nextPageOffset) )
@@ -208,33 +248,33 @@ public struct ConcentricOnboardingView : View {
208
248
let width = CGFloat ( radius * 2 )
209
249
shape = AnyView ( Circle ( ) . foregroundColor ( circleColor) . frame ( width: width, height: width, alignment: . center) )
210
250
211
- bgColor = bgColors [ currentIndex]
212
- circleColor = bgColors [ nextIndex]
251
+ bgColor = bgColors [ currentIndex. value ]
252
+ circleColor = bgColors [ nextIndex. value ]
213
253
}
214
254
215
255
func goToNextPageAnimated( ) {
216
- isAnimatingForward = true
217
- nextIndex = moveIndexForward ( currentIndex)
218
- isAnimating = true
256
+ isAnimatingForward. value = true
257
+ nextIndex. value = moveIndexForward ( currentIndex. value )
258
+ isAnimating. value = true
219
259
}
220
260
221
261
func goToNextPageUnanimated( ) {
222
- isAnimatingForward = true
223
- currentIndex = moveIndexForward ( currentIndex)
224
- nextIndex = moveIndexForward ( currentIndex)
262
+ isAnimatingForward. value = true
263
+ currentIndex. value = moveIndexForward ( currentIndex. value )
264
+ nextIndex. value = moveIndexForward ( currentIndex. value )
225
265
updateColors ( )
226
266
}
227
267
228
268
func goToPrevPageAnimated( ) {
229
- isAnimatingForward = false
230
- nextIndex = moveIndexBackward ( currentIndex)
231
- isAnimating = true
269
+ isAnimatingForward. value = false
270
+ nextIndex. value = moveIndexBackward ( currentIndex. value )
271
+ isAnimating. value = true
232
272
}
233
273
234
274
func goToPrevPageUnanimated( ) {
235
- isAnimatingForward = false
236
- currentIndex = moveIndexBackward ( currentIndex)
237
- nextIndex = moveIndexBackward ( currentIndex)
275
+ isAnimatingForward. value = false
276
+ currentIndex. value = moveIndexBackward ( currentIndex. value )
277
+ nextIndex. value = moveIndexBackward ( currentIndex. value )
238
278
updateColors ( )
239
279
}
240
280
0 commit comments