简体   繁体   中英

How to apply inner shadow on stroke of a shape in SwiftUI?

I would like to apply an inner shadow on the stroke of a shape. I have found an function that handles the inner shadow (can't use iOS 16 yet), but I'm having a hard time trying to apply this function to the stroke itself.

Here's what I got:

struct ContentView: View {
    var body: some View {
        VStack {
            PolygonShape(sides: 7)
                .stroke(Color.white, lineWidth: 10)
            PolygonShape(sides: 7)
                .innerShadow(color: .red, radius: 10)
        }
        .padding(32)
        .background(Color.black.ignoresSafeArea())
    }
}

extension Shape {
    func innerShadow(color: Color, radius: Double) -> some View {
        overlay(
            stroke(color, lineWidth: radius)
                .blur(radius: radius)
                .mask(self)
        )
    }
}

struct PolygonShape: Shape {
    let sides: Int

    func path(in rect: CGRect) -> Path {
        let c = CGPoint(x: rect.width/2.0, y: rect.height/2.0)
        let r = Double(min(rect.width,rect.height)) / 2.0
        var vertices:[CGPoint] = []
        for i in 0...sides {
            let angle = (2.0 * Double.pi * Double(i)/Double(sides))
            let pt = CGPoint(x: r * cos(angle), y: r * sin(angle))
            vertices.append(CGPoint(x: pt.x + c.x, y: pt.y + c.y))
        }
        var path = Path()
        for (n, pt) in vertices.enumerated() {
            n == 0 ? path.move(to: pt) : path.addLine(to: pt)
        }
        path.closeSubpath()
        return path
    }
}

struct StackedElementsView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

This is what it looks like but not what I want:

在此处输入图像描述

What I would like to do is put the inner red shadow just on the white stroke itself, so the stroke has red shadow in it only not in the shape.

Something similar to this:

在此处输入图像描述

I tried applying the inner shadow on the color itself but since it is not a shape it did not compile: .stroke(Color.white.innerShadow(color: .red, radius: 2)

How can I apply the inner shadow on a stroke color?

This isn't the cleanest or most complete answer, but it's simple.

The following code, in your var body ...

ZStack {
                
    PolygonShape(sides: 7)
        .stroke(Color.white, lineWidth: 10)
                
    PolygonShape(sides: 7)
        .stroke(Color.black, lineWidth: 10)
        .scaleEffect(1.07)
        .shadow(color: .red, radius: 3)
                
    PolygonShape(sides: 7)
        .scaleEffect(0.965)
        .shadow(color: .red, radius: 3)
}

...produces this type of border:

There's a red line around the shape, though, which may or may not be an easy fix, depending on your situation. Just throwing this as a temporary answer :) Alternatively, if you can get the shape to be just the border (with no fill), you could use .mask and mask it to a gradient of your choice.

Although not perfect and probably will not handle many cases, an alternate way is drawing the stroke, then another stroke half the size within in it that's blurred:

extension Shape {
    func stroke<Stroke: ShapeStyle>(
        _ content: Stroke,
        lineWidth: Double = 1,
        innerShadow: (color: Color, radius: Double)
    ) -> some View {
        stroke(content, lineWidth: lineWidth / 2)
            .blur(radius: innerShadow.radius)
            .background(stroke(innerShadow.color, lineWidth: lineWidth))
    }
}
PolygonShape(sides: 7)
    .stroke(Color.white, lineWidth: 10, innerShadow: (Color.red, 2))

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM