Flip a SwiftUI Shape based on layout direction

Viewed 467

I've made a SemiRoundedRectangle shape which I'm using to clipShape a side menu. I need to flip it if the user is in an RTL language, but not sure the best way of achieving this.

I've tried .flipsForRightToLeftLayoutDirection(true) but this flips the actual Arabic text too. When I try rotating the shape it no longer conforms to the Shape protocol and so I can no longer use it in .clipShape. Everything else in SwiftUI just magically flips itself when I switch into Arabic, is there something I could add to my shape to give it these magic powers too?

Thanks for your help :)


import SwiftUI

struct SemiRoundedRectangle: Shape {
    var cornerRadius: CGFloat
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.minX+cornerRadius, y: rect.maxY))
        path.addArc(center: CGPoint(x: cornerRadius, y: rect.height - cornerRadius),
                    radius: cornerRadius,
                    startAngle: .degrees(90),
                    endAngle: .degrees(180), clockwise: false)
        path.addLine(to: CGPoint(x: 0, y: cornerRadius))
        path.addArc(center: CGPoint(x: cornerRadius, y: cornerRadius),
                    radius: cornerRadius,
                    startAngle: .degrees(180),
                    endAngle: .degrees(270), clockwise: false)
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
        
        return path
    }

}

struct TestView {
    var body: some View {
        HStack {
            Text("ايه الأخبار؟")
            .padding()
            .background(Color.green)
            .clipShape(SemiRoundedRectangle(cornerRadius: 10.0))
            
            Spacer()
        }
        
    }
}
2 Answers

Try attaching the clipShape to the Color.green instead:

struct TestView: View {
    var body: some View {
        HStack {
            Text("ايه الأخبار؟")
            .padding()
            .background(
                Color.green /// here!
                    .clipShape(SemiRoundedRectangle(cornerRadius: 10.0))
                    .flipsForRightToLeftLayoutDirection(true)
            )
            
            Spacer()
        }
    }
}

Result:

English RTL Language
Green rectangle with left corners rounded, at screen left Green rectangle with right corners rounded, at screen right

There is a generic solution:

extension Shape {
    func flipped(_ axis: Axis = .horizontal, anchor: UnitPoint = .center) -> ScaledShape<Self> {
        switch axis {
        case .horizontal:
            return scale(x: -1, y: 1, anchor: anchor)
        case .vertical:
            return scale(x: 1, y: -1, anchor: anchor)
        }
        
    }
}

Use for this case:

struct TestView: View {
    var body: some View {
        HStack {
            Text("ايه الأخبار؟")
                .padding()
                .background(Color.green)
                .clipShape(SemiRoundedRectangle(cornerRadius: 20.0).flipped())
            Spacer()
        }
        .padding()
    }
}
Related