Shadow being drawn overtop stroke

I'm trying to have a RoundedRectangle with a slightly different color border (stroke in this case) with a shadow behind it. The issue I'm having is that the shadow itself is being drawn overtop the stroke. I've tried using a ZStack with another RoundedRectangle in the background with a shadow, but I kept running into the same issue.

Anyone have any better ideas?

Section {
            VStack {
                RoundedRectangle(cornerRadius: 11)
                    .fill(color.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)))
                     .stroke(color.opacity(0.5), lineWidth: 5)
            }
            .frame(height: 200)
            .padding()
        }
        .listRowInsets(EdgeInsets()) // Remove padding inside section, but causes clipping on the RoundedRectangle stroke
        .listRowBackground(Color.clear) // Remove background color
Answered by BabyJ in 847426022

When I preview your sample code I get this result (color = .red):

This is the expected behaviour: a red rounded rectangle with a drop shadow and then stroked with a semi-transparent border.

When you say the shadow is being draw over the top of the stroke, this isn't the case. It only appears like that because the stroke is 50% transparent so the shadow is visible through it.


I'm not quite understanding the end result you want either. Do you want:

  1. A filled rounded rectangle with a drop shadow applied to it, and then a stroke border on top.

    RoundedRectangle(cornerRadius: 11)
        .fill(.red.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)))
        .stroke(.blue, lineWidth: 5)
    

  2. A filled rounded rectangle, and a stroke border with a drop shadow on top.

    RoundedRectangle(cornerRadius: 11)
        .fill(.red)
        .stroke(
            .blue.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)),
            lineWidth: 5
        )
    

The reason I'm saying this, and also adding a blue border instead of 50% red, is because I think you are getting confused with what is happening because of the opacity you are adding to the stroke, and the fact that the stroke is half blending in with the rectangle and half overlapping the shadow which is seeping through.


If I were to provide my solution to what I think you are trying to achieve, it would be this:

RoundedRectangle(cornerRadius: 11)
    .fill(color.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)))
    .strokeBorder(color.mix(with: .white, by: 0.5), lineWidth: 5)

I have used strokeBorder here because of the way stroking works. stroke(_:lineWidth:) traces the outline of the shape and draws itself half inside and half outside. This results in the shadow being drawn over. Using strokeBorder(_:lineWidth:) instead draws the border fully inside the shape's bounds. It's effectively the same as regularly stroking the shape that is inset by half the line width.


Of course the colours and constants can be adjusted, but hopefully this explanation has helped and you have a solution to your problem.

When I preview your sample code I get this result (color = .red):

This is the expected behaviour: a red rounded rectangle with a drop shadow and then stroked with a semi-transparent border.

When you say the shadow is being draw over the top of the stroke, this isn't the case. It only appears like that because the stroke is 50% transparent so the shadow is visible through it.


I'm not quite understanding the end result you want either. Do you want:

  1. A filled rounded rectangle with a drop shadow applied to it, and then a stroke border on top.

    RoundedRectangle(cornerRadius: 11)
        .fill(.red.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)))
        .stroke(.blue, lineWidth: 5)
    

  2. A filled rounded rectangle, and a stroke border with a drop shadow on top.

    RoundedRectangle(cornerRadius: 11)
        .fill(.red)
        .stroke(
            .blue.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)),
            lineWidth: 5
        )
    

The reason I'm saying this, and also adding a blue border instead of 50% red, is because I think you are getting confused with what is happening because of the opacity you are adding to the stroke, and the fact that the stroke is half blending in with the rectangle and half overlapping the shadow which is seeping through.


If I were to provide my solution to what I think you are trying to achieve, it would be this:

RoundedRectangle(cornerRadius: 11)
    .fill(color.shadow(.drop(color: .gray, radius: 2, x: 2, y: 2)))
    .strokeBorder(color.mix(with: .white, by: 0.5), lineWidth: 5)

I have used strokeBorder here because of the way stroking works. stroke(_:lineWidth:) traces the outline of the shape and draws itself half inside and half outside. This results in the shadow being drawn over. Using strokeBorder(_:lineWidth:) instead draws the border fully inside the shape's bounds. It's effectively the same as regularly stroking the shape that is inset by half the line width.


Of course the colours and constants can be adjusted, but hopefully this explanation has helped and you have a solution to your problem.

Shadow being drawn overtop stroke
 
 
Q