Flip a SwiftUI Shape based on layout direction
Asked Answered
H

3

7

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()
        }
        
    }
}
Halloo answered 25/4, 2021 at 16:43 Comment(0)
A
3

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
Adest answered 25/4, 2021 at 16:52 Comment(0)
S
1

iOS 17+

Starting with Xcode 15, Shape has a layoutDirectionBehavior property that allows it to support flipping the shape horizontally automatically.

If deploying to iOS17 (& equivalents), this property defaults to .mirrors – so the shape will flip automatically. If deploying to earlier, it defaults to .fixed (for compatibility, I'm guessing).

https://developer.apple.com/documentation/swiftui/shape/layoutdirectionbehavior-361s6

Steersman answered 30/7, 2023 at 8:48 Comment(0)
S
0

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()
    }
}
Sum answered 25/5, 2022 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.