Instantly share code, notes, and snippets.
Last active
February 1, 2023 18:33
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save AdamWhitcroft/8d41e2c500a499e76540e2db3cc684a6 to your computer and use it in GitHub Desktop.
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
/* | |
Design credit to Nev Flynn | |
https://twitter.com/NevFlynn/status/1620426055155859458 | |
*/ | |
struct NevButton: View { | |
let pageBackground = Color(#colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)) | |
var body: some View { | |
ZStack { | |
pageBackground | |
.edgesIgnoringSafeArea(.all) | |
VStack(spacing: 8) { | |
HStack(spacing: 8) { | |
NevButtonButton( | |
buttonContent: "1", | |
buttonImageSystemName: "circle", | |
buttonImageColorName: .red | |
) | |
NevButtonButton( | |
buttonContent: "2", | |
buttonImageSystemName: "diamond", | |
buttonImageColorName: .blue | |
) | |
} | |
HStack { | |
NevButtonButton( | |
buttonContent: "3", | |
buttonImageSystemName: "square", | |
buttonImageColorName: .yellow | |
) | |
NevButtonButton( | |
buttonContent: "4", | |
buttonImageSystemName: "circle", | |
buttonImageColorName: .black | |
) | |
} | |
} | |
} | |
} | |
} | |
struct NevButtonButton: View { | |
@State private var buttonPressed = false | |
let buttonContent: String | |
let buttonImageSystemName: String | |
let buttonImageColorName: Color | |
var body: some View { | |
Button { | |
buttonPressed.toggle() | |
Haptics.shared.play(.soft) | |
SoundManager.instance.playSound(sound: .click) | |
} label: {} | |
.buttonStyle( | |
NevButtonStyle( | |
buttonPressed: $buttonPressed, | |
buttonContentString: buttonContent, | |
buttonImageSystemName: buttonImageSystemName, | |
buttonImageColorName: buttonImageColorName | |
) | |
) | |
} | |
} | |
struct NevButtonStyle: ButtonStyle { | |
@Binding var buttonPressed: Bool | |
let buttonContentString: String | |
let buttonImageSystemName: String | |
let buttonImageColorName: Color | |
let buttonGradientTopDefault = Color(#colorLiteral(red: 0.8784313725, green: 0.8784313725, blue: 0.8784313725, alpha: 1)) | |
let buttonGradientBottomDefault = Color(#colorLiteral(red: 0.9921568627, green: 0.9921568627, blue: 0.9921568627, alpha: 1)) | |
let buttonRoundShapeGradientTop = Color(#colorLiteral(red: 0.9176470588, green: 0.9176470588, blue: 0.9176470588, alpha: 1)) | |
let buttonRoundShapeGradientBottom = Color(#colorLiteral(red: 0.9333333333, green: 0.9333333333, blue: 0.9333333333, alpha: 1)) | |
let animation: Animation = .interpolatingSpring(mass: 0.2, stiffness: 300, damping: 20) | |
func makeBody(configuration: Configuration) -> some View { | |
ZStack { | |
// indented surround | |
Rectangle() | |
.fill(LinearGradient(colors: [buttonGradientTopDefault, buttonGradientBottomDefault], startPoint: .top, endPoint: .bottom)) | |
.frame(width: 120, height: 120) | |
.clipShape(RoundedRectangle(cornerRadius: 32, style: .continuous)) | |
.shadow(color: .white, radius: 1, x: 0, y: 1) | |
// button base | |
Rectangle() | |
.fill(LinearGradient(colors: [buttonGradientBottomDefault, buttonGradientTopDefault], startPoint: .top, endPoint: .bottom)) | |
.frame(width: 88, height: 88) | |
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) | |
.shadow( | |
color: buttonPressed == true ? .black.opacity(0) : .black.opacity(0.1), | |
radius: buttonPressed == true ? 0 : 8, | |
x: 0, | |
y: buttonPressed == true ? 0 : 4 | |
) | |
.shadow( | |
color: buttonPressed == true ? .black.opacity(0) : .black.opacity(0.125), | |
radius: buttonPressed == true ? 0 : 6, | |
x: 0, | |
y: buttonPressed == true ? 0 : 3 | |
) | |
.shadow( | |
color: buttonPressed == true ? .black.opacity(0) : .black.opacity(0.2), | |
radius: buttonPressed == true ? 0 : 1, | |
x: 0, | |
y: buttonPressed == true ? 0 : 1 | |
) | |
// button inner square | |
.overlay { | |
Rectangle() | |
.fill( | |
LinearGradient( | |
colors: [buttonGradientTopDefault, buttonGradientBottomDefault], | |
startPoint: .top, | |
endPoint: .bottom | |
) | |
) | |
.frame(width: 76, height: 76) | |
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) | |
} | |
.animation(animation, value: buttonPressed) | |
// button outline | |
RoundedRectangle(cornerRadius: 16, style: .continuous) | |
.strokeBorder( | |
LinearGradient( | |
colors: buttonPressed == true ? [.black.opacity(0.15), .black.opacity(0.15)] : [.black.opacity(0.2), .black.opacity(0.25)], | |
startPoint: .top, | |
endPoint: .bottom), | |
lineWidth: 1 | |
) | |
.background(.clear) | |
.frame(width: 88, height: 88) | |
// button inner circle | |
Circle() | |
.fill( | |
LinearGradient( | |
colors: [buttonRoundShapeGradientTop, buttonRoundShapeGradientBottom], | |
startPoint: .top, | |
endPoint: .bottom | |
) | |
) | |
.frame(width: 64, height: 64) | |
// button symbol | |
Image(systemName: buttonPressed == true ? "\(buttonImageSystemName).fill" : buttonImageSystemName) | |
.foregroundColor(buttonPressed == true ? buttonImageColorName : .gray) | |
.shadow(color: .white, radius: 0, x: 0, y: 1) | |
.font(.system(.body, design: .rounded).weight(.bold)) | |
.animation(.easeIn(duration: 0.125), value: buttonPressed) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment