Skip to content

Commit 0589e7a

Browse files
committed
Return ShimmerModifier
1 parent a849d21 commit 0589e7a

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ let package = Package(
88
platforms: [
99
.iOS(.v13),
1010
.watchOS(.v7),
11-
.macOS(.v11)
11+
.macOS(.v12)
1212
],
1313
products: [
1414
// Products define the executables and libraries a package produces, and make them visible to other packages.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ func cornerRadius(_ radius: CGFloat, corners: UIRectCorner)
117117
func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void)
118118
func rotateHandling(anchor: UnitPoint = .center)
119119
func rotated(_ angle: Angle)
120+
func shimmer(isActive: Bool = true, speed: Double = 0.15, angle: Angle = .init(degrees: 70), opacity: Double = 1.0)
120121
```
121122

122123
Shapes:
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//
2+
// ShimmerModifier.swift
3+
//
4+
//
5+
// Created by Mykola Fiantsev on 27.05.2023.
6+
//
7+
8+
import SwiftUI
9+
10+
public extension View {
11+
func shimmer(isActive: Bool = true, speed: Double = 0.15, angle: Angle = .init(degrees: 70), opacity: Double = 1.0) -> some View {
12+
return self.modifier(ShimmerModifier(isActive: isActive, speed: speed, angle: angle, opacity: opacity))
13+
}
14+
}
15+
16+
private struct ShimmerModifier: ViewModifier {
17+
let isActive: Bool
18+
let speed: Double
19+
let angle: Angle
20+
let opacity: Double
21+
22+
@ViewBuilder
23+
func body(content: Content) -> some View {
24+
if !isActive { content }
25+
26+
content
27+
.opacity(opacity)
28+
.overlay(ShimmerView(speed: speed, angle: angle).mask(content))
29+
}
30+
}
31+
32+
private struct ShimmerView: View {
33+
let speed: Double
34+
let angle: Angle
35+
@State private var _show = false
36+
37+
var body: some View {
38+
GeometryReader { geo in
39+
Color.white
40+
.mask(Rectangle().fill(_gradient))
41+
.padding(-_calcSize(geo))
42+
.rotationEffect(angle)
43+
.offset(x: _calcOffset(geo), y: 0)
44+
.animation(_animation)
45+
}
46+
.onAppear { _show.toggle() }
47+
}
48+
49+
private var _gradient: LinearGradient {
50+
let leading = Gradient.Stop(color: .clear, location: 0.35)
51+
let center = Gradient.Stop(color: Color.white.opacity(0.7), location: 0.45)
52+
let trailing = Gradient.Stop(color: .clear, location: 0.55)
53+
54+
return LinearGradient(
55+
gradient: .init(stops: [leading, center, trailing]),
56+
startPoint: .top,
57+
endPoint: .bottom
58+
)
59+
}
60+
61+
private var _animation: Animation {
62+
Animation.default
63+
.speed(0.15)
64+
.delay(0)
65+
.repeatForever(autoreverses: false)
66+
}
67+
68+
private func _calcOffset(_ geo: GeometryProxy) -> CGFloat {
69+
let size = _calcSize(geo)
70+
return (_show ? size : -size) * 2
71+
}
72+
73+
private func _calcSize(_ geo: GeometryProxy) -> CGFloat {
74+
return max(geo.size.width, geo.size.height)
75+
}
76+
}

0 commit comments

Comments
 (0)