82 lines
2.5 KiB
Swift
82 lines
2.5 KiB
Swift
//
|
|
// Shimmer.swift
|
|
//
|
|
//
|
|
// Created by Joshua Homann on 2/20/21.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
public struct ShimmerConfiguration {
|
|
|
|
public let gradient: Gradient
|
|
public let initialLocation: (start: UnitPoint, end: UnitPoint)
|
|
public let finalLocation: (start: UnitPoint, end: UnitPoint)
|
|
public let duration: TimeInterval
|
|
public let opacity: Double
|
|
public static let `default` = ShimmerConfiguration(
|
|
gradient: Gradient(stops: [
|
|
.init(color: .clear, location: 0),
|
|
.init(color: .black, location: 0.3),
|
|
.init(color: .black, location: 0.7),
|
|
.init(color: .clear, location: 1),
|
|
]),
|
|
initialLocation: (start: UnitPoint(x: -1, y: 0.5), end: .leading),
|
|
finalLocation: (start: .trailing, end: UnitPoint(x: 2, y: 0.5)),
|
|
duration: 2,
|
|
opacity: 0.6
|
|
)
|
|
}
|
|
|
|
struct ShimmeringView<Content: View>: View {
|
|
private let content: () -> Content
|
|
private let configuration: ShimmerConfiguration
|
|
@State private var startPoint: UnitPoint
|
|
@State private var endPoint: UnitPoint
|
|
init(configuration: ShimmerConfiguration, @ViewBuilder content: @escaping () -> Content) {
|
|
self.configuration = configuration
|
|
self.content = content
|
|
_startPoint = .init(wrappedValue: configuration.initialLocation.start)
|
|
_endPoint = .init(wrappedValue: configuration.initialLocation.end)
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
content()
|
|
LinearGradient(
|
|
gradient: configuration.gradient,
|
|
startPoint: startPoint,
|
|
endPoint: endPoint
|
|
)
|
|
.opacity(configuration.opacity)
|
|
.blendMode(.overlay)
|
|
.onAppear {
|
|
withAnimation(Animation.linear(duration: configuration.duration).repeatForever(autoreverses: false)) {
|
|
startPoint = configuration.finalLocation.start
|
|
endPoint = configuration.finalLocation.end
|
|
}
|
|
}
|
|
}
|
|
.edgesIgnoringSafeArea(.all)
|
|
}
|
|
}
|
|
|
|
public struct ShimmerModifier: ViewModifier {
|
|
let configuration: ShimmerConfiguration
|
|
public func body(content: Content) -> some View {
|
|
ShimmeringView(configuration: configuration) { content }
|
|
}
|
|
}
|
|
|
|
|
|
public extension View {
|
|
|
|
@ViewBuilder func shimmer(configuration: ShimmerConfiguration = .default, _ loading: Bool) -> some View {
|
|
if loading {
|
|
modifier(ShimmerModifier(configuration: configuration))
|
|
} else {
|
|
self
|
|
}
|
|
}
|
|
}
|