Shadows clipped by ScrollView

Viewed 1026

Is there a way I can get shadows applied to an image to also show outside of the parent view? Below you can see a sample, where I list out some images in a ScrollView and HStack. The image/artwork has a shadow applied to it, but this gets clipped by the ScrollView.

Screenshot for reference: enter image description here

ScrollView(.horizontal, showsIndicators: false) {
                                        HStack {
                                            ForEach(stationsFeed.items.sorted(by: { $0.updatedAt > $1.updatedAt }).prefix(15)) { item in

                                                NavigationLink(destination: StationDetailView(station: item)) {
                                                    VStack(alignment: .leading) {

                                                        if item.artwork != nil {
                                                            KFImage(self.artworkURLForStation(item)!)
                                                                .resizable()
                                                                .frame(width: 130, height: 130)
                                                                .scaledToFit()
                                                                .cornerRadius(6)
                                                                .shadow(radius: 5)
                                                        } else {
                                                            RoundedRectangle(cornerRadius: 6)
                                                                .background(Color.purple)
                                                                .shadow(radius: 5)
                                                        }

                                                        Text(item.name)
                                                            .font(.callout)
                                                            .foregroundColor(.primary)

                                                        Text(item.categories.first?.name ?? "N/A")
                                                            .font(.callout)
                                                            .foregroundColor(.secondary)
                                                    }
                                                    .frame(minWidth: 0, maxWidth: 130, alignment: .leading)
                                                }
                                                .buttonStyle(PlainButtonStyle())
                                            }
                                        }
                                    }
3 Answers

To make shadow uncut, add padding to HStack

demo

ScrollView(.horizontal, showsIndicators: false) {
   HStack {
      ForEach(stationsFeed.items.sorted(by: { $0.updatedAt > $1.updatedAt }).prefix(15)) { item in
       // ... other code
   }.padding()        // << here !!
}

I've found a decent workaround for this problem.

This is image BEFORE

This is image AFTER

I've added 2 paddings to ScrollView and to content of ScrollView with the same amount of padding but one with NEGATIVE value like this:

struct ScrollViewClippingProblem: View {
var body: some View {
    VStack {
        Text("Lorem Ipsum")
            .font(.title)
        
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(alignment: .top) {
                ForEach(0..<10, content: { item in
                    Text("\(item)")
                        .font(.title)
                        .padding()
                        .background(Circle().fill(.red))
                        .shadow(color: .blue, radius: 10, x: 15, y: 10)
                })
            }
            .padding(.vertical, 40) // <- add padding here to make the space for the shadow
        }
        .padding(.vertical, -40) // <- add NEGATIVE padding here of the same value to shrink the space you created with the padding above
        
        Text("Lorem Ipsum")
            .font(.title)
    }
    .background(.ultraThinMaterial)
}}

I hope this helps somebody! :)

There's a much simpler solution to this, provided that the shadows are uniform for each element within the HStack:

Just add the .shadow modifier to the ScrollView rather than to the element itself.

Using @Hollycene's answer for context.

See SwiftUI Preview Image

struct ScrollViewClippingSolutionView: View {
    var body: some View {
        VStack {
            Divider().padding()
            
            
            Text("Shadow on Element")
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .top) {
                    ForEach(0..<10) { item in
                        Text("\(item)")
                            .font(.title)
                            .padding()
                            .background(Circle().fill(.red))
                            .shadow(color: .blue, radius: 10, x: 15, y: 10) // <- Don't place .shadow modifier here
                    }
                }
            }
            
            
            Divider().padding()
            
            Text("Shadow on ScrollView")
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .top) {
                    ForEach(0..<10) { item in
                        Text("\(item)")
                            .font(.title)
                            .padding()
                            .background(Circle().fill(.red))
                    }
                }
            }
            .shadow(color: .blue, radius: 10, x: 15, y: 10) // <- Place .shadow modifier here instead
            
            Divider().padding()
        }
    }
}
Related