Skip to content

Instantly share code, notes, and snippets.

@mattyoung
Last active December 29, 2019 18:35
Show Gist options
  • Save mattyoung/c01a597d34e675c94469e81a10cdf9cd to your computer and use it in GitHub Desktop.
Save mattyoung/c01a597d34e675c94469e81a10cdf9cd to your computer and use it in GitHub Desktop.
iOS Control Center volume widget slash animation
import SwiftUI
struct AnimatedSlashSpeakerDemo: View {
// 0 mute is off, 1 mute is on
@State private var muteState: CGFloat = 0
private static let imageDimension: CGFloat = 250
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [.pink, .orange, .blue, .red, .green, .purple, .yellow]), startPoint: .topLeading, endPoint: .bottomTrailing)
VStack {
Spacer()
AnimatedSlashSpeaker(muteState: self.$muteState)
.frame(width: Self.imageDimension, height: Self.imageDimension)
.foregroundColor(.orange)
.animation(Animation.linear(duration: 1))
Spacer()
Button(muteState == 0 ? "Mute" : "Un-mute") {
self.muteState = 1 - self.muteState
}
.padding()
}
}
.edgesIgnoringSafeArea(.all)
}
}
extension CGFloat {
static let squareRootOf2 = CGFloat((2.0).squareRoot())
}
private struct AnimatedSlashSpeaker: View {
@Binding var muteState: CGFloat
var body: some View {
ZStack {
Image(systemName: "speaker.1.fill")
.resizable()
.clipShape(SlashShape(animatableData: self.muteState, asClipShape: true), style: FillStyle(eoFill: true, antialiased: true))
SlashShape(animatableData: self.muteState)
}
}
}
/// Diagonal capsule style slash inside a square CGRect
/// if asClipShape is true, a bounding box is added for use as a clip shape
private struct SlashShape: Shape {
var animatableData: CGFloat
var asClipShape = false
func path(in rect: CGRect) -> Path {
let thickness: CGFloat = 3 * rect.maxX / (asClipShape ? 20 : 40)
let padding: CGFloat = asClipShape ? thickness / 4 : 0
var path = Path()
path.addRoundedRect(
in: CGRect(x: -padding, y: -thickness / 2, width: animatableData * CGFloat.squareRootOf2 * rect.width + 2 * padding, height: thickness),
cornerSize: CGSize(width: thickness / 2, height: thickness / 2),
style: .circular,
transform: CGAffineTransform(rotationAngle: 45 * CGFloat.pi / 180)
)
if asClipShape {
path.addRect(rect)
}
return path
}
}
struct AnimatedSlashSpeakerDemo_Previews: PreviewProvider {
static var previews: some View {
AnimatedSlashSpeakerDemo()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment