diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs index 107160fb73d9..7d1040697be5 100644 --- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs +++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs @@ -227,26 +227,40 @@ internal static void UpdateClearButtonColor(this UITextField textField, IEntry e { if (textField.ValueForKey(new NSString("clearButton")) is UIButton clearButton) { - UIImage defaultClearImage = clearButton.ImageForState(UIControlState.Highlighted); - if (entry.TextColor is null) { // Setting TintColor to null allows the system to automatically apply the appropriate color based on the current theme (light or dark mode) clearButton.TintColor = null; + // SetImage(null) releases the custom tinted bitmap so UIKit restores its system default. + // The color path (else branch) reads ImageForState(.Highlighted) to get that original + // image as the source for tinting. Without these calls, TintColor=null has no visual effect. + clearButton.SetImage(null, UIControlState.Normal); + clearButton.SetImage(null, UIControlState.Highlighted); } else { + // On a null→color transition, UIKit restores the system image after SetImage(null), + // so ImageForState(Highlighted) returns the system clear button image as the tinting source. + UIImage? defaultClearImage = clearButton.ImageForState(UIControlState.Highlighted); clearButton.TintColor = entry.TextColor.ToPlatform(); var tintedClearImage = GetClearButtonTintImage(defaultClearImage, entry.TextColor.ToPlatform()); - clearButton.SetImage(tintedClearImage, UIControlState.Normal); - clearButton.SetImage(tintedClearImage, UIControlState.Highlighted); + if (tintedClearImage is not null) + { + clearButton.SetImage(tintedClearImage, UIControlState.Normal); + clearButton.SetImage(tintedClearImage, UIControlState.Highlighted); + } } } } - internal static UIImage? GetClearButtonTintImage(UIImage image, UIColor color) + internal static UIImage? GetClearButtonTintImage(UIImage? image, UIColor color) { + if (image is null) + { + return null; + } + var size = image.Size; var renderer = new UIGraphicsImageRenderer(size, new UIGraphicsImageRendererFormat() diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs index 753c158cb38b..1cb5c3f8895e 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -93,6 +93,56 @@ public async Task CharacterSpacingInitializesCorrectly() Assert.Equal(xplatCharacterSpacing, values.PlatformViewValue); } + [Fact(DisplayName = "Clear button image resets when TextColor is null")] + public async Task ClearButtonImageResetsWhenTextColorIsNull() + { + EntryStub entry = new EntryStub + { + Text = "MAUI", + ClearButtonVisibility = ClearButtonVisibility.WhileEditing, + TextColor = null + }; + + await AttachAndRun(entry, async (handler) => + { + await AssertEventually(() => handler.PlatformView.IsLoaded()); + Assert.True(handler.PlatformView.BecomeFirstResponder()); + await AssertEventually(() => handler.PlatformView.IsFirstResponder); + + var clearButton = GetNativeClearButton(handler); + Assert.NotNull(clearButton); + + var defaultImage = clearButton.ImageForState(UIControlState.Normal); + Assert.NotNull(defaultImage); + Assert.Equal(UIImageRenderingMode.AlwaysOriginal, defaultImage.RenderingMode); + + entry.TextColor = Colors.Purple; + handler.UpdateValue(nameof(IEntry.TextColor)); + + var tintedImage = clearButton.ImageForState(UIControlState.Normal); + Assert.NotNull(tintedImage); + Assert.Equal(UIImageRenderingMode.Automatic, tintedImage.RenderingMode); + + entry.TextColor = null; + handler.UpdateValue(nameof(IEntry.TextColor)); + + // UIKit restores the original AlwaysOriginal system image when SetImage(null) is called + // on this private clearButton — ImageForState(.Highlighted) must return non-null for re-tinting to work. + var resetImage = clearButton.ImageForState(UIControlState.Normal); + Assert.NotNull(resetImage); + Assert.Equal(UIImageRenderingMode.AlwaysOriginal, resetImage.RenderingMode); + + entry.TextColor = Colors.Blue; + handler.UpdateValue(nameof(IEntry.TextColor)); + + // Verify re-tinting works after reset (null→color→null→color) + // Confirms ImageForState(.Highlighted) returns the original after SetImage(null) + var retintedImage = clearButton.ImageForState(UIControlState.Normal); + Assert.NotNull(retintedImage); + Assert.Equal(UIImageRenderingMode.Automatic, retintedImage.RenderingMode); + }); + } + [Fact] public async Task NextMovesToNextEntry() { @@ -832,6 +882,9 @@ bool GetNativeIsChatKeyboard(EntryHandler entryHandler) bool GetNativeClearButtonVisibility(EntryHandler entryHandler) => GetNativeEntry(entryHandler).ClearButtonMode == UITextFieldViewMode.WhileEditing; + static UIButton GetNativeClearButton(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).ValueForKey(new NSString("clearButton")) as UIButton; + UITextAlignment GetNativeHorizontalTextAlignment(EntryHandler entryHandler) => GetNativeEntry(entryHandler).TextAlignment;