[iOS, Mac] Fix OnBackButtonPressed not invoked for NavigationPage and Shell#35072
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35072Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35072" |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Fixes cases where tapping the UI back button (navigation bar/toolbar) bypasses MAUI’s back-press pipeline, so OnBackButtonPressed overrides were not invoked for Shell (Android/iOS/Mac) and NavigationPage (iOS/Mac). Adds corresponding HostApp repro pages and Appium UI tests.
Changes:
- iOS Shell: route navigation-bar back taps through
Shell.SendBackButtonPressed()and fix_sendPopPendingreset on iOS 26+ when back is canceled. - Android Shell: route toolbar back taps through
Shell.SendBackButtonPressed()instead of going straight to the currentPage. - Add new HostApp issue pages + Appium UI tests for issues #8296 and #9095.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs | Ensures Shell back-button interception runs for nav-bar back, and prevents iOS 26+ _sendPopPending from getting stuck when back is canceled. |
| src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs | Routes toolbar back through Shell’s back pipeline before popping navigation. |
| src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs | Attempts to intercept iOS native back taps and invoke MAUI back-press handling before allowing the pop. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue9095.cs | Adds Shell-based HostApp repro for toolbar back vs OnBackButtonPressed (true/false paths). |
| src/Controls/tests/TestCases.HostApp/Issues/Issue8296.cs | Adds NavigationPage-based HostApp repro for iOS/macOS native back button calling OnBackButtonPressed (true/false paths). |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9095.cs | Adds UI test coverage for Shell toolbar back behavior. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8296.cs | Adds UI test coverage for NavigationPage native back behavior. |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please review the ai's summary?
… Shell (#35072) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details: #8296 - ContentPage.OnBackButtonPressed not invoked on iOS/MacCatalyst when the native navigation bar back button is tapped inside a NavigationPage. #9095 - Shell.OnBackButtonPressed not invoked when the toolbar back button is tapped on Android and iOS Shell pages ### Root Cause #8296 - ShouldPopItem in NavigationRenderer.cs (the navigationBar:shouldPopItem: UIKit delegate callback) fires when the user taps the native iOS back button. - Previously it unconditionally set _uiRequestedPop = true and returned true, allowing the native pop without ever notifying MAUI's page model. As a result, ContentPage.OnBackButtonPressed was never called on iOS when the native back button was tapped inside a NavigationPage. #9095 - iOS (ShellSectionRenderer.cs) - Same issue — tracker.Value.Page?.SendBackButtonPressed() skipped the Shell chain and went directly to the ContentPage. ### Description of change #8296 **NavigationRenderer.cs** - In ShouldPopItem, call NavPage?.CurrentPage?.SendBackButtonPressed() before allowing the native pop. If it returns true (the page handled/cancelled back navigation), reset _uiRequestedPop and return false to prevent the native pop. #9095 **ShellSectionRenderer.cs** (iOS Shell) - Route through _context.Shell?.SendBackButtonPressed() instead of tracker.Value.Page?.SendBackButtonPressed(), so the Shell back button tap goes through the same chain as the system back button. **Note** on _sendPopPending = false in the BackButtonBehavior command path: On iOS 26+, _sendPopPending is set to true unconditionally at the start of SendPop(), before any BackButtonBehavior checks. When a BackButtonBehavior.Command executes and returns false (preventing navigation), ViewDidDisappear never fires — so without an explicit reset, the flag stays true permanently and silently blocks all subsequent back presses. The _sendPopPending = false after command execution (line 188) is therefore an intentional and necessary fix to prevent the back button becoming permanently unresponsive after a command-handled back press on iOS 26+. ### Test results #### Shell — `OnBackButtonPressed` (fixes #9095) | Platform | Navigation | `OnBackButtonPressed` | |---|---|---| | iOS / MacCatalyst | `GoToAsync` | ✅ Triggered | | Android | `GoToAsync` | ✅ Triggered (app back button and emulator back button) | | Windows | `GoToAsync` | ✅ Triggered | >`OnBackButtonPressed` is triggered for both the `Shell` and the contained `ContentPage`. #### NavigationPage — `OnBackButtonPressed` (fixes #8296) | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered (app back button and emulator back button) | | | Android | `PushModalAsync` | ✅ Triggered | Called on the modal ContentPage | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | > On Android and Windows, `OnBackButtonPressed` is triggered for both the `NavigationPage` and the contained `ContentPage`. #### TabbedPage — `OnBackButtonPressed` | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered | | | Android | `PushModalAsync` | ✅ Triggered | | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | #### FlyoutPage — `OnBackButtonPressed` | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered | | | Android | `PushModalAsync` | ✅ Triggered | | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | ### Validated the behaviour in the following platforms - [ ] Android - [ ] Windows - [x] iOS - [x] Mac ### Issue Fixes Fixes #8296 ### Screenshots | | Before | After | |--|---------|--------| |8296| <video src="https://github.com/user-attachments/assets/13887369-e9e9-42df-a2a0-db88f0e6b38f"> | <video src="https://github.com/user-attachments/assets/d0ccfb01-59e8-44bc-8878-54c5d0f6fb7e"> | |9095 | <video src="https://github.com/user-attachments/assets/664e0b7f-3964-4a1b-a4f6-46b1db758b48"> | <video src="https://github.com/user-attachments/assets/3ff159ec-93b0-4a91-854a-ca23211ff204"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
|
/backport to release/10.0.1xx-sr7 |
|
Started backporting to |
|
@kubaflo backporting to git am output$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix: ContentPage.OnBackButtonPressed not invoked on iOS native back button
Applying: Updated Fix
Using index info to reconstruct a base tree...
M src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
Falling back to patching base and 3-way merge...
Auto-merging src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
Applying: test: add UITest for #9095 - Shell.OnBackButtonPressed invoked via toolbar back button
Using index info to reconstruct a base tree...
M src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
A src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9095.cs
Falling back to patching base and 3-way merge...
Auto-merging src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
CONFLICT (modify/delete): src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9095.cs deleted in HEAD and modified in test: add UITest for #9095 - Shell.OnBackButtonPressed invoked via toolbar back button. Version test: add UITest for #9095 - Shell.OnBackButtonPressed invoked via toolbar back button of src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9095.cs left in tree.
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0003 test: add UITest for #9095 - Shell.OnBackButtonPressed invoked via toolbar back button
Error: The process '/usr/bin/git' failed with exit code 128 |
… for NavigationPage and Shell (#35434) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! Manual backport of #35072 to `release/10.0.1xx-sr7`. ## Why this is a manual backport The automated `/backport` workflow ([failed run](https://github.com/dotnet/maui/actions/runs/25818135030)) hit a `modify/delete` conflict on `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue9095.cs`. The file is added by #35150 (already backported as #35430, currently OPEN) and modified by #35072. Because #35430 is not yet merged into `release/10.0.1xx-sr7`, the file does not exist on the target branch and the patch could not apply cleanly. ## Resolution Cherry-picked the squashed merge commit `7023635baac2524ab02f602e56fd7608142984a1` of #35072 onto `release/10.0.1xx-sr7`. Resolved the conflict by keeping the post-merge version of `Issue9095.cs` (verified byte-identical to `inflight/current`, which contains both #35150's added content and #35072's tweaks). ##⚠️ Dependency on #35430 This PR's diff currently shows `Issue9095.cs` as a new file because it includes content originally introduced by #35150. Recommended merge order: 1. Merge #35430 (Android backport of #35150) first. 2. This PR will then auto-rebase to show only #35072's incremental delta (a 2-line change to `Issue9095.cs` and the `ShellSectionRenderer.cs` / `NavigationRenderer.cs` / `Issue8296.cs` changes). Alternatively, this PR can be merged first, but #35430 will then need a manual conflict resolution (the file would already exist). Fixes #8296 in `release/10.0.1xx-sr7`. --------- Co-authored-by: Dhivya-SF4094 <127717131+Dhivya-SF4094@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… Shell (#35072) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details: #8296 - ContentPage.OnBackButtonPressed not invoked on iOS/MacCatalyst when the native navigation bar back button is tapped inside a NavigationPage. #9095 - Shell.OnBackButtonPressed not invoked when the toolbar back button is tapped on Android and iOS Shell pages ### Root Cause #8296 - ShouldPopItem in NavigationRenderer.cs (the navigationBar:shouldPopItem: UIKit delegate callback) fires when the user taps the native iOS back button. - Previously it unconditionally set _uiRequestedPop = true and returned true, allowing the native pop without ever notifying MAUI's page model. As a result, ContentPage.OnBackButtonPressed was never called on iOS when the native back button was tapped inside a NavigationPage. #9095 - iOS (ShellSectionRenderer.cs) - Same issue — tracker.Value.Page?.SendBackButtonPressed() skipped the Shell chain and went directly to the ContentPage. ### Description of change #8296 **NavigationRenderer.cs** - In ShouldPopItem, call NavPage?.CurrentPage?.SendBackButtonPressed() before allowing the native pop. If it returns true (the page handled/cancelled back navigation), reset _uiRequestedPop and return false to prevent the native pop. #9095 **ShellSectionRenderer.cs** (iOS Shell) - Route through _context.Shell?.SendBackButtonPressed() instead of tracker.Value.Page?.SendBackButtonPressed(), so the Shell back button tap goes through the same chain as the system back button. **Note** on _sendPopPending = false in the BackButtonBehavior command path: On iOS 26+, _sendPopPending is set to true unconditionally at the start of SendPop(), before any BackButtonBehavior checks. When a BackButtonBehavior.Command executes and returns false (preventing navigation), ViewDidDisappear never fires — so without an explicit reset, the flag stays true permanently and silently blocks all subsequent back presses. The _sendPopPending = false after command execution (line 188) is therefore an intentional and necessary fix to prevent the back button becoming permanently unresponsive after a command-handled back press on iOS 26+. ### Test results #### Shell — `OnBackButtonPressed` (fixes #9095) | Platform | Navigation | `OnBackButtonPressed` | |---|---|---| | iOS / MacCatalyst | `GoToAsync` | ✅ Triggered | | Android | `GoToAsync` | ✅ Triggered (app back button and emulator back button) | | Windows | `GoToAsync` | ✅ Triggered | >`OnBackButtonPressed` is triggered for both the `Shell` and the contained `ContentPage`. #### NavigationPage — `OnBackButtonPressed` (fixes #8296) | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered (app back button and emulator back button) | | | Android | `PushModalAsync` | ✅ Triggered | Called on the modal ContentPage | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | > On Android and Windows, `OnBackButtonPressed` is triggered for both the `NavigationPage` and the contained `ContentPage`. #### TabbedPage — `OnBackButtonPressed` | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered | | | Android | `PushModalAsync` | ✅ Triggered | | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | #### FlyoutPage — `OnBackButtonPressed` | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered | | | Android | `PushModalAsync` | ✅ Triggered | | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | ### Validated the behaviour in the following platforms - [ ] Android - [ ] Windows - [x] iOS - [x] Mac ### Issue Fixes Fixes #8296 ### Screenshots | | Before | After | |--|---------|--------| |8296| <video src="https://github.com/user-attachments/assets/13887369-e9e9-42df-a2a0-db88f0e6b38f"> | <video src="https://github.com/user-attachments/assets/d0ccfb01-59e8-44bc-8878-54c5d0f6fb7e"> | |9095 | <video src="https://github.com/user-attachments/assets/664e0b7f-3964-4a1b-a4f6-46b1db758b48"> | <video src="https://github.com/user-attachments/assets/3ff159ec-93b0-4a91-854a-ca23211ff204"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…xx-sr8 (#35810) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Cut-then-merge step 2 of 2 SR8 was cut from `main` at [`e02d6b6dc2`](e02d6b6) (commit "Add gh-aw rerun review scanner (#35685)"). This PR pulls the SR7 stabilization work into SR8 so SR8 contains everything that's in SR7. - **Base:** `release/10.0.1xx-sr8` @ [`e02d6b6dc2`](e02d6b6) - **Merging in:** `release/10.0.1xx-sr7` @ [`9da598b4a1`](9da598b) (PatchVersion bump to 71) - **Merge base:** [`f8cb875e`](f8cb875eee) ("[Testing] The Windows WebView category is removed from CI…" #35335) - **Strategy:** non-fast-forward merge commit (preserves both branches' history) ## Conflict resolution Two trivial conflicts, both resolved by taking the SR8 (`HEAD`) side: | File | Why it conflicted | Resolution | | --- | --- | --- | | [`eng/Versions.props`](https://github.com/dotnet/maui/blob/release/10.0.1xx-sr8/eng/Versions.props) | SR7 bumped `PatchVersion` 70→71 (#35786); SR8 starts at 80 | Keep `PatchVersion=80` (SR8 is the higher patch band) | | `src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs` | Whitespace-only difference (`false; //` vs `false; //`) in two comments | Keep SR8's whitespace | No semantic conflicts. ## Inherited from SR7 26 SR7-only commits land in SR8 via this merge. The source PRs are: <details> <summary>Source PRs (43, deduped by commit)</summary> #35020, #35072, #35092, #35150, #35223, #35299, #35305, #35347, #35356, #35359, #35360, #35421, #35423, #35424, #35425, #35426, #35427, #35428, #35430, #35434, #35441, #35447, #35461, #35480, #35503, #35520, #35521, #35559, #35566, #35585, #35625, #35642, #35664, #35689, #35690, #35691, #35692, #35693, #35694, #35703, #35744, #35768, #35786 Includes the SR7 revert chain: - #35689 — Revert PR #30068 (FontImageSource centering on Windows) - #35694 — Revert TalkBack RadioButton fix - #35703 — Revert Shell.NavBarIsVisible fix - #35744 — Revert HybridWebView WebView fix - #35461, #35503 — additional Android reverts </details> After this lands, the release-readiness tracker can survey `release/10.0.1xx-sr8` directly instead of using `-Candidate -InheritFromPriorSr` mode against SR7.
… Shell (#35072) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details: #8296 - ContentPage.OnBackButtonPressed not invoked on iOS/MacCatalyst when the native navigation bar back button is tapped inside a NavigationPage. #9095 - Shell.OnBackButtonPressed not invoked when the toolbar back button is tapped on Android and iOS Shell pages ### Root Cause #8296 - ShouldPopItem in NavigationRenderer.cs (the navigationBar:shouldPopItem: UIKit delegate callback) fires when the user taps the native iOS back button. - Previously it unconditionally set _uiRequestedPop = true and returned true, allowing the native pop without ever notifying MAUI's page model. As a result, ContentPage.OnBackButtonPressed was never called on iOS when the native back button was tapped inside a NavigationPage. #9095 - iOS (ShellSectionRenderer.cs) - Same issue — tracker.Value.Page?.SendBackButtonPressed() skipped the Shell chain and went directly to the ContentPage. ### Description of change #8296 **NavigationRenderer.cs** - In ShouldPopItem, call NavPage?.CurrentPage?.SendBackButtonPressed() before allowing the native pop. If it returns true (the page handled/cancelled back navigation), reset _uiRequestedPop and return false to prevent the native pop. #9095 **ShellSectionRenderer.cs** (iOS Shell) - Route through _context.Shell?.SendBackButtonPressed() instead of tracker.Value.Page?.SendBackButtonPressed(), so the Shell back button tap goes through the same chain as the system back button. **Note** on _sendPopPending = false in the BackButtonBehavior command path: On iOS 26+, _sendPopPending is set to true unconditionally at the start of SendPop(), before any BackButtonBehavior checks. When a BackButtonBehavior.Command executes and returns false (preventing navigation), ViewDidDisappear never fires — so without an explicit reset, the flag stays true permanently and silently blocks all subsequent back presses. The _sendPopPending = false after command execution (line 188) is therefore an intentional and necessary fix to prevent the back button becoming permanently unresponsive after a command-handled back press on iOS 26+. ### Test results #### Shell — `OnBackButtonPressed` (fixes #9095) | Platform | Navigation | `OnBackButtonPressed` | |---|---|---| | iOS / MacCatalyst | `GoToAsync` | ✅ Triggered | | Android | `GoToAsync` | ✅ Triggered (app back button and emulator back button) | | Windows | `GoToAsync` | ✅ Triggered | >`OnBackButtonPressed` is triggered for both the `Shell` and the contained `ContentPage`. #### NavigationPage — `OnBackButtonPressed` (fixes #8296) | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered (app back button and emulator back button) | | | Android | `PushModalAsync` | ✅ Triggered | Called on the modal ContentPage | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | > On Android and Windows, `OnBackButtonPressed` is triggered for both the `NavigationPage` and the contained `ContentPage`. #### TabbedPage — `OnBackButtonPressed` | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered | | | Android | `PushModalAsync` | ✅ Triggered | | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | #### FlyoutPage — `OnBackButtonPressed` | Platform | Navigation | `OnBackButtonPressed` | Notes | |---|---|---|---| | iOS / MacCatalyst | `PushAsync` | ✅ Triggered | | | iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in modal navigation | | Android | `PushAsync` | ✅ Triggered | | | Android | `PushModalAsync` | ✅ Triggered | | | Windows | `PushAsync` | ✅ Triggered | | | Windows | `PushModalAsync` | — | Back button not visible in modal navigation | ### Validated the behaviour in the following platforms - [ ] Android - [ ] Windows - [x] iOS - [x] Mac ### Issue Fixes Fixes #8296 ### Screenshots | | Before | After | |--|---------|--------| |8296| <video src="https://github.com/user-attachments/assets/13887369-e9e9-42df-a2a0-db88f0e6b38f"> | <video src="https://github.com/user-attachments/assets/d0ccfb01-59e8-44bc-8878-54c5d0f6fb7e"> | |9095 | <video src="https://github.com/user-attachments/assets/664e0b7f-3964-4a1b-a4f6-46b1db758b48"> | <video src="https://github.com/user-attachments/assets/3ff159ec-93b0-4a91-854a-ca23211ff204"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Issue Details:
#8296 - ContentPage.OnBackButtonPressed not invoked on iOS/MacCatalyst when the native navigation bar back button is tapped inside a NavigationPage.
#9095 - Shell.OnBackButtonPressed not invoked when the toolbar back button is tapped on Android and iOS Shell pages
Root Cause
#8296
#9095 - iOS (ShellSectionRenderer.cs)
Description of change
#8296 NavigationRenderer.cs
#9095 ShellSectionRenderer.cs (iOS Shell)
Note on _sendPopPending = false in the BackButtonBehavior command path: On iOS 26+, _sendPopPending is set to true unconditionally at the start of SendPop(), before any BackButtonBehavior checks. When a BackButtonBehavior.Command executes and returns false (preventing navigation), ViewDidDisappear never fires — so without an explicit reset, the flag stays true permanently and silently blocks all subsequent back presses. The _sendPopPending = false after command execution (line 188) is therefore an intentional and necessary fix to prevent the back button becoming permanently unresponsive after a command-handled back press on iOS 26+.
Test results
Shell —
OnBackButtonPressed(fixes #9095)OnBackButtonPressedGoToAsyncGoToAsyncGoToAsyncNavigationPage —
OnBackButtonPressed(fixes #8296)OnBackButtonPressedPushAsyncPushModalAsyncPushAsyncPushModalAsyncPushAsyncPushModalAsyncTabbedPage —
OnBackButtonPressedOnBackButtonPressedPushAsyncPushModalAsyncPushAsyncPushModalAsyncPushAsyncPushModalAsyncFlyoutPage —
OnBackButtonPressedOnBackButtonPressedPushAsyncPushModalAsyncPushAsyncPushModalAsyncPushAsyncPushModalAsyncValidated the behaviour in the following platforms
Issue Fixes
Fixes #8296
Screenshots
BeforeFix-8296.mov
AfterFix-8296.mov
9095_BeforeFix_iOS.mov
9095_AfterFix_iOS.mov