Skip to content

Commit 345c0cd

Browse files
author
Vladyslav Sosiuk
committed
fix: Remove IfElse To Keep State Correct
Refs: #12
1 parent 2c632ed commit 345c0cd

File tree

3 files changed

+98
-8
lines changed

3 files changed

+98
-8
lines changed

Sources/Shimmer/Shimmer.swift

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import SwiftUI
88

99
/// A view modifier that applies an animated "shimmer" to any view, typically to show that an operation is in progress.
1010
public struct Shimmer: ViewModifier {
11+
private let isActive: Bool
1112
private let animation: Animation
1213
private let gradient: Gradient
1314
private let min, max: CGFloat
@@ -21,10 +22,12 @@ public struct Shimmer: ViewModifier {
2122
/// - bandSize: The size of the animated mask's "band". Defaults to 0.3 unit points, which corresponds to
2223
/// 30% of the extent of the gradient.
2324
public init(
25+
isActive: Bool,
2426
animation: Animation = Self.defaultAnimation,
2527
gradient: Gradient = Self.defaultGradient,
2628
bandSize: CGFloat = 0.3
2729
) {
30+
self.isActive = isActive
2831
self.animation = animation
2932
self.gradient = gradient
3033
// Calculate unit point dimensions beyond the gradient's edges by the band size
@@ -82,11 +85,35 @@ public struct Shimmer: ViewModifier {
8285

8386
public func body(content: Content) -> some View {
8487
content
85-
.mask(LinearGradient(gradient: gradient, startPoint: startPoint, endPoint: endPoint))
86-
.animation(animation, value: isInitialState)
88+
.mask(
89+
LinearGradient(
90+
gradient: isActive ? gradient : .transparent,
91+
startPoint: startPoint,
92+
endPoint: endPoint
93+
)
94+
)
95+
.animation(
96+
isActive ? animation : .linear(duration: 0), // FW: This is a correct way to stop animation. If using `isActive ? .linear(...) : .none` instead then the animation would be choppy. https://stackoverflow.com/questions/61830571/whats-causing-swiftui-nested-view-items-jumpy-animation-after-the-initial-drawi/61841018#61841018
97+
value: isInitialState
98+
)
8799
.onAppear {
88-
isInitialState = false
100+
startShimmering()
89101
}
102+
.valueChanged(value: isActive) { isActive in
103+
if isActive {
104+
startShimmering()
105+
} else {
106+
stopShimmering()
107+
}
108+
}
109+
}
110+
111+
private func startShimmering() {
112+
isInitialState = false
113+
}
114+
115+
private func stopShimmering() {
116+
isInitialState = true
90117
}
91118
}
92119

@@ -104,11 +131,9 @@ public extension View {
104131
gradient: Gradient = Shimmer.defaultGradient,
105132
bandSize: CGFloat = 0.3
106133
) -> some View {
107-
if active {
108-
modifier(Shimmer(animation: animation, gradient: gradient, bandSize: bandSize))
109-
} else {
110-
self
111-
}
134+
modifier(
135+
Shimmer(isActive: active, animation: animation, gradient: gradient, bandSize: bandSize)
136+
)
112137
}
113138

114139
/// Adds an animated shimmering effect to any view, typically to show that an operation is in progress.
@@ -130,6 +155,32 @@ public extension View {
130155

131156
#if DEBUG
132157
struct Shimmer_Previews: PreviewProvider {
158+
struct TogglePreview: View {
159+
@State private var isShimmeringActive = true
160+
161+
var body: some View {
162+
Form {
163+
Toggle(isOn: $isShimmeringActive) {
164+
Text("Is shimmering active")
165+
}
166+
ViewWithItsOwnState()
167+
.shimmering(active: isShimmeringActive)
168+
}
169+
}
170+
171+
struct ViewWithItsOwnState: View {
172+
@State private var isOn: Bool = false
173+
174+
var body: some View {
175+
Toggle(isOn: $isOn) {
176+
Text("Should remain the same when toggle shimmering")
177+
}
178+
Text("SwiftUI Shimmer")
179+
.foregroundColor(.red)
180+
}
181+
}
182+
}
183+
133184
static var previews: some View {
134185
Group {
135186
Text("SwiftUI Shimmer")
@@ -155,6 +206,9 @@ struct Shimmer_Previews: PreviewProvider {
155206
.font(.largeTitle)
156207
.shimmering()
157208
.environment(\.layoutDirection, .rightToLeft)
209+
210+
TogglePreview()
211+
.previewDisplayName("Shimmering toggle")
158212
}
159213
}
160214
#endif
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// Gradient+Convenience.swift
3+
//
4+
//
5+
// Created by Vladyslav Sosiuk on 06.11.2023.
6+
//
7+
8+
import SwiftUI
9+
10+
extension Gradient {
11+
static var transparent: Self {
12+
.init(colors: [.black]) // .black has no effect
13+
}
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// View+iOS13Support.swift
3+
//
4+
//
5+
// Created by Vladyslav Sosiuk on 06.11.2023.
6+
//
7+
8+
import SwiftUI
9+
import Combine
10+
11+
extension View {
12+
/// A backwards compatible wrapper for iOS 14 `onChange` based on https://betterprogramming.pub/implementing-swiftui-onchange-support-for-ios13-577f9c086c9
13+
@ViewBuilder func valueChanged<T: Equatable>(value: T, onChange: @escaping (T) -> Void) -> some View {
14+
if #available(iOS 14.0, *) {
15+
self.onChange(of: value, perform: onChange)
16+
} else {
17+
self.onReceive(Just(value)) { (value) in
18+
onChange(value)
19+
}
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)