'How to make inner shadow in SwiftUI?
How can I use Inner Shadow on a Rectangle()?
Rectangle()
.foregroundColor(.green)
.frame(width: 400, height: 300)
I can only manage to do an Outer Shadow using .shadow
I'm looking for something like this
Solution 1:[1]
For this problem, I built a modifier for the View
protocol and a extension, like below
View+innerShadow.swift
import SwiftUI
extension View {
func innerShadow(color: Color, radius: CGFloat = 0.1) -> some View {
modifier(InnerShadow(color: color, radius: min(max(0, radius), 1)))
}
}
private struct InnerShadow: ViewModifier {
var color: Color = .gray
var radius: CGFloat = 0.1
private var colors: [Color] {
[color.opacity(0.75), color.opacity(0.0), .clear]
}
func body(content: Content) -> some View {
GeometryReader { geo in
content
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .top, endPoint: .bottom)
.frame(height: self.radius * self.minSide(geo)),
alignment: .top)
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .bottom, endPoint: .top)
.frame(height: self.radius * self.minSide(geo)),
alignment: .bottom)
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .leading, endPoint: .trailing)
.frame(width: self.radius * self.minSide(geo)),
alignment: .leading)
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .trailing, endPoint: .leading)
.frame(width: self.radius * self.minSide(geo)),
alignment: .trailing)
}
}
func minSide(_ geo: GeometryProxy) -> CGFloat {
CGFloat(3) * min(geo.size.width, geo.size.height) / 2
}
}
And, for the inner shadow, you just need to add .innerShadow(color:radius)
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
Rectangle()
.foregroundColor(.green)
.frame(width: 400, height: 300)
.innerShadow(color: Color.black.opacity(0.3), radius: 0.05)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Solution 2:[2]
If you want to add shadow only to any specific side.
private struct InnerShadow: ViewModifier {
var color: Color = .gray
var topToBottomRadius = 0.0
var bottomToTopRadius = 0.0
var leadingToTrailingRadius = 0.0
var trailingToLeadingRadius = 0.0
private var colors: [Color] {
[color.opacity(0.75), color.opacity(0.0), .clear]
}
func body(content: Content) -> some View {
GeometryReader { geo in
content
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .top, endPoint: .bottom)
.frame(height: self.topToBottomRadius * self.minSide(geo)),
alignment: .top)
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .bottom, endPoint: .top)
.frame(height: self.bottomToTopRadius * self.minSide(geo)),
alignment: .bottom)
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .leading, endPoint: .trailing)
.frame(width: self.leadingToTrailingRadius * self.minSide(geo)),
alignment: .leading)
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .trailing, endPoint: .leading)
.frame(width: self.trailingToLeadingRadius * self.minSide(geo)),
alignment: .trailing)
}
}
func minSide(_ geo: GeometryProxy) -> CGFloat {
CGFloat(3) * min(geo.size.width, geo.size.height) / 2
}
}
extension View {
func innerShadow(color: Color, topRadius: CGFloat = 0.0, bottomRadius: CGFloat = 0.0, leftRadius: CGFloat = 0.0, rightRadius: CGFloat = 0.0) -> some View {
modifier(InnerShadow(color: color, topToBottomRadius: min(max(0, topRadius), 1), bottomToTopRadius: min(max(0, bottomRadius), 1), leadingToTrailingRadius: min(max(0, leftRadius), 1), trailingToLeadingRadius:min(max(0, rightRadius), 1)))
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Ailton Vieira Pinto Filho |
Solution 2 | Dharman |