SwiftUI Mask a rectangle inside a rounded rectangle
Asked Answered
O

4

10

Card

Hello there. I am wondering, in SwiftUI, how do you mask the contents of a rounded rectangle so that a child rectangle clips the corners.

In my example I have a white rounded rectangle and a pink rectangle on a zstack, I've tried to apply clipping, but the pink rectangle does not conform to the corners.

I've tried applying .mask to the white rectangle, but it gives different results to expectations (sometimes it doesn't show the pink rectangle).

I did find an example where you can set your own cornerRadius Round Specific Corners SwiftUI

But I was wondering if perhaps there was a way to mask the internals/body of the pink rectangle so that it conforms to the parent's rounded rectangle?

My code follows;

var body: some View {
        GeometryReader { geometry in

            Color.gray
                .edgesIgnoringSafeArea(.top)
                .overlay(

                    ZStack (alignment: .topLeading) {

                        RoundedRectangle(cornerRadius: 16,
                                         style: .continuous)
                            .foregroundColor(.white)
                            .shadow(radius: 10)
                             // Tried using .mask here 

                        Rectangle()
                            .fill(Color.pink)
                            .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 150, alignment: .top)
                            .clipped()


                    }
                    .frame(width: 300, height: 450, alignment: .center)
            )

        }
        .edgesIgnoringSafeArea(.all)
    }

Edit: To clarify:

The pink rectangle should remain as a rectangle, but clip the top left and right to match the parent white rounded rectangle.

Onaonager answered 27/5, 2020 at 10:3 Comment(0)
P
20

If I correctly understood your goal, here is a solution - the only needed clip in right place is after internal content (two rectangles in this case) is constructed. So clipping with RoundedRectangle gives rounded corners around entire card. (As well as shadow most probably is needed to entire card, so placed at the end).

UPDATE: re-tested with Xcode 13.3 / iOS 15.4

demo

ZStack (alignment: .topLeading) {
    Rectangle()
        .foregroundColor(.white)

    Rectangle()
        .fill(Color.pink)
        .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 150, alignment: .top)
}
.clipShape(RoundedRectangle(cornerRadius: 16))       // << here !!
.frame(width: 300, height: 450, alignment: .center)
.shadow(radius: 10)
Pitchford answered 27/5, 2020 at 10:26 Comment(2)
Is it possible that you only define the 16 once? It seems a bit silly to define it twice for both clipShape and the RoundRectangle? If not, its fine.Onaonager
@zardon, magic numbers is always evil, here for is ok, in real project it's preferable to use named constants.Pitchford
T
2

@Asperi already posted a great answer, I have done this aswell with using mask modifier in SwiftUI. Furthermore you only have to set cornerRadius once.

VStack(spacing: 0)
{
    ZStack(alignment: .center)
    {
       Rectangle()
       .fill(Color.red)
       .frame(width: 66, height: 20)

    }


    ZStack(alignment: .center)
    {
       Rectangle()
       .fill(Color.white)
       .frame(width: 66, height: 46)

    }
}
.mask(Rectangle()
        .cornerRadius(3.0)
        .frame(width: 66, height: 66)
)

enter image description here

Teodorateodorico answered 27/5, 2020 at 20:36 Comment(0)
D
0

I think the easier way is to apply cornerradius to ZStack

ZStack (alignment: .topLeading) {

    RoundedRectangle(cornerRadius: 16,
                 style: .continuous)
        .foregroundColor(.white)
        .shadow(radius: 10)
         // Tried using .mask here

    Rectangle()
       .fill(Color.pink)
       .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 150, alignment: .top)
        //.clipped() //<<= here
}
.frame(width: 300, height: 450, alignment: .center)
.cornerRadius(20) //<<= here
Deadandalive answered 22/10, 2021 at 11:30 Comment(0)
F
-1

enter image description herecheck this out

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in

            Color.gray
                .edgesIgnoringSafeArea(.top)
                .overlay(

                    ZStack (alignment: .topLeading) {

                        RoundedRectangle(cornerRadius: 16,
                                         style: .continuous)
                            .foregroundColor(.white)
                            .shadow(radius: 10)
                        // Tried using .mask here

                        Rectangle()
                            .fill(Color.pink)
                            .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 150, alignment: .top)
                            .clipShape(RoundedRectangle(cornerRadius: 16)) // <<<<<<


                    }
                    .frame(width: 300, height: 450, alignment: .center)
            )

        }
        .edgesIgnoringSafeArea(.all)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Florafloral answered 27/5, 2020 at 10:27 Comment(1)
The pink rectangle should remain as a rectangle, but clip the top left and right to match the parent white rounded rectangle. I'll update the question to reflect thisOnaonager

© 2022 - 2025 — McMap. All rights reserved.