File: Platform\iOS\TextFieldExtensions.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
using CoreGraphics;
using Foundation;
using Microsoft.Maui.Graphics;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Platform
{
	public static class TextFieldExtensions
	{
		public static void UpdateText(this UITextField textField, IEntry entry)
		{
			textField.Text = entry.Text;
		}
 
		public static void UpdateTextColor(this UITextField textField, ITextStyle textStyle)
		{
			var textColor = textStyle.TextColor;
			if (textColor != null)
				textField.TextColor = textColor.ToPlatform(ColorExtensions.LabelColor);
		}
 
		public static void UpdateIsPassword(this UITextField textField, IEntry entry)
		{
			if (entry.IsPassword && textField.IsFirstResponder)
			{
				textField.Enabled = false;
				textField.SecureTextEntry = true;
				textField.Enabled = entry.IsEnabled;
				textField.BecomeFirstResponder();
			}
			else
				textField.SecureTextEntry = entry.IsPassword;
		}
 
		public static void UpdateHorizontalTextAlignment(this UITextField textField, ITextAlignment textAlignment)
		{
			textField.TextAlignment = textAlignment.HorizontalTextAlignment.ToPlatformHorizontal(textField.EffectiveUserInterfaceLayoutDirection);
		}
 
		public static void UpdateVerticalTextAlignment(this UITextField textField, ITextAlignment textAlignment)
		{
			textField.VerticalAlignment = textAlignment.VerticalTextAlignment.ToPlatformVertical();
		}
 
		public static void UpdateIsTextPredictionEnabled(this UITextField textField, IEntry entry)
		{
			if (entry.IsTextPredictionEnabled)
				textField.AutocorrectionType = UITextAutocorrectionType.Yes;
			else
				textField.AutocorrectionType = UITextAutocorrectionType.No;
		}
 
		public static void UpdateIsSpellCheckEnabled(this UITextField textField, IEntry entry)
		{
			if (entry.IsSpellCheckEnabled)
				textField.SpellCheckingType = UITextSpellCheckingType.Yes;
			else
				textField.SpellCheckingType = UITextSpellCheckingType.No;
		}
 
		public static void UpdateMaxLength(this UITextField textField, IEntry entry)
		{
			var newText = textField.AttributedText.TrimToMaxLength(entry.MaxLength);
			if (newText != null && textField.AttributedText != newText)
				textField.AttributedText = newText;
		}
 
		public static void UpdatePlaceholder(this UITextField textField, IEntry entry, Color? defaultPlaceholderColor = null)
		{
			var placeholder = entry.Placeholder;
			if (placeholder == null)
			{
				textField.AttributedPlaceholder = null;
				return;
			}
 
			var placeholderColor = entry.PlaceholderColor;
			var foregroundColor = placeholderColor ?? defaultPlaceholderColor;
 
			textField.AttributedPlaceholder = foregroundColor == null
 				? new NSAttributedString(placeholder)
 				: new NSAttributedString(str: placeholder, foregroundColor: foregroundColor.ToPlatform());
 
			textField.AttributedPlaceholder.WithCharacterSpacing(entry.CharacterSpacing);
		}
 
		public static void UpdateIsReadOnly(this UITextField textField, IEntry entry)
		{
			textField.UserInteractionEnabled = !(entry.IsReadOnly || entry.InputTransparent);
		}
 
		public static void UpdateFont(this UITextField textField, ITextStyle textStyle, IFontManager fontManager)
		{
			var uiFont = fontManager.GetFont(textStyle.Font, UIFont.LabelFontSize);
			textField.Font = uiFont;
		}
 
		public static void UpdateReturnType(this UITextField textField, IEntry entry)
		{
			textField.ReturnKeyType = entry.ReturnType.ToPlatform();
		}
 
		public static void UpdateCharacterSpacing(this UITextField textField, ITextStyle textStyle)
		{
			var textAttr = textField.AttributedText?.WithCharacterSpacing(textStyle.CharacterSpacing);
			if (textAttr != null)
				textField.AttributedText = textAttr;
 
			textAttr = textField.AttributedPlaceholder?.WithCharacterSpacing(textStyle.CharacterSpacing);
			if (textAttr != null)
				textField.AttributedPlaceholder = textAttr;
		}
 
		public static void UpdateKeyboard(this UITextField textField, IEntry entry)
		{
			var keyboard = entry.Keyboard;
 
			textField.ApplyKeyboard(keyboard);
 
			if (keyboard is not CustomKeyboard)
			{
				textField.UpdateIsTextPredictionEnabled(entry);
				textField.UpdateIsSpellCheckEnabled(entry);
			}
 
			textField.ReloadInputViews();
		}
 
		public static void UpdateCursorPosition(this UITextField textField, IEntry entry)
		{
			var selectedTextRange = textField.SelectedTextRange;
			if (selectedTextRange == null)
				return;
			if (textField.GetOffsetFromPosition(textField.BeginningOfDocument, selectedTextRange.Start) != entry.CursorPosition)
				UpdateCursorSelection(textField, entry);
		}
 
		public static void UpdateSelectionLength(this UITextField textField, IEntry entry)
		{
			var selectedTextRange = textField.SelectedTextRange;
			if (selectedTextRange == null)
				return;
			if (textField.GetOffsetFromPosition(selectedTextRange.Start, selectedTextRange.End) != entry.SelectionLength)
				UpdateCursorSelection(textField, entry);
		}
 
		/* Updates both the IEntry.CursorPosition and IEntry.SelectionLength properties. */
		static void UpdateCursorSelection(this UITextField textField, IEntry entry)
		{
			if (!entry.IsReadOnly)
			{
				UITextPosition start = GetSelectionStart(textField, entry, out int startOffset);
				UITextPosition end = GetSelectionEnd(textField, entry, start, startOffset);
 
				textField.SelectedTextRange = textField.GetTextRange(start, end);
			}
		}
 
		static UITextPosition GetSelectionStart(UITextField textField, IEntry entry, out int startOffset)
		{
			int cursorPosition = entry.CursorPosition;
 
			UITextPosition start = textField.GetPosition(textField.BeginningOfDocument, cursorPosition) ?? textField.EndOfDocument;
			startOffset = Math.Max(0, (int)textField.GetOffsetFromPosition(textField.BeginningOfDocument, start));
 
			if (startOffset != cursorPosition)
				entry.CursorPosition = startOffset;
 
			return start;
		}
 
		static UITextPosition GetSelectionEnd(UITextField textField, IEntry entry, UITextPosition start, int startOffset)
		{
			int selectionLength = entry.SelectionLength;
			int textFieldLength = textField.Text == null ? 0 : textField.Text.Length;
			// Get the desired range in respect to the actual length of the text we are working with
			UITextPosition end = textField.GetPosition(start, Math.Min(textFieldLength - entry.CursorPosition, selectionLength)) ?? start;
			int endOffset = Math.Max(startOffset, (int)textField.GetOffsetFromPosition(textField.BeginningOfDocument, end));
 
			int newSelectionLength = Math.Max(0, endOffset - startOffset);
			if (newSelectionLength != selectionLength)
				entry.SelectionLength = newSelectionLength;
 
			return end;
		}
 
		public static void UpdateClearButtonVisibility(this UITextField textField, IEntry entry)
		{
			if (entry.ClearButtonVisibility == ClearButtonVisibility.WhileEditing)
			{
				textField.ClearButtonMode = UITextFieldViewMode.WhileEditing;
				textField.UpdateClearButtonColor(entry);
			}
			else
				textField.ClearButtonMode = UITextFieldViewMode.Never;
		}
 
		internal static void UpdateClearButtonColor(this UITextField textField, IEntry entry)
		{
			if (textField.ValueForKey(new NSString("clearButton")) is UIButton clearButton)
			{
				UIImage defaultClearImage = clearButton.ImageForState(UIControlState.Highlighted);
 
				if (entry.TextColor is null)
				{
					clearButton.SetImage(defaultClearImage, UIControlState.Normal);
					clearButton.SetImage(defaultClearImage, UIControlState.Highlighted);
				}
				else
				{
					clearButton.TintColor = entry.TextColor.ToPlatform();
 
					var tintedClearImage = GetClearButtonTintImage(defaultClearImage, entry.TextColor.ToPlatform());
					clearButton.SetImage(tintedClearImage, UIControlState.Normal);
					clearButton.SetImage(tintedClearImage, UIControlState.Highlighted);
				}
			}
		}
 
		internal static UIImage? GetClearButtonTintImage(UIImage image, UIColor color)
		{
			var size = image.Size;
 
			UIGraphics.BeginImageContextWithOptions(size, false, UIScreen.MainScreen.Scale);
 
			if (UIGraphics.GetCurrentContext() == null)
				return null;
 
			var context = UIGraphics.GetCurrentContext();
 
			image.Draw(CGPoint.Empty, CGBlendMode.Normal, 1.0f);
			context?.SetFillColor(color.CGColor);
			context?.SetBlendMode(CGBlendMode.SourceIn);
			context?.SetAlpha(1.0f);
 
			var rect = new CGRect(CGPoint.Empty.X, CGPoint.Empty.Y, image.Size.Width, image.Size.Height);
			context?.FillRect(rect);
 
			var tintedImage = UIGraphics.GetImageFromCurrentImageContext();
 
			UIGraphics.EndImageContext();
 
			return tintedImage;
		}
	}
}