Skip to content

Instantly share code, notes, and snippets.

@kieranb662
Last active July 23, 2024 15:19
Show Gist options
  • Save kieranb662/c254ec73a6121ce1f88f60e4a1f9a082 to your computer and use it in GitHub Desktop.
Save kieranb662/c254ec73a6121ce1f88f60e4a1f9a082 to your computer and use it in GitHub Desktop.
[Pulse ViewModifier] Pulse SwiftUI ViewModifier #SwiftUI #ViewModifier
// Kieran Brown
// Kieran's Components "Pulse and Sheen"
// https://kieranb662.github.io/blog/2020/04/17/Pulse-and-Sheen
struct PulseEffect<S: Shape>: ViewModifier {
var shape: S
@State var isOn: Bool = false
var animation: Animation {
Animation
.easeIn(duration: 1)
.repeatCount(8, autoreverses: false)
}
func body(content: Content) -> some View {
content
.background(
ZStack {
shape
.stroke(Color.yellow, lineWidth: 1)
.scaleEffect(self.isOn ? 2 : 1)
.opacity(self.isOn ? 0 : 1)
shape
.stroke(Color.blue)
})
.onAppear {
withAnimation(self.animation) {
self.isOn = true
}
}
}
}
public extension View {
func pulse<S: Shape>(_ shape: S) -> some View {
self.modifier(PulseEffect(shape: shape))
}
}
@kieranb662
Copy link
Author

@papinko
Copy link

papinko commented Mar 17, 2023

very nice!

@NickAtGit
Copy link

NickAtGit commented Jul 23, 2024

I made this a bit more configurable for me:

struct PulseEffect<S: Shape>: ViewModifier {
    let shape: S
    let color: Color
    let fromScale: CGFloat
    let toScale: CGFloat
    let fromOpacity: CGFloat
    let toOpacity: CGFloat
    @State private var isOn: Bool = false

    var animation: Animation {
        Animation
            .easeIn(duration: 1)
            .repeatForever(autoreverses: false)
    }

    func body(content: Content) -> some View {
        content
            .background(
                shape
                    .stroke(color, lineWidth: 1)
                    .scaleEffect(isOn ? toScale : fromScale)
                    .opacity(isOn ? fromOpacity : toOpacity)
            )
            .onAppear {
                withAnimation(animation) {
                    isOn = true
                }
            }
    }
}

public extension View {
    func pulse<S: Shape>(
        _ shape: S,
        color: Color,
        fromScale: CGFloat = 1,
        toScale: CGFloat = 1.3,
        fromOpacity: CGFloat = 0,
        toOpacity: CGFloat = 0.7
    ) -> some View  {
        self.modifier(
            PulseEffect(
                shape: shape,
                color: color,
                fromScale: fromScale,
                toScale: toScale,
                fromOpacity: fromOpacity,
                toOpacity: toOpacity
            )
        )
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment