diff --git a/Dayflow/Dayflow/Views/UI/MainView/Layout.swift b/Dayflow/Dayflow/Views/UI/MainView/Layout.swift index e61f5b071..05fc660db 100644 --- a/Dayflow/Dayflow/Views/UI/MainView/Layout.swift +++ b/Dayflow/Dayflow/Views/UI/MainView/Layout.swift @@ -121,9 +121,10 @@ extension MainView { onOpenSettings: { handleTimelineFailureToastOpenSettings(payload) }, onDismiss: { handleTimelineFailureToastDismiss(payload) } ) + .environment(\.layoutDirection, .leftToRight) .padding(.trailing, 24) .padding(.bottom, 24) - .transition(.move(edge: .trailing).combined(with: .opacity)) + .transition(toastOverlayTransition) } } @@ -134,12 +135,23 @@ extension MainView { onOpenSettings: handleScreenRecordingPermissionNoticeOpenSettings, onDismiss: handleScreenRecordingPermissionNoticeDismiss ) + .environment(\.layoutDirection, .leftToRight) .padding(.trailing, 24) .padding(.bottom, 24) - .transition(.move(edge: .trailing).combined(with: .opacity)) + .transition(toastOverlayTransition) } } + private var toastOverlayTransition: AnyTransition { + // Avoid `.move(edge:)` here. On macOS, edge transitions on bottom-trailing + // overlays can briefly reuse a flipped layer snapshot while other overlays + // are animating, which presents the toast text mirrored/upside down. + .asymmetric( + insertion: .opacity.combined(with: .offset(x: 18)), + removal: .opacity.combined(with: .offset(x: 12)) + ) + } + private func performMainLayoutOnAppear() { syncCurrentUIContext() @@ -483,6 +495,7 @@ private struct TimelineFailureToastView: View { RoundedRectangle(cornerRadius: 12) .stroke(Color(hex: "F3D9C2"), lineWidth: 1) ) + .compositingGroup() .shadow(color: Color.black.opacity(0.12), radius: 12, x: 0, y: 6) } } @@ -550,6 +563,7 @@ private struct ScreenRecordingPermissionNoticeView: View { RoundedRectangle(cornerRadius: 12) .stroke(Color(hex: "F3D9C2"), lineWidth: 1) ) + .compositingGroup() .shadow(color: Color.black.opacity(0.12), radius: 12, x: 0, y: 6) } }