Created
March 15, 2025 06:29
-
-
Save ObuchiYuki/d15e799268730f7b49e0a423ae75502b to your computer and use it in GitHub Desktop.
Glowing Glass View
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
// | |
// GlassView.swift | |
// MFileViewer | |
// | |
// Created by yuki on 2025/03/14. | |
// | |
import SwiftUI | |
struct GlassViewMain: View { | |
@State var value = 0.4 | |
@State var isGrowing = false | |
var body: some View { | |
VStack(spacing: 16) { | |
GlassView(thickness: .thin) { | |
VStack(alignment: .leading) { | |
Text("Volume") | |
.font(.system(size: 14)) | |
.fontWeight(.bold) | |
.foregroundColor(.white.opacity(0.8)) | |
HStack(spacing: 16) { | |
Button(action: { | |
withAnimation { | |
self.value -= 0.1 | |
self.value = max(0, self.value) | |
self.isGrowing = true | |
} | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { | |
withAnimation { | |
self.isGrowing = false | |
} | |
} | |
}) { | |
Image(systemName: "speaker.fill") | |
.font(.system(size: 20)) | |
.frame(width: 20, height: 20) | |
} | |
.buttonStyle(GlassButtonStyle()) | |
GeometryReader { proxy in | |
Capsule() | |
.fill(Color.black.opacity(0.1)) | |
.overlay(alignment: .leading) { | |
GlassView(thickness: .regular, cornerRadius: .infinity, isGrowing: self.isGrowing) { | |
Color.clear | |
.frame(width: self.value * proxy.size.width) | |
} | |
} | |
} | |
.frame(maxWidth: .infinity) | |
.frame(height: 20) | |
Button(action: { | |
withAnimation { | |
self.value += 0.1 | |
self.value = min(1, self.value) | |
self.isGrowing = true | |
} | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { | |
withAnimation { | |
self.isGrowing = false | |
} | |
} | |
}) { | |
Image(systemName: "speaker.wave.3.fill") | |
.font(.system(size: 20)) | |
.frame(width: 20, height: 20) | |
} | |
.buttonStyle(GlassButtonStyle()) | |
} | |
} | |
.padding() | |
} | |
} | |
.padding() | |
.background { | |
Image(.background) | |
} | |
} | |
} | |
struct GlassButtonStyle: ButtonStyle { | |
let cornerRadius: CGFloat | |
init(cornerRadius: CGFloat = .infinity) { | |
self.cornerRadius = cornerRadius | |
} | |
func makeBody(configuration: Configuration) -> some View { | |
GlassView( | |
thickness: .regular, | |
cornerRadius: self.cornerRadius, | |
isGrowing: configuration.isPressed | |
) { | |
configuration.label | |
.padding() | |
} | |
.animation(.easeInOut(duration: 0.26), value: configuration.isPressed) | |
} | |
} | |
struct GlassView<Content: View>: View { | |
enum Thickness { | |
case thin | |
case regular | |
case thick | |
} | |
let thickness: Thickness | |
let content: () -> Content | |
let cornerRadius: CGFloat | |
let isGrowing: Bool | |
init( | |
thickness: Thickness = .regular, | |
cornerRadius: CGFloat = 10, | |
isGrowing: Bool = false, | |
@ViewBuilder content: @escaping () -> Content | |
) { | |
self.thickness = thickness | |
self.cornerRadius = cornerRadius | |
self.isGrowing = isGrowing | |
self.content = content | |
} | |
var body: some View { | |
self.content() | |
.background { | |
RoundedRectangle(cornerRadius: self.cornerRadius) | |
.fill( | |
Color.black | |
.shadow(.inner(color: .white, radius: 1/3, x: 1/3, y: 2/3)) | |
.shadow(.inner(color: .white, radius: 1/3, x: -1/3, y: -2/3)) | |
) | |
.blendMode(.overlay) | |
} | |
.background { | |
Color.white | |
.opacity(self.isGrowing ? 1 : self.opacity) | |
} | |
.background(self.material) | |
.clipShape(RoundedRectangle(cornerRadius: self.cornerRadius)) | |
.shadow(color: self.isGrowing ? .white.opacity(0.15) : .black.opacity(0.15), radius: 1, x: -1, y: 0) | |
.shadow(color: self.isGrowing ? .white.opacity(0.15) : .black.opacity(0.15), radius: 1, x: 1, y: 0) | |
.shadow(color: self.isGrowing ? .white.opacity(0.36) : .black.opacity(0.36), radius: 30, x: 0, y: 2) | |
} | |
private var opacity: Double { | |
switch self.thickness { | |
case .thin: return 0.4 | |
case .regular: return 0.6 | |
case .thick: return 0.65 | |
} | |
} | |
private var material: Material { | |
switch self.thickness { | |
case .thin: return .ultraThinMaterial | |
case .regular: return .ultraThinMaterial | |
case .thick: return .regularMaterial | |
} | |
} | |
} | |
#Preview { | |
GlassViewMain() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment