Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue34422.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 34422, "SearchBar clear button still appears on MacCatalyst after clearing input", PlatformAffected.macOS)]
public class Issue34422 : ContentPage
{
readonly SearchBar _searchBar;

public Issue34422()
{
_searchBar = new SearchBar
{
Placeholder = "Search...",
AutomationId = "TestSearchBar"
};

var addTextButton = new Button
{
Text = "Add Text",
AutomationId = "AddTextButton"
};

addTextButton.Clicked += (s, e) =>
{
_searchBar.Text = "Search text";
};

var clearButton = new Button
{
Text = "Clear SearchBar Text",
AutomationId = "ClearButton"
};

clearButton.Clicked += (s, e) =>
{
_searchBar.Text = string.Empty;
};

Content = new VerticalStackLayout
{
Padding = new Thickness(20),
Spacing = 10,
Children =
{
_searchBar,
addTextButton,
clearButton
}
};
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue34422 : _IssuesUITest
{
public Issue34422(TestDevice device) : base(device) { }

public override string Issue => "SearchBar clear button still appears on MacCatalyst after clearing input";

[Test, Order(1)]
[Category(UITestCategories.SearchBar)]
public void SearchBarClearButtonShouldBeVisibleWithText()
{
App.WaitForElement("TestSearchBar");
App.Tap("TestSearchBar");
App.Tap("AddTextButton");
VerifyScreenshot();
}

[Test, Order(2)]
[Category(UITestCategories.SearchBar)]
public void SearchBarClearButtonShouldDisappearAfterClearingInput()
{
// First add text so the clear button appears
App.WaitForElement("TestSearchBar");
App.Tap("TestSearchBar");
App.Tap("AddTextButton");
App.Tap("ClearButton");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Regression Prevention and Test Coverage — This regression test clears the SearchBar through a helper Button, so it does not exercise the native MacCatalyst clear affordance whose stale rendering is being fixed. Add coverage that enters text and activates/verifies the native clear button path, or expose/assert the native clear-button state from the handler, so the test fails for the current Hidden-toggle implementation and passes for the hierarchy-refresh fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this fix does not use the remove/re-add approach for the native clear button, the proposed test coverage is not applicable to the current implementation.

VerifyScreenshot();
}
Comment thread
devanathan-vaithiyanathan marked this conversation as resolved.
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ protected override MauiSearchBar CreatePlatformView()

_editor = searchBar.GetSearchTextField();


return searchBar;
}

Expand Down Expand Up @@ -154,6 +153,9 @@ internal static void MapSelectionLength(ISearchBarHandler handler, ISearchBar se
public static void MapCancelButtonColor(ISearchBarHandler handler, ISearchBar searchBar)
{
handler.PlatformView?.UpdateCancelButton(searchBar);
if (handler is SearchBarHandler searchBarHandler)
handler.PlatformView?.UpdateClearButtonVisibility(!string.IsNullOrEmpty(searchBar.Text));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Handler Mapper and Property Patterns — The clear-button visibility refresh is wired through MapCancelButtonColor, but text changes are owned by MapText/TextSetOrChanged, not the cancel-button-color mapper. If ShowsCancelButton is already in the expected state, a programmatic Text mutation can skip this mapper and leave the MacCatalyst clear button stale. Move the refresh to the actual text update paths and keep MapCancelButtonColor scoped to cancel-button styling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per the suggestion, the Clear Button remains visible while the SearchBar is focused, which is not required. Therefore, not taking this suggestion, as the current implementation works correctly.


Comment thread
devanathan-vaithiyanathan marked this conversation as resolved.
}

internal static void MapSearchIconColor(ISearchBarHandler handler, ISearchBar searchBar)
Expand Down Expand Up @@ -277,6 +279,7 @@ void OnEditingChanged(object? sender, EventArgs e)
if (Handler is SearchBarHandler handler)
{
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only updates the MacCatalyst clear button from native editing callbacks. The failing repro clears the managed SearchBar.Text programmatically, which flows through MapText/UpdateText and does not raise this callback, so the native clear button can remain visible after the text is cleared. Please update the clear-button state from the text mapper as well.

handler.UpdateCancelButtonVisibility();
handler.PlatformView?.UpdateClearButtonVisibility(!string.IsNullOrEmpty(VirtualView?.Text));
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/Core/src/Platform/iOS/SearchBarExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ public static void UpdateFont(this UISearchBar uiSearchBar, ITextStyle textStyle
textField.UpdateFont(textStyle, fontManager);
}

internal static void UpdateClearButtonVisibility(this UISearchBar uiSearchBar, bool hasText)
{
if (OperatingSystem.IsMacCatalyst())
{
var clearButton = uiSearchBar.GetClearButton();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This relies on private KVC (clearButton) and a cached UIKit subview at the moment this method runs. On MacCatalyst UIKit can create or refresh this control outside these callbacks, so this is fragile and was not enough to remove the Clear text element in the regression evidence. Prefer a supported text-field mode or a lifecycle-aware update tied to MapText, and keep the private lookup as a last resort only if it is empirically proven.


if (clearButton != null)
{
var shouldHide = !hasText;

if (clearButton.Hidden != shouldHide)
{
clearButton.Hidden = shouldHide;
Comment thread
devanathan-vaithiyanathan marked this conversation as resolved.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Logic and Correctness — Toggling the private clear button's Hidden flag is not sufficient for the MacCatalyst failure mode: the supplied gate result shows the PR fix still fails, while the verified alternative removes/re-adds the private clear UIButton from its superview to force UIKit to refresh the rendered state. Please replace this with the empirically passing hierarchy-refresh approach, or another mechanism that actually invalidates the native clear button.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the clear button only needs its visibility updated in this scenario, removing and re-adding it to the hierarchy is not required. The current visibility toggle approach is sufficient for the fix.

}
}
}
}

public static void UpdateVerticalTextAlignment(this UISearchBar uiSearchBar, ISearchBar searchBar)
{
uiSearchBar.UpdateVerticalTextAlignment(searchBar, null);
Expand Down Expand Up @@ -460,5 +478,7 @@ static UITextPosition GetSelectionEnd(UITextField textField, UITextPosition star
var end = textField.GetPosition(start, endOffset - startOffset);
return end ?? start;
}
internal static UIButton? GetClearButton(this UISearchBar searchBar) =>
searchBar.GetSearchTextField()?.ValueForKey(new NSString("clearButton")) as UIButton;
}
}
Loading