Why does the blur modifier affect views differently?

Viewed 33

I spent a lot of time trying to figure out whether this was a bug, but I couldn't for the life of me figure out why a shape wasn't blurring correctly.

VStack {
    //Doesn't blur right
    Capsule()
        .fill(LinearGradient(colors: [Color.red, Color.blue], startPoint: .leading, endPoint: .trailing))
        .frame(width: 80, height: 40)
        .blur(radius: 10)
    //Does blur correctly
    HStack {
    }
    .frame(width: 80, height: 40)
    .background(
        LinearGradient(colors: [Color.red, Color.blue], startPoint: .leading, endPoint: .trailing)
    )
    .clipShape(Capsule())
    .blur(radius: 10)
}

enter image description here

When you apply blur to an empty HStack, it blurs correctly, while using a blur to a shape doesn't. Can someone explain why it differs?

1 Answers

Firstly, two of them looks like the same but it's not the same.

From Apple docs, Capsule() is subclass of Shape which is subclass of View.

The first one, when you using Capsule() directly then you .fill means just like you add subview LinearGradient with colors into Capsule(). That's the reason I think .blur not working correctly when you call directly from Capsule().

The second one, you .blur correctly because the view was directly be drawn on view ( just like drawing in layer of view in normal UIKit)

The view hierarchy show the different between two of them. Example

The solution:
You can do like your second view

HStack {
}
.frame(width: 80, height: 40)
.background(
    LinearGradient(colors: [Color.red, Color.blue], startPoint: .leading, endPoint: .trailing)
)
.clipShape(Capsule())
.blur(radius: 10)

Or you can make linear gradient with mask

HStack {
    LinearGradient(gradient: Gradient(colors: [.red, .blue]), startPoint: .leading, endPoint: .trailing)
        .mask(
            Capsule()
            .blur(radius: 10)
            .frame(width: 80, height: 40)
        )
}.frame(width: 200, height: 60)

Both will act the same like you draw on the layer of view The result

UPDATE

If you need it works directly, just use only one color then call .foregroundColor then blur will continue work - But only one color at the time only.

Capsule()
    .foregroundColor(Color.red)
    .blur(radius: 10)
    .frame(width: 80, height: 40)
Related