10

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())
                                            }
                                        }
                                    }
infero
  • 300
  • 2
  • 11
  • You should set some sort of content inset / padding for you parent view so that there is space left to draw the shadow. – Tritonal Jun 02 '20 at 17:04

3 Answers3

10

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 !!
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 2
    Thanks, that pointed me at the right direction. However, I needed to additional add ``.padding(.horizontal, -20).padding(.vertical, -10)`` to the ScrollView to get it to look right. Is there a better way than hardcoding the padding? – infero Jun 02 '20 at 18:24
  • 5
    This is not a solution, just a workaround for the issue - this won't cover up the issue if shadow radius >= default SwiftUI padding value. – Przemyslaw Jablonski Sep 12 '20 at 12:12
5

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! :)

Hollycene
  • 287
  • 1
  • 2
  • 12
  • Surprised this isn't the correct answer. While the selected answer works it creates problems if the shadow is larger than padding that can afford to be added. This way, the view stays the same size but the shadow isn 't clipped at all. – Josh Feb 04 '23 at 19:49
1

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()
        }
    }
}