Last active
December 29, 2019 18:35
-
-
Save mattyoung/c01a597d34e675c94469e81a10cdf9cd to your computer and use it in GitHub Desktop.
iOS Control Center volume widget slash animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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