-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[Mac] Fix for preventing macOS picker dialog from closing immediately when opened with Tab #33784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…or MacCatalyst that explicitly dismisses the picker only on programmatic unfocus calls, while removing automatic dismissal from EditingDidEnd handler to prevent TAB-induced immediate closure.
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a macOS-specific issue where the Picker dialog closes immediately when opened using the Tab key. The root cause was that pressing Tab triggers an EditingDidEnd event that persisted when the picker reopens, causing the picker to dismiss immediately after opening. This was a regression from PR #27973.
Changes:
- Removed automatic dismissal from the EditingDidEnd handler, which now only updates IsFocused and IsOpen state
- Added MapUnfocus command handler for MacCatalyst that dismisses the UIAlertController only when Unfocus() is called programmatically
- Added Focus/Unfocus command mappings to the CommandMapper for MACCATALYST platform
- Added a UI test to verify the picker remains open when accessed via Tab key
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Core/src/Handlers/Picker/PickerHandler.iOS.cs | Adds _currentPickerController field to track active picker, modifies EditingDidEnd to only update state (not dismiss), adds MapFocus and MapUnfocus handlers for MacCatalyst |
| src/Core/src/Handlers/Picker/PickerHandler.cs | Extends CommandMapper to include Focus/Unfocus commands for MACCATALYST in addition to ANDROID |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33463.cs | Adds UI test that verifies picker stays open after Tab key navigation |
| src/Controls/tests/TestCases.HostApp/Issues/Issue33463.cs | Adds test page with Picker and Entry controls for testing Tab navigation |
| _currentPickerController = UIAlertController.Create("", "", UIAlertControllerStyle.ActionSheet); | ||
| var pickerController = _currentPickerController; |
Copilot
AI
Feb 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If DisplayAlert is called while _currentPickerController is not null (for example, if the picker is already open and DisplayAlert is called again), the code will create a new UIAlertController and overwrite the _currentPickerController reference without dismissing the previous one. This could lead to multiple picker dialogs being displayed simultaneously or leaked view controllers.
Consider adding a guard at the start of DisplayAlert to check if _currentPickerController is not null, and if so, either dismiss it first or return early to prevent duplicate displays.
| Task.Delay(800).Wait(); | ||
|
|
||
| var doneButton = App.FindElement("Done"); |
Copilot
AI
Feb 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After SendTabKey(), the test should verify that the picker opens before checking for the Done button. Currently, it relies on a fixed delay and then uses FindElement which may return null without waiting. If the picker hasn't opened yet, FindElement("Done") will fail.
The test should use WaitForElement instead of Task.Delay + FindElement:
- Remove: Task.Delay(800).Wait(); var doneButton = App.FindElement("Done");
- Use: var doneButton = App.WaitForElement("Done", timeout: TimeSpan.FromSeconds(2));
This ensures the test waits for the picker to open rather than assuming 800ms is sufficient.
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!
Root cause
The issue occurs because the TAB key triggers an EditingDidEnd event that persists when the picker reopens. When the picker opens, EditingDidBegin presents the UIAlertController, but immediately after, another EditingDidEnd event fires due to the lingering focus state. The original code dismissed the picker in the EditingDidEnd handler, causing the picker to close instantly after opening and breaking keyboard navigation.
Regression PR: #27973
Description of Issue Fix
The fix involves separating how the picker responds to different dismissal triggers. It removes automatic dismissal from the EditingDidEnd handler, which now only updates the IsFocused and IsOpen state. A new MapUnfocus command handler is added for MacCatalyst that stores a reference to the active UIAlertController and dismisses it only when Unfocus() is called programmatically. This distinguishes between TAB-induced events (no dismissal) and explicit unfocus commands (dismissal), fixing the keyboard navigation issue while maintaining compatibility with existing focus behavior.
Tested the behavior in the following platforms.
Issues Fixed
Fixes #33463
Output
PickerDialogIssue-BeforeFix.mov
PickerDialogIssue-AfterFix.mov