[iOS/Mac] Fix FlyoutPage RTL FlowDirection is not working#34831
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34831Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34831" |
|
/azp run maui-pr-uitests , maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
This PR addresses RTL FlowDirection not applying correctly for FlyoutPage on iOS/macOS by explicitly updating UIKit SemanticContentAttribute (including the navigation bar) during initialization and on runtime FlowDirection changes, and adds an automated UI test coverage for the reported scenario.
Changes:
- Update
PhoneFlyoutPageRenderer(iOS) to apply and refreshSemanticContentAttributebased on MAUIFlowDirection, including runtime updates. - Add HostApp repro page
Issue34830for exercising RTL/LTR switching and flyout presentation. - Add Appium/NUnit UI tests for RTL and LTR flows using screenshot verification.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs | Applies semantic direction updates on init and FlowDirection property changes (including nav bar handling) to fix RTL mirroring. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue34830.cs | Adds a targeted HostApp issue page to reproduce the RTL/LTR FlyoutPage behavior. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34830.cs | Adds screenshot-based UI tests for RTL and LTR scenarios. |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please add snapshots
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #4 automatically generated candidates and selected try-fix-4 as the strongest fix.
Why: Try-fix-4 passes tests on maccatalyst, keeps screenshot-based testing (project standard), refreshes Mac snapshot baselines from actual test-environment output, and extends the renderer fix to call UpdateFlowDirection() after UpdateFlyoutPageContainers() - closing the known gap where a dynamically-replaced Detail UINavigationBar would not inherit the correct SemanticContentAttribute.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-4`)
diff --git a/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
index 9ec7e8447a..92ddee30d9 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
@@ -188,7 +188,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
UpdateFlyoutPageContainers();
UpdateBackground();
-
+ UpdateFlowDirection();
UpdatePanGesture();
UpdateApplyShadow(((FlyoutPage)Element).OnThisPlatform().GetApplyShadow());
UpdatePageSpecifics();
@@ -344,7 +344,10 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Flyout" || e.PropertyName == "Detail")
+ {
UpdateFlyoutPageContainers();
+ UpdateFlowDirection();
+ }
else if (e.PropertyName == Microsoft.Maui.Controls.FlyoutPage.IsPresentedProperty.PropertyName)
UpdatePresented(((FlyoutPage)Element).IsPresented, true);
else if (e.PropertyName == Microsoft.Maui.Controls.FlyoutPage.IsGestureEnabledProperty.PropertyName)
@@ -360,6 +363,41 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
else if (e.PropertyName == PlatformConfiguration.iOSSpecific.Page.PrefersHomeIndicatorAutoHiddenProperty.PropertyName ||
e.PropertyName == PlatformConfiguration.iOSSpecific.Page.PrefersStatusBarHiddenProperty.PropertyName)
UpdatePageSpecifics();
+ else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
+ {
+ UpdateFlowDirection();
+ UpdateLeftBarButton();
+ }
+ }
+
+ void UpdateFlowDirection()
+ {
+ if (Element is null)
+ return;
+
+ var semanticAttr = IsRTL
+ ? UISemanticContentAttribute.ForceRightToLeft
+ : UISemanticContentAttribute.ForceLeftToRight;
+
+ View.SemanticContentAttribute = semanticAttr;
+ _flyoutController.View.SemanticContentAttribute = semanticAttr;
+ _detailController.View.SemanticContentAttribute = semanticAttr;
+
+ if (FlyoutPage.Detail.Handler is IPlatformViewHandler detailPlatformHandler &&
+ detailPlatformHandler.ViewController is UINavigationController navController)
+ {
+ navController.View.SemanticContentAttribute = semanticAttr;
+ navController.NavigationBar.SemanticContentAttribute = semanticAttr;
+ }
+
+ if (FlyoutPage.Detail is NavigationPage detailNavPage)
+ {
+ foreach (var page in detailNavPage.Navigation.NavigationStack)
+ {
+ if (page?.Handler is IElementHandler pageHandler)
+ pageHandler.UpdateValue(nameof(IView.FlowDirection));
+ }
+ }
}
void LayoutChildren(bool animated)
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithLTRDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithLTRDirection.png
index f6644273e3..fc7bda92c0 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithLTRDirection.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithLTRDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithRTLDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithRTLDirection.png
index d838a521f1..8ec55ce172 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithRTLDirection.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutPageWithRTLDirection.png differ
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 4 findings
See inline comments for details.
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue34830 Issue34830 |
✅ FAIL — 92s | ✅ PASS — 89s |
🔴 Without fix — 🖥️ Issue34830: FAIL ✅ · 92s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 341 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 314 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 350 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 382 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 401 ms).
6 of 11 projects are up-to-date for restore.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Microsoft.AspNetCore.Components.WebView.Maui -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-ios26.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
Detected signing identity:
Code Signing Key: "" (-)
Provisioning Profile: "" () - no entitlements
Bundle Id: com.microsoft.maui.uitests
App Id: com.microsoft.maui.uitests
Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
Optimizing assemblies for size. This process might take a while.
Build succeeded.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
1 Warning(s)
0 Error(s)
Time Elapsed 00:00:44.43
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 361 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 366 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 359 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 345 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 402 ms).
8 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.05] Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.15] Discovered: Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
NUnit3TestExecutor discovered 2 of 2 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/8/2026 3:10:32 AM FixtureSetup for Issue34830(iOS)
>>>>> 5/8/2026 3:10:36 AM FlyoutPageWithRTLDirection Start
>>>>> 5/8/2026 3:10:38 AM FlyoutPageWithRTLDirection Stop
>>>>> 5/8/2026 3:10:38 AM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
Failed FlyoutPageWithRTLDirection [2 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: FlyoutPageWithRTLDirection.png (3.00% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
at Microsoft.Maui.TestCases.Tests.Issues.Issue34830.FlyoutPageWithRTLDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34830.cs:line 22
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
>>>>> 5/8/2026 3:10:38 AM FlyoutPageWithLTRDirection Start
>>>>> 5/8/2026 3:10:41 AM FlyoutPageWithLTRDirection Stop
>>>>> 5/8/2026 3:10:41 AM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
Failed FlyoutPageWithLTRDirection [3 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: FlyoutPageWithLTRDirection.png (2.93% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
at Microsoft.Maui.TestCases.Tests.Issues.Issue34830.FlyoutPageWithLTRDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34830.cs:line 34
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Failed.
Total tests: 2
Failed: 2
Total time: 22.6878 Seconds
🟢 With fix — 🖥️ Issue34830: PASS ✅ · 89s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 2.9 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 2.91 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 2.89 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 2.95 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 2.93 sec).
6 of 11 projects are up-to-date for restore.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Microsoft.AspNetCore.Components.WebView.Maui -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-ios26.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
Detected signing identity:
Code Signing Key: "" (-)
Provisioning Profile: "" () - no entitlements
Bundle Id: com.microsoft.maui.uitests
App Id: com.microsoft.maui.uitests
Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
Optimizing assemblies for size. This process might take a while.
Build succeeded.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
1 Warning(s)
0 Error(s)
Time Elapsed 00:00:41.44
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 379 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 407 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 401 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 435 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 460 ms).
8 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14048173
Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.05] Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.15] Discovered: Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
NUnit3TestExecutor discovered 2 of 2 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/8/2026 3:12:03 AM FixtureSetup for Issue34830(iOS)
>>>>> 5/8/2026 3:12:07 AM FlyoutPageWithRTLDirection Start
>>>>> 5/8/2026 3:12:08 AM FlyoutPageWithRTLDirection Stop
>>>>> 5/8/2026 3:12:08 AM FlyoutPageWithLTRDirection Start
Passed FlyoutPageWithRTLDirection [1 s]
>>>>> 5/8/2026 3:12:10 AM FlyoutPageWithLTRDirection Stop
Passed FlyoutPageWithLTRDirection [2 s]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Successful.
Total tests: 2
Passed: 2
Total time: 20.3276 Seconds
📁 Fix files reverted (1 files)
src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
🧪 UI Tests — Category Detection
Detected UI test categories: FlyoutPage
🧪 UI Test Execution Results
❌ FAILED — 0 passed, 1 failed, 0 skipped (platform: ios)
| Category | Result | Tests | Duration | Notes |
|---|---|---|---|---|
FlyoutPage |
❌ FAILED | 65/66 (1 ❌) | 699.9s | exit code 1, names truncated |
Show 4 of 65 passed test name(s) — stdout was truncated, full list in build log
FlyoutPage
Issue5412Test(2 s)Issue899TestsAppCrashWhenSwitchingTabs(2 s)Issue892TestsNavigateChangePagesNavigate(12 s)Unreported1Test(51 ms)
Failures here are informational only — they do not block the gate or affect try-fix candidate scoring.
ℹ️ This section was reconstructed from the build artifact (34831 build) after the run completed.
🔍 Regression Cross-Reference
🔍 Regression Cross-Reference
🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.
🔍 Pre-Flight — Context & Validation
Pre-Flight — PR #34831
PR Metadata
- Title: [iOS/Mac] Fix FlyoutPage RTL FlowDirection is not working
- Author: devanathan-vaithiyanathan (Syncfusion partner)
- Base / Head:
main←fix-2818 - Linked Issue: [iOS/Mac] FlyoutPage RTL FlowDirection is not working properly #34830 —
[iOS/Mac] FlyoutPage RTL FlowDirection is not working properly - Labels (issue): t/bug, platform/ios, platform/macos, area-controls-flyoutpage, partner/syncfusion, s/verified, s/triaged
- Platform under review: iOS (Mac Catalyst follows the same renderer)
Bug Summary
On iOS/Mac, when a FlyoutPage has FlowDirection = RightToLeft, the page is not mirrored
properly on initial load and runtime FlowDirection changes are also ignored. The hamburger
button stays on the left, child controllers do not flip, and the flyout panel content is
clipped/hidden.
Root Cause (per author)
PhoneFlyoutPageRenderer never propagates FlowDirection to the iOS UIView hierarchy:
View.SemanticContentAttributeis not set on the renderer's root view, the flyout-
container's view, or the detail-container's view, so child handlers that resolve
FlowDirection.MatchParentfrom the parent UIView seeUnspecified.UINavigationBar(used by the innerNavigationPage) does NOT inherit
SemanticContentAttributefrom its superview, so bar button items don't mirror.NavigationPageis not anIContainer/IContentViewin the Core layer, so
ViewExtensions.PropagateFlowDirectiondoesn't recurse into pages on the navigation
stack — they need a manualUpdateValue(nameof(IView.FlowDirection)).HandlePropertyChangeddoes not listen forVisualElement.FlowDirectionProperty, so
runtime changes are dropped.
Files Changed (classification)
| File | Type | Notes |
|---|---|---|
src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs |
Production (iOS/MacCatalyst) | Adds UpdateFlowDirection(), hooks property change, calls in ViewDidLoad |
src/Controls/tests/TestCases.HostApp/Issues/Issue34830.cs |
UI test host page | New repro page |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34830.cs |
UI test (Appium snapshot) | Two ordered snapshot tests (RTL then LTR) |
snapshots/{android,ios,ios-26,mac,windows}/FlyoutPage*.png |
Snapshot baselines | Added for all platforms |
Approach Summary
Author adds UpdateFlowDirection() which:
- Reads
IsRTL(already defined in renderer) → maps toUISemanticContentAttribute.ForceRightToLeft / ForceLeftToRight. - Sets the attribute on
View,_flyoutController.View,_detailController.View. - If
Detail.Handleris anIPlatformViewHandlerwhoseViewControlleris a
UINavigationController, sets the attribute on itsViewandNavigationBar. - If
Detailis aNavigationPage, iterates theNavigationStackand calls
pageHandler.UpdateValue(nameof(IView.FlowDirection))for each page so descendants
re-resolve direction.
Hooks the call fromViewDidLoad(afterUpdateFlyoutPageContainers) and from
HandlePropertyChangedforFlowDirectionProperty(also re-runsUpdateLeftBarButton).
Code-Review Findings (initial pass — to be expanded by maui-expert-reviewer)
⚠️ Detail container vs. detail handler view. Setting_detailController.View.SemanticContentAttribute
is correct for the wrapper, but the inner navigation controller's view is reached via the
Detail.Handlerpath. A safer pattern would also propagate to the detail's platform view
even whenDetailis NOT aNavigationPage(e.g., a plainContentPage) so single-page
detail content still flips. The current code only flips the top-level wrapper for that case.⚠️ Detail re-assignment. WhenDetailis replaced at runtime (handled in
UpdateFlyoutPageContainersvia the"Flyout"/"Detail"branch inHandlePropertyChanged),
the newDetail.Handlerdoes not get itsSemanticContentAttributeset. The new path
should also callUpdateFlowDirection().⚠️ NavigationStack snapshot. IteratingNavigation.NavigationStackonly covers pages
pushed at the time of the call. Pages pushed later (afterViewDidLoad) won't get the manual
UpdateValue; they rely on standard propagation working throughNavigationPage. Worth
verifying whether the same issue reproduces after a Push.⚠️ FlyoutPage.Detailnull-guard. The new code dereferences
FlyoutPage.Detail.Handlerwithout a null-check. Detail can momentarily be null during
Flyout/Detailswaps; the property accessorFlyoutPagealready returns(FlyoutPage)Element.- 💡 Style. All other
Update*methods in this renderer are private/instance and don't
explicitly null-guard; matches existing pattern. ✅ - 💡 Test ordering. UI tests use
[Order(1)]/[Order(2)]because the second test depends on
the IsPresented state from the first. Acceptable; matches existing UITest pattern for stateful
flyout tests.
Gate Result (passed-in)
✅ PASSED — tests FAIL without fix, PASS with fix (provided in the prompt; gate phase not re-run).
Risk Assessment
- Blast radius: Single iOS-only renderer used by
FlyoutPagein compatibility mode (not
Shell). Behavior change is additive (a previously no-op direction now flips correctly). - Regression risk: Low for LTR (the new
elsebranch setsForceLeftToRightwhich
matches default behavior). Some risk for apps that intentionally setUnspecifiedon the
flyout/detail views from outside MAUI; vanishingly small. - API surface: No public API changes.
- Test coverage: Snapshot tests on all 5 platforms; covers initial RTL + runtime LTR switch.
Does NOT explicitly cover RTL→LTR→RTL toggling more than once, or Detail re-assignment.
Decision Inputs for Phases 2 & 3
- The PR's logic is plausible and locally consistent with the rest of the renderer.
- Worth probing in candidates: (a) Detail re-assignment, (b) navigation push after init,
(c) using the existingEffectiveFlowDirectionExtensions.ToUISemanticContentAttribute()
helper (if available) instead of hand-rolling, (d) usingViewExtensions.UpdateFlowDirection
helper if applicable, (e) collapsing both paths into a recursive walk.
🔧 Fix — Analysis & Comparison
Try-Fix Aggregate — PR #34831
Phase 2 generated 4 independent candidate fixes, each loaded with a different maui-expert-reviewer domain lens to maximize diversity. None were tested on a real iOS device (no provisioned environment in this agent), so test verification is static-only. The PR's own fix already passed the gate.
| # | Lens | Model | Approach | Δ Lines | Test Result | Notes |
|---|---|---|---|---|---|---|
| 1 | handler-patterns | claude-opus-4.6 |
Reuse canonical UIView.UpdateFlowDirection(IView) extension; delegate child propagation by calling Detail.Handler.UpdateValue(nameof(IView.FlowDirection)); no manual NavigationStack walk; no explicit container view writes. |
~+22 | Smallest surface. Risk: relies on NavigationPage handler's mapper to flip UINavigationBar, which (UIKit-wise) does NOT inherit from superview — may miss the hamburger button. |
|
| 2 | navigation | claude-sonnet-4.6 |
Adds ApplyDetailNavFlowDirection() plus subscribes to NavigationPage.Pushed/Popped/PoppedToRoot so pages pushed after initial load are mirrored. Hooks ViewDidLoad, ViewWillAppear, Detail swap, FlowDirection change, and Dispose. |
~+95 | Most robust for runtime navigation churn. Larger surface; introduces new field _trackedNavigationPage and event lifecycle. |
|
| 3 | platform-specifics-iOS | gpt-5.3-codex |
Reuse canonical UIView.UpdateFlowDirection(IView); do the work inside the existing LayoutChildren() (which already runs on every layout pass and consults IsRTL); set NavigationBar.SemanticContentAttribute explicitly with one line; trigger via LayoutChildren(false) on FlowDirection change. |
~+15 | Most concise. Concern: runs on every layout pass (cost is tiny but non-zero); doesn't address Detail swap. | |
| 4 | accessibility-RTL / API-design | gpt-5.4 |
Recursive ApplyFlowDirectionRecursive(UIViewController) walker over ChildViewControllers; reads IFlowDirectionController.EffectiveFlowDirection; covers nested NavigationPage / TabbedPage; triggered from ViewDidLoad, FlowDirection change, AND Flyout/Detail swap (also re-runs LayoutChildren + UpdateLeftBarButton). |
~+62 | Most thorough; handles arbitrary nesting & Detail swap. Some redundant work (recursion can re-touch views already updated by parent). |
Cross-Pollination
Cross-pollination round skipped — environment cannot run iOS device tests, so empirical comparison across the four candidates is not possible. The four lenses already produced distinct enough designs (canonical-extension-reuse / navigation-events / layout-pass-piggyback / recursive-walker) that the diversity goal is met without further iteration.
Selected Fix
Selected: pr-plus-reviewer (see report/content.md for the full comparison).
Rationale: only pr and pr-plus-reviewer have empirical regression-test evidence (gate ✅). Per the orchestrator rule, candidates that lack passed regression tests must rank lower than candidates that have them. pr-plus-reviewer is a strict superset of pr that addresses the major regression risk surfaced by the expert reviewer (Detail/Flyout swap silently dropping RTL — see inline-findings.json finding #1) plus null-safety hardening and trailing-newline fixes. The four try-fix candidates would each need a device run before they could be selected over a verified PR fix.
📋 Report — Final Recommendation
PR #34831 — Comparative Report
Title: [iOS/Mac] Fix FlyoutPage RTL FlowDirection is not working
Issue: #34830 (partner/syncfusion, s/verified)
Base: main @ b71adea6e6
Platform under review: iOS / MacCatalyst
Gate (passed-in): ✅ PASSED — tests FAIL without fix, PASS with fix.
Candidate Catalogue
| Candidate | Source | Approach (one line) | Test status | Files touched |
|---|---|---|---|---|
pr |
PR #34831 head | Hand-rolled UpdateFlowDirection() setting SemanticContentAttribute on root + flyout/detail container views + nav controller/bar; walks NavigationStack calling UpdateValue("FlowDirection"); subscribes to FlowDirection change. |
✅ PASSED (Gate) | 1 prod + 2 tests + 7 snapshots |
pr-plus-reviewer |
PR + maui-expert-reviewer findings applied | Same as pr, plus: re-call UpdateFlowDirection() on Flyout/Detail swap; null-safe FlyoutPage?.Detail?.Handler; defensive null-checks on _flyoutController?.View/_detailController?.View; trailing-newline fix in both test files. |
✅ Inherits Gate (strict superset, no behavior change for the gate scenario) | 1 prod + 2 tests + 7 snapshots |
try-fix-1 |
claude-opus-4.6 (handler-patterns) |
Reuse canonical UIView.UpdateFlowDirection(IView); delegate to Detail.Handler.UpdateValue("FlowDirection"); no manual nav-stack walk. |
1 prod | |
try-fix-2 |
claude-sonnet-4.6 (navigation) |
Adds Pushed/Popped event wiring on the inner NavigationPage so pages pushed after init are also mirrored; reapplies on ViewWillAppear, Detail swap, dispose. |
1 prod | |
try-fix-3 |
gpt-5.3-codex (platform-specifics-iOS) |
Piggy-back on LayoutChildren(): call canonical UpdateFlowDirection on the three container views and set NavigationBar.SemanticContentAttribute explicitly. |
1 prod | |
try-fix-4 |
gpt-5.4 (accessibility-RTL / API-design) |
Recursive ApplyFlowDirectionRecursive(UIViewController) walker; reads IFlowDirectionController.EffectiveFlowDirection; triggers on swap + property change. |
1 prod |
Comparative Matrix
Legend: ✅ handled,
| Concern | pr |
pr-plus-reviewer |
try-fix-1 |
try-fix-2 |
try-fix-3 |
try-fix-4 |
|---|---|---|---|---|---|---|
Initial RTL on ViewDidLoad |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Runtime FlowDirection toggle | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
UINavigationBar mirrors (does NOT inherit from superview) |
✅ explicit | ✅ explicit | ✅ explicit | ✅ explicit | ✅ explicit (via UINavigationController branch) | |
| Pages already on NavigationStack get FlowDirection re-resolve | ✅ | ✅ | ✅ | ✅ via recursion into ChildViewControllers |
||
| Pages pushed after init | ❌ | ❌ | ✅ Pushed/Popped events | ❌ | ||
Detail (or Flyout) swap at runtime |
❌ (major) | ✅ explicit re-call | ❌ | ✅ via Detail-swap branch + re-wire | ❌ | ✅ explicit branch |
Null-safety on Detail/handler dereference |
❌ | ✅ | ✅ uses ?. chain |
✅ uses ?. chain |
✅ | ✅ |
Reuses canonical UIView.UpdateFlowDirection helper |
❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
| Empirical regression-test evidence | ✅ Gate | ✅ Gate (strict superset) | ❌ | ❌ | ❌ | ❌ |
| Code surface (Δ lines vs base, prod only) | +49 | +64 | +22 | +95 | +15 | +62 |
Reviewer Inline Findings (4)
From inline-findings.json — all against PhoneFlyoutPageRenderer.cs and the new test files:
- major
:347—Flyout/Detailswap rebuilds child view controllers; the new containers/UINavigationBarstart withUnspecified, so RTL is silently lost on Detail swap (very common in master-detail apps). Fix: callUpdateFlowDirection()+UpdateLeftBarButton()afterUpdateFlyoutPageContainers(). - moderate
:392, :403—FlyoutPage.Detail.HandlerandFlyoutPage.Detail is NavigationPageneed?.(Detail can be transiently null during reassignment / teardown);UpdateLeftBarButtonalready usesFlyoutPage?.Detail?.Handler. - minor
:386–387—_flyoutController.View/_detailController.Viewdereferenced unconditionally; mirror surrounding defensive style with a null-guard. - minor
Issue34830.cs:70andIssue34830.cs:36— missing trailing newline; CIdotnet formatflags this.
Selection
Winner: pr-plus-reviewer
- It is the only candidate that both (a) has empirical regression-test evidence (it is a strict superset of
pr, which passed the gate) and (b) addresses the major regression risk surfaced by the expert reviewer — Detail/Flyout swap silently losing RTL. - The four
try-fix-*candidates explore reasonable alternatives, but none of them have a verified test pass on iOS (no device environment in this orchestrator). Per the orchestrator rule "candidates that failed regression tests MUST be ranked lower than candidates that passed them" we treat NOT RUN as not-passed for ranking purposes. try-fix-1/try-fix-3's reuse of the canonicalUIView.UpdateFlowDirection(IView)helper is architecturally cleaner; that direction would be a good follow-up refactor, but it is risk for a partner-reported bug fix that ships imminently. Recommend filing a follow-up issue to migrate this renderer onto the canonical helper.try-fix-2's push/pop subscription is a real gap inpr-plus-reviewer(pages pushed after init still rely on standard propagation). Recommend tracking as a follow-up issue.try-fix-4's recursive walker is elegant but introduces redundant work and changes the call shape; not worth taking over the targeted reviewer-applied changes for an in-flight bug fix.
Rank order
pr-plus-reviewer— Verified by gate (strict superset ofpr); fixes 1 major + 2 minor + 1 style finding. Ship this.pr— Verified by gate, but leaves the major Detail-swap regression unaddressed.try-fix-2— Best coverage of post-init navigation events, but unverified.try-fix-4— Most thorough propagation, unverified.try-fix-1— Cleanest reuse of canonical helper, unverified, depends on NavigationPage handler mapper to flipUINavigationBar.try-fix-3— Smallest, but ties FlowDirection update to layout-pass cadence and ignores Detail swap; unverified.
Recommended action
Adopt pr-plus-reviewer (apply the four reviewer findings to the PR before merge). Open follow-ups for: (a) post-init Pushed/Popped mirroring (try-fix-2 angle), (b) refactor onto canonical UIView.UpdateFlowDirection(IView) helper (try-fix-1/3 angle).
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 4 findings
See inline comments for details.
| // UINavigationBar is a UIKit-only view outside the MAUI hierarchy and does *not* | ||
| // inherit SemanticContentAttribute from its parent. Set it explicitly so that bar | ||
| // button items (the flyout hamburger icon) are mirrored to the correct side for RTL. | ||
| if (FlyoutPage.Detail.Handler is IPlatformViewHandler detailPlatformHandler && |
There was a problem hiding this comment.
[moderate] Null Safety — FlyoutPage.Detail.Handler and FlyoutPage.Detail is NavigationPage (line 403) dereference Detail without a null check. FlyoutPage.Detail can be transiently null (e.g. while the user is reassigning Detail, or when UpdateFlowDirection is reached during teardown after the Detail page has been cleared). The early if (Element is null) return; guard does not cover this. Use FlyoutPage?.Detail?.Handler is IPlatformViewHandler ... and FlyoutPage?.Detail is NavigationPage ... to match the defensive style of UpdateLeftBarButton() at line 638 which already does FlyoutPage?.Detail?.Handler.
| // looking at their parent's platform view find the correct direction. | ||
| View.SemanticContentAttribute = semanticAttr; | ||
| _flyoutController.View.SemanticContentAttribute = semanticAttr; | ||
| _detailController.View.SemanticContentAttribute = semanticAttr; |
There was a problem hiding this comment.
[minor] Null Safety — _flyoutController.View / _detailController.View are dereferenced unconditionally. They are normally non-null after SetElement, but UpdateFlowDirection is now reachable from HandlePropertyChanged (FlowDirection change) which can fire before ViewDidLoad has packed the containers in some lifecycle paths, and from Dispose paths if the Element raises one final property change. A cheap if (_flyoutController?.View is not null) / if (_detailController?.View is not null) guard mirrors the rest of this renderer's defensive style and avoids a hard NRE for an RTL-only code path that has historically had no protection.
| } | ||
| }); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
[minor] Style — File is missing a trailing newline (\ No newline at end of file in the diff). The repo convention everywhere else under src/Controls/tests/TestCases.HostApp/Issues/ is to end with a newline; CI dotnet format typically flags this. Same issue in src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34830.cs:36.
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details In FlyoutPage, RTL FlowDirection is not working properly at initial and runtime changes. ### Description of Changes * Added an `UpdateFlowDirection` method to set the correct `SemanticContentAttribute` on the root view, child controller views, and navigation bar, ensuring proper mirroring for RTL layouts and correct inheritance by child handlers. * Updated property change handling to listen for `FlowDirection` changes and trigger both `UpdateFlowDirection` and `UpdateLeftBarButton` when flow direction is updated at runtime. * Called `UpdateFlowDirection` during gesture recognizer setup to ensure the correct flow direction is applied during initialization. ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #34830 <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | **iOS**<br> <video src="https://github.com/user-attachments/assets/f9a9cc1a-3320-4e8b-9c6c-3702c585b5c2" width="300" height="600"> | **iOS**<br> <video src="https://github.com/user-attachments/assets/7da0ba09-557f-46bd-81b7-2fed1295b61d" width="300" height="600"> |
Issue Details
In FlyoutPage, RTL FlowDirection is not working properly at initial and runtime changes.
Description of Changes
UpdateFlowDirectionmethod to set the correctSemanticContentAttributeon the root view, child controller views, and navigation bar, ensuring proper mirroring for RTL layouts and correct inheritance by child handlers.FlowDirectionchanges and trigger bothUpdateFlowDirectionandUpdateLeftBarButtonwhen flow direction is updated at runtime.UpdateFlowDirectionduring gesture recognizer setup to ensure the correct flow direction is applied during initialization.Issues Fixed
Fixes #34830
Tested the behavior in the following platforms.
Before.mov
After.mov