55 lines
1.4 KiB
Swift
55 lines
1.4 KiB
Swift
import SwiftUI
|
|
|
|
public struct ShimmeringLoader: View {
|
|
@State private var phase: CGFloat = 0
|
|
|
|
public init() {}
|
|
|
|
public var body: some View {
|
|
ZStack {
|
|
Color.systemBackground
|
|
Color.appGraySolid
|
|
.contentShape(Rectangle())
|
|
.modifier(AnimatedMask(phase: phase).animation(
|
|
Animation.linear(duration: 2.0)
|
|
.repeatForever(autoreverses: false)
|
|
))
|
|
.onAppear { phase = 0.8 }
|
|
}
|
|
.frame(height: 2)
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
|
|
/// An animatable modifier to interpolate between `phase` values.
|
|
struct AnimatedMask: AnimatableModifier {
|
|
var phase: CGFloat = 0
|
|
|
|
var animatableData: CGFloat {
|
|
get { phase }
|
|
set { phase = newValue }
|
|
}
|
|
|
|
func body(content: Content) -> some View {
|
|
content
|
|
.mask(GradientMask(phase: phase).scaleEffect(3))
|
|
}
|
|
}
|
|
|
|
/// An animatable gradient between transparent and opaque to use as mask.
|
|
/// The `phase` parameter shifts the gradient, moving the opaque band.
|
|
struct GradientMask: View {
|
|
let phase: CGFloat
|
|
let centerColor = Color.appGraySolid
|
|
let edgeColor = Color.clear
|
|
|
|
var body: some View {
|
|
LinearGradient(gradient:
|
|
Gradient(stops: [
|
|
.init(color: edgeColor, location: phase),
|
|
.init(color: centerColor, location: phase + 0.1),
|
|
.init(color: edgeColor, location: phase + 0.2)
|
|
]), startPoint: .leading, endPoint: .trailing)
|
|
}
|
|
}
|
|
}
|