TestinginProd
TestinginProd

Reputation: 1166

How to prevent section header from inheriting shadow of List view?

I have a SwiftUI List where each row has a shadow under it, to add a 3D look to it. While the rows look great, the section header text seems to inherit the shadow from the list, and nothing I have tried can remove it.

Screenshot of the effect based on code below:

Screenshot of the shadow effect

What I have tried so far:

I'm at a total loss on what to do.

How do I make the Section text not inherit the shadow formatting on the List view?

Minimal reproducible example:

struct MinReproduceExample: View {
  var body: some View {
    List {
        Section(header: MinReproCell(text: "Section Title")) {
            MinReproCell(text: "Cell text")
            //causes shadow on cell text
            //does not create red shadow on cell view itself
                .shadow(color:.red, radius: 5, x:-3, y:3)

        }
    }
    .background(.clear)
    //Must be here to make cell view shadow red.
    //Also causes Section title to be red
    //does not cause cell text to have red shadow
    .shadow(color:.red, radius: 5, x:-3, y:3)
    //scrollContent required to be clear to show shadows of views
    .scrollContentBackground(.hidden)
  }
}

struct MinReproCell: View {
  var text: String
  var body: some View {
      Text(text).foregroundStyle(.black)
  }
}

struct MinReproSection:View {
  var text: String
  var body: some View {
      Text(text)
        .foregroundStyle(.black)
        .font(Font.system(size: 20, weight: .bold))
        .textCase(.none) // has no effect on section text shadow
  }
}

Upvotes: 0

Views: 55

Answers (1)

Benzy Neez
Benzy Neez

Reputation: 21710

If the .shadow is applied to the List as a whole then it is going to apply to all the content inside the list. This includes the section header.

One workaround would be to apply the shadow effect to the list rows instead. This is demonstrated in the answer to How to use dual shadows on a list? (it was my answer). However, this technique is a bit cumbersome.

A simpler workaround is to hide the native section header and show your own header as an overlay in the same space:

  • .matchedGeometryEffect can be used to match the position exactly. I found it works best if anchor: .bottom is used.
  • Alternatively, the overlay could be applied with alignment: .topLeading and padding used to adjust its position.
  • To mimic the styling, set the font to .subheadline.smallCaps().
  • If you really want to match the styling precisely, you may need to tweak the kerning too.
struct MinReproduceExample: View {
    @Namespace private var ns

    var body: some View {
        List {
            Section {
                MinReproCell(text: "Row one")
                MinReproCell(text: "Row two")
                    .shadow(color:.red, radius: 5, x:-3, y:3)

            } header: {
                MinReproCell(text: "Section Title")
                    .hidden()
                    .matchedGeometryEffect(id: "title", in: ns, anchor: .bottom)
            }
        }
        .background(.clear)
        .scrollContentBackground(.hidden)
        .shadow(color:.red, radius: 5, x:-3, y:3)
        .overlay {
            MinReproCell(text: "Section Title")
                .font(.subheadline.smallCaps())
                .kerning(0.2)
                .matchedGeometryEffect(
                    id: "title",
                    in: ns,
                    properties: .position,
                    anchor: .bottom,
                    isSource: false
                )
        }
    }
}

Screenshot

Upvotes: 0

Related Questions