Closed
Description
import SwiftUI
extension View {
/// Adds a loading skeleton effect by masking the subviews when `condition` is true.
/// If you want to add skeleton to all the subviews in a container (like a Stack),
/// apply skeleton to the Stack itself.
///
/// Apply to the whole view
/// ==================
/// If you need to apply the effect to the whole view instead of only to the subviews,
/// create a conditional overlay color and apply the skeleton to it:
///
/// ```swift
/// view
/// .overlay(Color.gray)
/// .skeleton()
/// ```
@ViewBuilder
func skeleton(condition: Bool = true) -> some View {
if condition {
redacted(reason: .placeholder)
.modifier(Shimmer())
} else {
self
}
}
}
struct Shimmer: ViewModifier {
@State private var isInitialState = true
func body(content: Content) -> some View {
content
.mask(
LinearGradient(
gradient: Gradient(
colors: [
.white.opacity(0.9),
.white.opacity(0.6),
.white.opacity(0.5),
.white.opacity(0.3),
.white.opacity(0),
.white.opacity(0.3),
.white.opacity(0.6),
.white.opacity(0.9),
]
),
startPoint: isInitialState ? .init(x: -1.0, y: 0) : .init(x: 1, y: 1),
endPoint: isInitialState ? .init(x: 0, y: 0) : .init(x: 1.9, y: 1)
)
)
.animation(
.easeIn(duration: 1.25)
.delay(0.25)
.repeatForever(autoreverses: false),
value: isInitialState
)
.onAppear {
// Needed to prevent a random bug in SwiftUI that makes this view
// to move randomly while the animation is on.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
isInitialState = false
}
}
}
}
Metadata
Metadata
Assignees
Labels
No labels