Skip to content

feat(ios): mirror the side drawer for right-to-left layouts - #57

Open
3adeling wants to merge 1 commit into
NativePHP:mainfrom
3adeling:feat/rtl-drawer
Open

feat(ios): mirror the side drawer for right-to-left layouts#57
3adeling wants to merge 1 commit into
NativePHP:mainfrom
3adeling:feat/rtl-drawer

Conversation

@3adeling

Copy link
Copy Markdown

Summary

Mirrors the iOS side drawer for right-to-left layouts. This is the mobile-ui companion to the opt-in RTL support landed in nativephp/mobile-air (PR #362).

NativeUIDrawerHost hand-rolls its geometry with physical .offset(x:) math and drag-translation signs. SwiftUI's .layoutDirection environment does not mirror those — it only flips logical .leading/.trailing alignment. As a result, when core sets .environment(\.layoutDirection, .rightToLeft):

  • the ☰ affordance flips to the top-right (.topLeading + .padding(.leading) are logical), but
  • the drawer would still slide in from the left, and the edge-swipe would still require a left-edge drag — a visual/gesture mismatch.

Changes

NativeUIDrawerHost.swift now reads @Environment(\.layoutDirection) and inverts every physical direction:

  • Drawer pins to .leading (LTR) / .trailing (RTL) via a derived drawerAlignment.
  • Content slide and the modal drawer slide use an openSign (+1 LTR / -1 RTL).
  • Edge-swipe-to-open and drag-to-close use direction-agnostic translation checks.
  • The edge detector strip is positioned at the leading/trailing edge and its ignoresSafeArea edge follows the direction.
  • settleOpen/settleClose map translation + predicted velocity onto the "open" axis, so threshold and flick-velocity checks are direction-agnostic.

The ☰ button needs no change — its logical .topLeading / .padding(.leading) already flip and now line up with the mirrored drawer.

Compatibility

  • LTR behavior is unchanged (all signs reduce to the previous values when openSign == 1).
  • One intentional correction: the swipe-to-open detector was previously a centered 30pt strip (missing edge alignment), so edge-swipes only registered from the screen center. It is now correctly positioned at the leading edge in LTR (and trailing in RTL), matching the existing "edge detector" comment and intent.

Note on testing

mobile-ui CI runs PHP-only (pint + pest) and has no Swift test target for the renderers, so this change is validated by review rather than an automated native test.

The drawer host hand-rolls its geometry with physical `.offset(x:)` math
and drag-translation signs, which SwiftUI's `.layoutDirection`
environment does not mirror on its own. Read `@Environment(\.layoutDirection)`
and invert the edge, offsets, and gesture thresholds so the drawer opens
from the trailing (right) edge in RTL while LTR behavior is unchanged.

- Drawer pins to leading (LTR) / trailing (RTL) via a derived alignment.
- Content slide and modal-drawer slide use a direction sign (`openSign`).
- Edge-swipe open / drag-close use direction-agnostic translation checks.
- The edge detector is now positioned at the leading/trailing edge and its
  `ignoresSafeArea` edge follows the direction.
- settleOpen/settleClose map translation + velocity onto the "open" axis.

The logical ☰ button (`.topLeading` / `.padding(.leading)`) already flips
automatically, so no change is needed there — it now lines up with the
mirrored drawer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant