@@ -8,9 +8,19 @@ import SwiftUI
8
8
9
9
/// A view modifier that applies an animated "shimmer" to any view, typically to show that an operation is in progress.
10
10
public struct Shimmer : ViewModifier {
11
+ public enum Mode {
12
+ /// Masks the content with the gradient (this is the usual, default mode).
13
+ case mask
14
+ /// Overlays the gradient with a given `BlendMode` (`.sourceAtop` by default).
15
+ case overlay( blendMode: BlendMode = . sourceAtop)
16
+ /// Places the gradient behind the content.
17
+ case background
18
+ }
19
+
11
20
private let animation : Animation
12
21
private let gradient : Gradient
13
22
private let min , max : CGFloat
23
+ private let mode : Mode
14
24
@State private var isInitialState = true
15
25
@Environment ( \. layoutDirection) private var layoutDirection
16
26
@@ -23,13 +33,15 @@ public struct Shimmer: ViewModifier {
23
33
public init (
24
34
animation: Animation = Self . defaultAnimation,
25
35
gradient: Gradient = Self . defaultGradient,
26
- bandSize: CGFloat = 0.3
36
+ bandSize: CGFloat = 0.3 ,
37
+ mode: Mode = . mask
27
38
) {
28
39
self . animation = animation
29
40
self . gradient = gradient
30
41
// Calculate unit point dimensions beyond the gradient's edges by the band size
31
42
self . min = 0 - bandSize
32
43
self . max = 1 + bandSize
44
+ self . mode = mode
33
45
}
34
46
35
47
/// The default animation effect.
@@ -65,23 +77,23 @@ public struct Shimmer: ViewModifier {
65
77
/// The start unit point of our gradient, adjusting for layout direction.
66
78
var startPoint : UnitPoint {
67
79
if layoutDirection == . rightToLeft {
68
- return isInitialState ? UnitPoint ( x: max, y: min) : UnitPoint ( x: 0 , y: 1 )
80
+ isInitialState ? UnitPoint ( x: max, y: min) : UnitPoint ( x: 0 , y: 1 )
69
81
} else {
70
- return isInitialState ? UnitPoint ( x: min, y: min) : UnitPoint ( x: 1 , y: 1 )
82
+ isInitialState ? UnitPoint ( x: min, y: min) : UnitPoint ( x: 1 , y: 1 )
71
83
}
72
84
}
73
85
74
86
/// The end unit point of our gradient, adjusting for layout direction.
75
87
var endPoint : UnitPoint {
76
88
if layoutDirection == . rightToLeft {
77
- return isInitialState ? UnitPoint ( x: 1 , y: 0 ) : UnitPoint ( x: min, y: max)
89
+ isInitialState ? UnitPoint ( x: 1 , y: 0 ) : UnitPoint ( x: min, y: max)
78
90
} else {
79
- return isInitialState ? UnitPoint ( x: 0 , y: 0 ) : UnitPoint ( x: max, y: max)
91
+ isInitialState ? UnitPoint ( x: 0 , y: 0 ) : UnitPoint ( x: max, y: max)
80
92
}
81
93
}
82
94
83
95
public func body( content: Content ) -> some View {
84
- content
96
+ applyingGradient ( to : content)
85
97
. mask ( LinearGradient ( gradient: gradient, startPoint: startPoint, endPoint: endPoint) )
86
98
. animation ( animation, value: isInitialState)
87
99
. onAppear {
@@ -92,6 +104,18 @@ public struct Shimmer: ViewModifier {
92
104
}
93
105
}
94
106
}
107
+
108
+ @ViewBuilder public func applyingGradient( to content: Content ) -> some View {
109
+ let gradient = LinearGradient ( gradient: gradient, startPoint: startPoint, endPoint: endPoint)
110
+ switch mode {
111
+ case . mask:
112
+ content. mask ( gradient)
113
+ case let . overlay( blendMode: blendMode) :
114
+ content. overlay ( gradient. blendMode ( blendMode) )
115
+ case . background:
116
+ content. background ( gradient)
117
+ }
118
+ }
95
119
}
96
120
97
121
public extension View {
@@ -106,10 +130,11 @@ public extension View {
106
130
active: Bool = true ,
107
131
animation: Animation = Shimmer . defaultAnimation,
108
132
gradient: Gradient = Shimmer . defaultGradient,
109
- bandSize: CGFloat = 0.3
133
+ bandSize: CGFloat = 0.3 ,
134
+ mode: Shimmer . Mode = . mask
110
135
) -> some View {
111
136
if active {
112
- modifier ( Shimmer ( animation: animation, gradient: gradient, bandSize: bandSize) )
137
+ modifier ( Shimmer ( animation: animation, gradient: gradient, bandSize: bandSize, mode : mode ) )
113
138
} else {
114
139
self
115
140
}
@@ -159,6 +184,14 @@ struct Shimmer_Previews: PreviewProvider {
159
184
. font ( . largeTitle)
160
185
. shimmering ( )
161
186
. environment ( \. layoutDirection, . rightToLeft)
187
+
188
+ Text ( " Custom Gradient Mode " ) . bold ( )
189
+ . font ( . largeTitle)
190
+ . shimmering (
191
+ gradient: Gradient ( colors: [ . clear, . orange, . white, . green, . clear] ) ,
192
+ bandSize: 0.5 ,
193
+ mode: . overlay( )
194
+ )
162
195
}
163
196
}
164
197
#endif
0 commit comments