Instantly share code, notes, and snippets.
Created
September 26, 2024 17:21
-
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 Petezah/9d762af6fb58097a7becd09ad476ccc5 to your computer and use it in GitHub Desktop.
Glass SwiftUI Button
This file contains hidden or 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. | |
// | |
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