uitableviewswiftuimaskshadow

Box like shadow in swiftUI


I have added a VStack, I am adding 3 elements like Top , middle and bottom and adding shadow respectively. Problem is I am getting 1px gap between the elements even after adding spacing as 0. Top element will have traling , leading and bottom shadow Middle will have only leading and traling shadow Bottom will have leading, trailing and bottom shadow. I have tried adding offset its not improving

struct ContentView: View {
    var body: some View {
        VStack(spacing: 0) {
            Text("Test")
                .padding()
                .background(
                    Color.white
                        .clipShape(
                            .rect(topLeadingRadius: 12, bottomLeadingRadius: 0, bottomTrailingRadius: 0, topTrailingRadius: 12)
                        )
                )
                .shadow(color: .black.opacity(0.5), radius: 5)
                .addShadow(topPadding: -30, bottomPadding: 0, trailingPadding: -30, leadingPadding: -30)
            
            Text("Test")
                .padding()
                .background(
                    Color.white
                        .clipShape(
                            .rect(topLeadingRadius: -2, bottomLeadingRadius: -2, bottomTrailingRadius: -2, topTrailingRadius: -2)
                        )
                )
                .offset(x: 0, y: -1)
                .shadow(color: .black.opacity(0.5), radius: 5)
                .addShadow(topPadding: 0, bottomPadding: 0, trailingPadding: -30, leadingPadding: -30)

            
            Text("Test")
                .padding()
                .background(
                    Color.white
                        .clipShape(
                            .rect(topLeadingRadius: 0, bottomLeadingRadius: 2, bottomTrailingRadius: 2, topTrailingRadius: 0)
                        )
                )
[enter image description here](https://i.sstatic.net/2dRrCOM6.png)                .shadow(color: .black.opacity(0.5), radius: 5)
                .addShadow(topPadding: 0, bottomPadding: -30, trailingPadding: -30, leadingPadding: -30)

        }
    }
}

struct MaskModifier: ViewModifier {
    let topPadding: CGFloat
    let bottomPadding: CGFloat
    let trailingPadding: CGFloat
    let leadingPadding: CGFloat
    
    init(
        topPadding: CGFloat,
        bottomPadding: CGFloat,
        trailingPadding: CGFloat,
        leadingPadding: CGFloat
    ) {
        self.topPadding = topPadding
        self.bottomPadding = bottomPadding
        self.trailingPadding = trailingPadding
        self.leadingPadding = leadingPadding
    }
    func body(content: Content) -> some View {
        content
        .mask(
            Rectangle()
                .padding(.top, topPadding)
                .padding(.leading, leadingPadding)
                .padding(.trailing, trailingPadding)
                .padding(.bottom, bottomPadding)

        )
    }
}
extension View {
    func addShadow(topPadding: CGFloat,
                     bottomPadding: CGFloat,
                     trailingPadding: CGFloat,
                     leadingPadding: CGFloat) -> some View {
        modifier(MaskModifier(topPadding: topPadding, bottomPadding: bottomPadding, trailingPadding: trailingPadding, leadingPadding: leadingPadding))
    }
}

Solution

  • Try moving the shadow to the background of each view and removing the offset:

    VStack(spacing: 0) {
        Text("Test")
            .padding()
            .background(
                Color.white
                    .clipShape(
                        .rect(topLeadingRadius: 12, bottomLeadingRadius: 0, bottomTrailingRadius: 0, topTrailingRadius: 12)
                    )
                    .shadow(color: .black.opacity(0.5), radius: 5)
                    .addShadow(topPadding: -30, bottomPadding: 0, trailingPadding: -30, leadingPadding: -30)
            )
    
        // ...etc
    }
    

    Screenshot

    You will notice that the shadow drops between each section. This is because, there is less shadow around the corners of each child shape.

    To compensate, you could add negative padding before adding the shadows. The masks that you are already using will take care of clipping the extended shadows. The extra clip shape on the middle section is not needed:

    Text("Test")
        .padding()
        .background {
            Color.white
                .clipShape(
                    .rect(topLeadingRadius: 12, bottomLeadingRadius: 0, bottomTrailingRadius: 0, topTrailingRadius: 12)
                )
                .padding(.bottom, -30)
                .shadow(color: .black.opacity(0.5), radius: 5)
                .addShadow(topPadding: -30, bottomPadding: 0, trailingPadding: -30, leadingPadding: -30)
        }
    
    Text("Test")
        .padding()
        .background {
            Color.white
                .padding(.vertical, -30)
                .shadow(color: .black.opacity(0.5), radius: 5)
                .addShadow(topPadding: 0, bottomPadding: 0, trailingPadding: -30, leadingPadding: -30)
        }
    
    Text("Test")
        .padding()
        .background {
            Color.white
                .clipShape(
                    .rect(topLeadingRadius: 0, bottomLeadingRadius: 2, bottomTrailingRadius: 2, topTrailingRadius: 0)
                )
                .padding(.top, -30)
                .shadow(color: .black.opacity(0.5), radius: 5)
                .addShadow(topPadding: 0, bottomPadding: -30, trailingPadding: -30, leadingPadding: -30)
        }
    

    Screenshot

    However, this would seem to be a rather convoluted way of adding a shadow around a group of items inside a container. A simpler and cleaner approach would be to move the background and its shadow to the container itself (the VStack). Then you don't need to use a mask to clip the different parts of the shadow:

    VStack(spacing: 0) {
        Text("Test")
            .padding()
    
        Text("Test")
            .padding()
    
        Text("Test")
            .padding()
    }
    .background {
        Color.white
            .clipShape(
                .rect(topLeadingRadius: 12, bottomLeadingRadius: 2, bottomTrailingRadius: 2, topTrailingRadius: 12)
            )
            .shadow(color: .black.opacity(0.5), radius: 5)
    }
    

    Screenshot