'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

Inner 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