Create a single shadow for multiple shapes in SwiftUI

A user put a question about how to solve replacing multiple shape shadows, with a single uniform shadow for multiple shapes? He posted the initial implementation that caused a shadow to appear between shapes:

shadow issue

A good option is .compositingGroup as documented here but before I put that out, I’d like to leave a different working version because there are some use cases where you may not be able or want to use compositingGroup.

So let’s look at .background, you can copy and paste the code to see in the simulator:

import SwiftUI

struct AnyShapedView: View {
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 20)
                .fill(Color.orange)
                .frame(width: 200, height: 250)
                .zIndex(0)
            Circle()
                .trim(from: 0.5, to: 1)
                .fill(Color.orange)
                .frame(width: 100, height: 100)
                .offset(y: -125)
                .zIndex(1)
        }
    }
}

struct ContentView: View {
    var body: some View {
        AnyShapedView()
        .background(
            AnyShapedView()
            .shadow(color: Color.black, radius: 10, x: 0, y: 0)
        )
    }
}

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

The output:

The example

The solution by using the modifier compositingGroup that makes compositing effects in this view’s ancestor views, like opacity and the blend mode, take effect before this view renders. Follows:

struct CardView: View {
    var body: some View {
        VStack(spacing: 0) {
            Circle()
                .trim(from: 0.5, to: 1)
                .fill(Color.orange)
                .frame(width: 100, height: 100)
                .offset(x: 0, y: 50)
            RoundedRectangle(cornerRadius: 20)
                .fill(Color.orange)
                .frame(width: 200, height: 250)
        }
        .compositingGroup()
        .shadow(color: Color.primary, radius: 10, x: 0, y: 0)
    }
}

The first should allow you to understand how to workout a solution given the background modifier, and the compositingGroup hopefully the quickest way to solve a big percentage of cases you’d encounter.

comments powered by Disqus