Shimmer.swift (2587B)
1 // 2 // Shimmer.swift 3 // 4 // 5 // Created by Joshua Homann on 2/20/21. 6 // 7 8 import SwiftUI 9 10 public struct ShimmerConfiguration { 11 12 public let gradient: Gradient 13 public let initialLocation: (start: UnitPoint, end: UnitPoint) 14 public let finalLocation: (start: UnitPoint, end: UnitPoint) 15 public let duration: TimeInterval 16 public let opacity: Double 17 public static let `default` = ShimmerConfiguration( 18 gradient: Gradient(stops: [ 19 .init(color: .clear, location: 0), 20 .init(color: .black, location: 0.3), 21 .init(color: .black, location: 0.7), 22 .init(color: .clear, location: 1), 23 ]), 24 initialLocation: (start: UnitPoint(x: -1, y: 0.5), end: .leading), 25 finalLocation: (start: .trailing, end: UnitPoint(x: 2, y: 0.5)), 26 duration: 2, 27 opacity: 0.6 28 ) 29 } 30 31 struct ShimmeringView<Content: View>: View { 32 private let content: () -> Content 33 private let configuration: ShimmerConfiguration 34 @State private var startPoint: UnitPoint 35 @State private var endPoint: UnitPoint 36 init(configuration: ShimmerConfiguration, @ViewBuilder content: @escaping () -> Content) { 37 self.configuration = configuration 38 self.content = content 39 _startPoint = .init(wrappedValue: configuration.initialLocation.start) 40 _endPoint = .init(wrappedValue: configuration.initialLocation.end) 41 } 42 43 var body: some View { 44 ZStack { 45 content() 46 LinearGradient( 47 gradient: configuration.gradient, 48 startPoint: startPoint, 49 endPoint: endPoint 50 ) 51 .opacity(configuration.opacity) 52 .blendMode(.overlay) 53 .onAppear { 54 withAnimation(Animation.linear(duration: configuration.duration).repeatForever(autoreverses: false)) { 55 startPoint = configuration.finalLocation.start 56 endPoint = configuration.finalLocation.end 57 } 58 } 59 } 60 .edgesIgnoringSafeArea(.all) 61 } 62 } 63 64 public struct ShimmerModifier: ViewModifier { 65 let configuration: ShimmerConfiguration 66 public func body(content: Content) -> some View { 67 ShimmeringView(configuration: configuration) { content } 68 } 69 } 70 71 72 public extension View { 73 74 @ViewBuilder func shimmer(configuration: ShimmerConfiguration = .default, _ loading: Bool) -> some View { 75 if loading { 76 modifier(ShimmerModifier(configuration: configuration)) 77 } else { 78 self 79 } 80 } 81 }