Skip to content

Commit

Permalink
fix: Remove IfElse To Keep State Correct
Browse files Browse the repository at this point in the history
Refs: markiv#12
  • Loading branch information
Vladyslav Sosiuk committed Nov 7, 2023
1 parent 2c632ed commit 95ae26b
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 8 deletions.
72 changes: 64 additions & 8 deletions Sources/Shimmer/Shimmer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SwiftUI

/// A view modifier that applies an animated "shimmer" to any view, typically to show that an operation is in progress.
public struct Shimmer: ViewModifier {
private let isActive: Bool
private let animation: Animation
private let gradient: Gradient
private let min, max: CGFloat
Expand All @@ -21,10 +22,12 @@ public struct Shimmer: ViewModifier {
/// - bandSize: The size of the animated mask's "band". Defaults to 0.3 unit points, which corresponds to
/// 30% of the extent of the gradient.
public init(
isActive: Bool,
animation: Animation = Self.defaultAnimation,
gradient: Gradient = Self.defaultGradient,
bandSize: CGFloat = 0.3
) {
self.isActive = isActive
self.animation = animation
self.gradient = gradient
// Calculate unit point dimensions beyond the gradient's edges by the band size
Expand Down Expand Up @@ -82,11 +85,37 @@ public struct Shimmer: ViewModifier {

public func body(content: Content) -> some View {
content
.mask(LinearGradient(gradient: gradient, startPoint: startPoint, endPoint: endPoint))
.animation(animation, value: isInitialState)
.mask(
LinearGradient(
gradient: isActive ? gradient : .transparent,
startPoint: startPoint,
endPoint: endPoint
)
)
.animation(
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
value: isInitialState
)
.onAppear {
isInitialState = false
if isActive {
startShimmering()
}
}
.valueChanged(value: isActive) { isActive in
if isActive {
startShimmering()
} else {
stopShimmering()
}
}
}

private func startShimmering() {
isInitialState = false
}

private func stopShimmering() {
isInitialState = true
}
}

Expand All @@ -104,11 +133,9 @@ public extension View {
gradient: Gradient = Shimmer.defaultGradient,
bandSize: CGFloat = 0.3
) -> some View {
if active {
modifier(Shimmer(animation: animation, gradient: gradient, bandSize: bandSize))
} else {
self
}
modifier(
Shimmer(isActive: active, animation: animation, gradient: gradient, bandSize: bandSize)
)
}

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

#if DEBUG
struct Shimmer_Previews: PreviewProvider {
struct TogglePreview: View {
@State private var isShimmeringActive = true

var body: some View {
Form {
Toggle(isOn: $isShimmeringActive) {
Text("Is shimmering active")
}
ViewWithItsOwnState()
.shimmering(active: isShimmeringActive)
}
}

struct ViewWithItsOwnState: View {
@State private var isOn: Bool = false

var body: some View {
Toggle(isOn: $isOn) {
Text("Should remain the same when toggle shimmering")
}
Text("SwiftUI Shimmer")
.foregroundColor(.red)
}
}
}

static var previews: some View {
Group {
Text("SwiftUI Shimmer")
Expand All @@ -155,6 +208,9 @@ struct Shimmer_Previews: PreviewProvider {
.font(.largeTitle)
.shimmering()
.environment(\.layoutDirection, .rightToLeft)

TogglePreview()
.previewDisplayName("Shimmering toggle")
}
}
#endif
14 changes: 14 additions & 0 deletions Sources/Shimmer/Utility/Gradient+Convenience.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Gradient+Convenience.swift
//
//
// Created by Vladyslav Sosiuk on 06.11.2023.
//

import SwiftUI

extension Gradient {
static var transparent: Self {
.init(colors: [.black]) // .black has no effect
}
}
22 changes: 22 additions & 0 deletions Sources/Shimmer/Utility/View+iOS13Support.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// View+iOS13Support.swift
//
//
// Created by Vladyslav Sosiuk on 06.11.2023.
//

import SwiftUI
import Combine

extension View {
/// A backwards compatible wrapper for iOS 14 `onChange` based on https://betterprogramming.pub/implementing-swiftui-onchange-support-for-ios13-577f9c086c9
@ViewBuilder func valueChanged<T: Equatable>(value: T, onChange: @escaping (T) -> Void) -> some View {
if #available(iOS 14.0, *) {
self.onChange(of: value, perform: onChange)
} else {
self.onReceive(Just(value)) { (value) in
onChange(value)
}
}
}
}

0 comments on commit 95ae26b

Please sign in to comment.