Instantly share code, notes, and snippets.
Last active
October 30, 2024 05:07
-
Star
(6)
6
You must be signed in to star a gist -
Fork
(1)
1
You must be signed in to fork a gist
-
Save waynedahlberg/bb8dc0276657b213c47d98ba42ea3769 to your computer and use it in GitHub Desktop.
Glass SwiftUI Button
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
// | |
// ContentView.swift | |
// Glass Button | |
// | |
// Created by Wayne Dahlberg on 9/25/24. | |
// | |
// IF YOU USE THIS, AND YOU ARE WELCOME TO... CREDIT ME AND REFER TO IT AS 'GLASSWARE' NOT 'GLASSMORPHISM' | |
import SwiftUI | |
struct ContentView: View { | |
var body: some View { | |
ZStack { | |
NoiseBackground() | |
VStack { | |
Spacer() | |
HeaderView() | |
.padding(.bottom, 64) | |
} | |
} | |
.ignoresSafeArea(.all) | |
.persistentSystemOverlays(.hidden) | |
} | |
} | |
struct NoiseBackground: View { | |
var body: some View { | |
ZStack { | |
Color(#colorLiteral(red: 0.9058823529, green: 0.9058823529, blue: 0.9058823529, alpha: 1)) // #e7e7e7 | |
Canvas { context, size in | |
context.addFilter(.alphaThreshold(min: 0.5, color: .black)) | |
context.addFilter(.blur(radius: 8)) | |
context.drawLayer { ctx in | |
for _ in 0..<100 { | |
let x = Double.random(in: 0...size.width) | |
let y = Double.random(in: 0...size.height) | |
let rect = CGRect(x: x, y: y, width: 1, height: 1) | |
ctx.fill(Path(rect), with: .color(.black.opacity(0.2))) | |
} | |
} | |
} | |
.opacity(0.2) | |
} | |
.ignoresSafeArea() | |
} | |
} | |
struct HeaderView: View { | |
@State private var isPressed = false | |
var body: some View { | |
HStack(spacing: 24) { | |
Button(action: { | |
// Login action | |
}) { | |
HStack(spacing: 8) { | |
LoginIcon() | |
Text("Login") | |
.font(.system(size: 14, weight: .semibold)) | |
} | |
} | |
.buttonStyle(LinkButtonStyle()) | |
Button(action: { | |
// Start trial action | |
}) { | |
HStack(spacing: 8) { | |
LockIcon() | |
Text("Upgrade to Pro") | |
.font(.system(size: 14, weight: .semibold)) | |
} | |
.padding() | |
} | |
.buttonStyle(GlassButtonStyle(isPressed: $isPressed)) | |
.shadow(color: Color.black.opacity(isPressed ? 0.15 : 0.5), | |
radius: isPressed ? 0 : 10, | |
x: 0, | |
y: isPressed ? 0 : 20) | |
.animation(.spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0), value: isPressed) | |
} | |
} | |
} | |
struct LinkButtonStyle: ButtonStyle { | |
func makeBody(configuration: Configuration) -> some View { | |
configuration.label | |
.foregroundColor(.black) | |
} | |
} | |
struct GlassButtonStyle: ButtonStyle { | |
@Binding var isPressed: Bool | |
func makeBody(configuration: Configuration) -> some View { | |
configuration.label | |
.padding(.vertical, 8) | |
.padding(.horizontal, 17) | |
.background( | |
ZStack { | |
Color.black.opacity(0.04) | |
RoundedRectangle(cornerRadius: 9999) | |
.strokeBorder( | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color.white.opacity(0.94), | |
Color(#colorLiteral(red: 0.4745098039, green: 0.4745098039, blue: 0.4745098039, alpha: 1)), // #797979 | |
Color(#colorLiteral(red: 0.6431372549, green: 0.6431372549, blue: 0.6431372549, alpha: 1)), // #a4a4a4 | |
Color.white | |
]), | |
startPoint: .top, | |
endPoint: .bottom | |
), | |
lineWidth: 1 | |
) | |
} | |
) | |
.clipShape(RoundedRectangle(cornerRadius: 9999)) | |
.shadow(color: Color.black.opacity(0.1), radius: 78, x: 0, y: 78) | |
.shadow(color: Color.black.opacity(0.07), radius: 50, x: 0, y: 50) | |
.shadow(color: Color.black.opacity(0.06), radius: 30, x: 0, y: 30) | |
.shadow(color: Color.black.opacity(0.04), radius: 16, x: 0, y: 16) | |
.shadow(color: Color.black.opacity(0.04), radius: 6, x: 0, y: 6) | |
.shadow(color: Color.black.opacity(0.02), radius: 2, x: 0, y: 2) | |
.scaleEffect(configuration.isPressed ? 0.9 : 1) | |
.animation(.easeInOut(duration: 0.2), value: configuration.isPressed) | |
.onChange(of: configuration.isPressed) { _, newValue in | |
isPressed = newValue | |
} | |
} | |
} | |
struct LoginIcon: View { | |
var body: some View { | |
Image(systemName: "person.crop.circle") | |
.resizable() | |
.aspectRatio(contentMode: .fit) | |
.frame(width: 18, height: 18) | |
} | |
} | |
struct LockIcon: View { | |
var body: some View { | |
Image(systemName: "lock.open") | |
.resizable() | |
.aspectRatio(contentMode: .fit) | |
.frame(width: 18, height: 18) | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} | |
#Preview { | |
ContentView() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment