Created
December 24, 2019 13:13
-
-
Save MrNickBarker/21eb5d1eb2fafeac69b04fe6b9c7d93d to your computer and use it in GitHub Desktop.
UISwitch (or Toggle) recreated using SwiftUI with a more playful 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
struct MyToggle: View { | |
@Binding var isOn: Bool | |
private enum SwitchState { | |
case notPressed | |
case pressed(percent: CGFloat) | |
} | |
@State private var state: SwitchState = .notPressed | |
@State private var startPercent: CGFloat? | |
@State private var pressed = false | |
private var percent: CGFloat { | |
if case let .pressed(percent) = state { | |
return percent | |
} | |
return isOn ? 1 : 0 | |
} | |
var body: some View { | |
GeometryReader { geo in | |
HStack { | |
Circle() | |
.padding(2) | |
.frame(width: geo.size.height, height: geo.size.height) | |
.foregroundColor(.white) | |
.shadow(radius: 2) | |
.scaleEffect(self.pressed ? 0.8 : 1) | |
.alignmentGuide(HorizontalAlignment.leading, computeValue: { d in | |
d[.leading] - ((geo.size.width - d.width) * self.percent) | |
}) | |
} | |
.frame(maxWidth: .infinity, alignment: .leading) | |
.background(self.percent > 0.5 ? Color.green : Color(white: 0.9)) | |
.clipShape(RoundedRectangle(cornerRadius: geo.size.width / 2)) | |
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local).onChanged { change in | |
if self.startPercent == nil { self.startPercent = self.percent } | |
let value = change.translation.width / (geo.size.width - geo.size.height) | |
self.state = .pressed(percent: min(max((self.startPercent ?? 0) + value, 0), 1)) | |
withAnimation(Animation.easeIn(duration: 0.05)) { | |
self.pressed = true | |
} | |
}.onEnded { _ in | |
if abs(self.percent.distance(to: self.startPercent ?? 0)) < 0.1 { | |
self.isOn.toggle() | |
} | |
else { | |
self.isOn = self.percent > 0.5 | |
} | |
withAnimation(Animation.easeOut(duration: 0.1)) { | |
self.startPercent = nil | |
self.state = .notPressed | |
} | |
withAnimation(Animation.spring(response: 0.12, dampingFraction: 0.23, blendDuration: 0)) { | |
self.pressed = false | |
} | |
}) | |
} | |
.frame(width: 50, height: 30) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment