Skip to content

Instantly share code, notes, and snippets.

@LidorFadida
Created January 21, 2025 14:36
Show Gist options
  • Select an option

  • Save LidorFadida/4ba7f9e8c8f5ef0707948b932c035b22 to your computer and use it in GitHub Desktop.

Select an option

Save LidorFadida/4ba7f9e8c8f5ef0707948b932c035b22 to your computer and use it in GitHub Desktop.
//
// Neumorphism.swift
// Neumorphism
//
// Created by Lidor Fadida on 21/01/2025.
//
struct NeumorphismPyramidView: View {
@State private var animate: Bool = false
@State private var animationProperties: [(Double, Double)] = Constants.range.map { index in
(0.0, Double(index) * 0.1)
}
private struct Constants {
static let range = (0..<9)
static let animationDuration: TimeInterval = 0.3
}
var body: some View {
GeometryReader { proxy in
ZStack {
neumorphicSteps(proxy: proxy)
}
.frame(width: proxy.size.width, height: proxy.size.height)
}
.contentShape(
Rectangle()
)
.onTapGesture(perform: didTapGeometryReader)
}
private func didTapGeometryReader() {
animate.toggle()
animationProperties = Constants.range.map { index in
let opacity = animate ? 1.0 : 0.0
let value = animate ? 1.0 - Double(index) * 0.1 : Double(index) * 0.1
return (opacity, value)
}
}
private func neumorphicSteps(proxy: GeometryProxy) -> some View {
ForEach(animationProperties.indices, id: \.self) { index in
let index = animate ? index : (animationProperties.count - 1) - index
let (opacity, sizeMultiplier) = animationProperties[index]
let size = CGSize(
width: proxy.size.width * sizeMultiplier,
height: proxy.size.height * sizeMultiplier
)
let delay = CGFloat(index) * 0.3
makeStep(opacity: opacity, size: size)
.animation(
.easeInOut(duration: Constants.animationDuration).delay(delay),
value: animate
)
}
}
@ViewBuilder
private func makeStep(opacity: Double, size: CGSize) -> some View {
let cornerRadius = size.width * 0.1
let fillColor = Color.lightGray
let shadowRadius = 8.0
ZStack {
RoundedRectangle(cornerRadius: cornerRadius)
.fill(fillColor)
.opacity(opacity)
.frame(width: size.width, height: size.height)
.shadow(color: Color.white.opacity(0.8), radius: shadowRadius, x: -shadowRadius, y: -shadowRadius)
.shadow(color: Color.black.opacity(0.2), radius: shadowRadius, x: shadowRadius, y: shadowRadius)
RoundedRectangle(cornerRadius: cornerRadius)
.fill(fillColor)
.frame(width: size.width, height: size.height)
}
}
}
extension Color {
static let lightGray = Color( ///Your favorite color
red: <#T##Double#>,
green: <#T##Double#>,
blue: <#T##Double#>
)
}
#Preview("Neumorphism") {
ZStack {
Color.lightGray ///Use your
.ignoresSafeArea()
NeumorphismPyramidView()
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment