Created
April 16, 2025 21:15
-
-
Save Shriram-Vasudevan/e9a20a79ff11da12daa18ecc5560ebf0 to your computer and use it in GitHub Desktop.
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
import SwiftUI | |
struct LightDarkMode: View { | |
@State private var isDarkMode: Bool = false | |
@State private var animationPhase: Double = 0 | |
@State private var isPressed: Bool = false | |
let timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect() | |
var body: some View { | |
ZStack { | |
backgroundColor | |
.ignoresSafeArea() | |
WaveView( | |
isDarkMode: isDarkMode, | |
animationPhase: animationPhase | |
) | |
.ignoresSafeArea() | |
HStack(spacing: 20) { | |
ZStack { | |
Circle() | |
.fill( | |
RadialGradient( | |
gradient: Gradient(colors: [ | |
Color(red: 1.0, green: 0.9, blue: 0.6), | |
Color(red: 0.95, green: 0.8, blue: 0.5) | |
]), | |
center: .center, | |
startRadius: 1, | |
endRadius: 14 | |
) | |
) | |
.frame(width: 28, height: 28) | |
.shadow(color: Color(red: 1.0, green: 0.8, blue: 0.4, opacity: 0.7), radius: 8) | |
.opacity(isDarkMode ? 0 : 1) | |
.scaleEffect(isDarkMode ? 0.01 : 1) | |
ZStack { | |
Circle() | |
.fill( | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color(red: 0.9, green: 0.9, blue: 1.0), | |
Color(red: 0.8, green: 0.8, blue: 0.9) | |
]), | |
startPoint: .topLeading, | |
endPoint: .bottomTrailing | |
) | |
) | |
.frame(width: 26, height: 26) | |
.shadow(color: Color(red: 0.5, green: 0.5, blue: 0.9, opacity: 0.3), radius: 8) | |
Circle() | |
.fill( | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color(red: 0.18, green: 0.18, blue: 0.25), | |
Color(red: 0.12, green: 0.12, blue: 0.18) | |
]), | |
startPoint: .topLeading, | |
endPoint: .bottomTrailing | |
) | |
) | |
.frame(width: 24, height: 24) | |
.offset(x: 4, y: -4) | |
} | |
.opacity(isDarkMode ? 1 : 0) | |
.scaleEffect(isDarkMode ? 1 : 0.01) | |
} | |
Text(isDarkMode ? "Dark Mode" : "Light Mode") | |
.font(.system(size: 17, weight: .medium, design: .rounded)) | |
.foregroundColor(isDarkMode ? | |
Color(red: 0.95, green: 0.95, blue: 1.0) : | |
Color(red: 0.2, green: 0.2, blue: 0.3)) | |
} | |
.padding(.horizontal, 24) | |
.padding(.vertical, 16) | |
.background( | |
Capsule() | |
.fill( | |
isDarkMode ? | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color(red: 0.18, green: 0.18, blue: 0.25), | |
Color(red: 0.12, green: 0.12, blue: 0.20) | |
]), | |
startPoint: .topLeading, | |
endPoint: .bottomTrailing | |
) : | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color.white, | |
Color(red: 0.95, green: 0.95, blue: 0.98) | |
]), | |
startPoint: .topLeading, | |
endPoint: .bottomTrailing | |
) | |
) | |
.shadow( | |
color: isDarkMode ? | |
Color.black.opacity(0.4) : | |
Color.black.opacity(0.12), | |
radius: 20, | |
x: 0, | |
y: 10 | |
) | |
) | |
.overlay( | |
Capsule() | |
.strokeBorder( | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
isDarkMode ? | |
Color.white.opacity(0.2) : | |
Color.white.opacity(0.7), | |
isDarkMode ? | |
Color.white.opacity(0.05) : | |
Color.white.opacity(0.2) | |
]), | |
startPoint: .topLeading, | |
endPoint: .bottomTrailing | |
), | |
lineWidth: 1 | |
) | |
) | |
.scaleEffect(isPressed ? 0.97 : 1) | |
.animation(.spring(response: 0.3, dampingFraction: 0.6), value: isPressed) | |
.onTapGesture { | |
withAnimation(.spring(response: 0.5, dampingFraction: 0.7)) { | |
isDarkMode.toggle() | |
let generator = UIImpactFeedbackGenerator(style: .medium) | |
generator.impactOccurred() | |
isPressed = true | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { | |
isPressed = false | |
} | |
} | |
} | |
} | |
.onReceive(timer) { _ in | |
animationPhase += 0.05 | |
if animationPhase > 100 { | |
animationPhase = 0 | |
} | |
} | |
} | |
var backgroundColor: some View { | |
Group { | |
if isDarkMode { | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color(red: 0.08, green: 0.08, blue: 0.14), | |
Color(red: 0.12, green: 0.12, blue: 0.20) | |
]), | |
startPoint: .top, | |
endPoint: .bottom | |
) | |
} else { | |
LinearGradient( | |
gradient: Gradient(colors: [ | |
Color(red: 0.98, green: 0.98, blue: 1.0), | |
Color(red: 0.95, green: 0.95, blue: 0.98) | |
]), | |
startPoint: .top, | |
endPoint: .bottom | |
) | |
} | |
} | |
.animation(.easeInOut(duration: 0.7), value: isDarkMode) | |
} | |
} | |
struct WaveView: View { | |
let isDarkMode: Bool | |
let animationPhase: Double | |
let waveCount = 4 | |
var body: some View { | |
GeometryReader { geo in | |
ZStack { | |
ForEach(0..<waveCount, id: \.self) { index in | |
VisibleWaveLayer( | |
isDarkMode: isDarkMode, | |
index: index, | |
total: waveCount, | |
phase: animationPhase, | |
size: geo.size | |
) | |
} | |
} | |
} | |
.animation(.easeInOut(duration: 0.5), value: isDarkMode) | |
} | |
} | |
struct VisibleWaveLayer: View { | |
let isDarkMode: Bool | |
let index: Int | |
let total: Int | |
let phase: Double | |
let size: CGSize | |
var body: some View { | |
let layerPhase = phase + Double(index) * 0.3 | |
let amplitude = size.height * 0.08 | |
let waveDensity = 5.0 + Double(index) * 0.8 | |
Path { path in | |
path.move(to: CGPoint(x: 0, y: size.height)) | |
for x in stride(from: 0, to: size.width + 10, by: 4) { | |
let relativeX = Double(x) / Double(size.width) | |
let y1 = sin(relativeX * waveDensity + layerPhase) | |
let y2 = sin(relativeX * (waveDensity / 2) + layerPhase * 1.3) | |
let y = size.height * 0.7 + (y1 * 0.7 + y2 * 0.3) * amplitude | |
path.addLine(to: CGPoint(x: x, y: y)) | |
} | |
path.addLine(to: CGPoint(x: size.width, y: size.height)) | |
path.closeSubpath() | |
} | |
.fill( | |
LinearGradient( | |
gradient: Gradient(colors: waveColors), | |
startPoint: .top, | |
endPoint: .bottom | |
) | |
) | |
.opacity(waveOpacity) | |
.blur(radius: 12) | |
} | |
var waveColors: [Color] { | |
let intensity = Double(index) / Double(total - 1) | |
if isDarkMode { | |
return [ | |
Color(red: 0.4, green: 0.4, blue: 0.9, opacity: 0.2 + intensity * 0.2), | |
Color(red: 0.2, green: 0.3, blue: 0.8, opacity: 0.1 + intensity * 0.2) | |
] | |
} else { | |
return [ | |
Color(red: 0.3, green: 0.6, blue: 1.0, opacity: 0.2 + intensity * 0.15), | |
Color(red: 0.4, green: 0.7, blue: 1.0, opacity: 0.1 + intensity * 0.15) | |
] | |
} | |
} | |
var waveOpacity: Double { | |
let baseOpacity: Double = isDarkMode ? 0.7 : 0.5 | |
return baseOpacity - Double(index) * 0.1 | |
} | |
} | |
#Preview { | |
LightDarkMode() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment