Skip to content

Instantly share code, notes, and snippets.

@maximkrouk
Last active June 24, 2021 12:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maximkrouk/e2f0294d670d80457db42d0106cd8075 to your computer and use it in GitHub Desktop.
Save maximkrouk/e2f0294d670d80457db42d0106cd8075 to your computer and use it in GitHub Desktop.
Neomorphic view from shape for SwiftUI
import SwiftUI
public struct ShadowConfiguration {
public var color: Color
public var radius: CGFloat
public var offset: CGPoint
public init(color: Color, radius: CGFloat, offset: CGPoint) {
self.color = color
self.radius = radius
self.offset = offset
}
public static var defaultNeomorphic: (top: Self, bottom: Self) {
(
ShadowConfiguration(color: Color.black.opacity(0.2),
radius: 5,
offset: .init(x: 5, y: 5)),
ShadowConfiguration(color: Color.white.opacity(0.8),
radius: 10,
offset: .init(x: -5, y: -5))
)
}
}
public struct NeomorphicShapeConfiguration<Fill: ShapeStyle> {
public var fill: Fill
public var topShadow: ShadowConfiguration
public var bottomShadow: ShadowConfiguration
public init(
fill: Fill,
topShadow: ShadowConfiguration = ShadowConfiguration.defaultNeomorphic.top,
bottomShadow: ShadowConfiguration = ShadowConfiguration.defaultNeomorphic.bottom
) {
self.fill = fill
self.topShadow = topShadow
self.bottomShadow = bottomShadow
}
}
extension NeomorphicShapeConfiguration where Fill == Color {
public init() { self.init(fill: Color(.systemGray6)) }
public static var `default`: Self { .init() }
}
struct _NeomorphicShape<Content: Shape, Fill: ShapeStyle>: View {
let content: Content
let configuration: NeomorphicShapeConfiguration<Fill>
init(
_ content: Content,
configuration: NeomorphicShapeConfiguration<Fill>
) {
self.content = content
self.configuration = configuration
}
var body: some View {
content.fill(configuration.fill)
.shadow(color: configuration.bottomShadow.color,
radius: configuration.bottomShadow.radius,
x: configuration.bottomShadow.offset.x,
y: configuration.bottomShadow.offset.y)
.shadow(color: configuration.topShadow.color,
radius: configuration.topShadow.radius,
x: configuration.topShadow.offset.x,
y: configuration.topShadow.offset.y)
}
}
extension Shape {
public func neomorphic() -> some View {
_NeomorphicShape(self, configuration: .default)
}
public func neomorphic<Fill: ShapeStyle>(configuration: NeomorphicShapeConfiguration<Fill>) -> some View {
_NeomorphicShape(self, configuration: configuration)
}
public func neomorphic<Fill: ShapeStyle>(
_ fill: Fill,
topShadow: ShadowConfiguration = ShadowConfiguration.defaultNeomorphic.top,
bottomShadow: ShadowConfiguration = ShadowConfiguration.defaultNeomorphic.bottom
) -> some View {
_NeomorphicShape(self, configuration: .init(fill: fill, topShadow: topShadow, bottomShadow: bottomShadow))
}
}
@maximkrouk
Copy link
Author

maximkrouk commented Mar 7, 2020

Usage example

// Swift playgrounds
struct ContentView: View {
    var body: some View {
        ZStack {
            VStack {
                Button(action: {}) { 
                    Text("Tap")
                        .foregroundColor(.init(.systemGray))
                        .padding(30)
                }.background(
                    RoundedRectangle(cornerRadius: 20)
                        .neomorphic(Color(.systemGray5))
                )
            }
        }   
        .frame(width: 414, height: 896)
        .background(Color(.systemGray5))
    }
}

Screen Shot 2020-03-08 at 02 00 37

Back to index

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment