// // 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: 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 } } }