File: Platform\iOS\TextViewExtensions.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Platform
{
	public static class TextViewExtensions
	{
		public static void UpdateText(this UITextView textView, IEditor editor)
		{
			string text = editor.Text;
 
			if (textView.Text != text)
				textView.Text = text;
		}
 
		public static void UpdateTextColor(this UITextView textView, IEditor editor)
		{
			var textColor = editor.TextColor;
 
			if (textColor == null)
				textView.TextColor = ColorExtensions.LabelColor;
			else
				textView.TextColor = textColor.ToPlatform();
		}
 
		public static void UpdateCharacterSpacing(this UITextView textView, ITextStyle textStyle)
		{
			var textAttr = textView.AttributedText?.WithCharacterSpacing(textStyle.CharacterSpacing);
			if (textAttr != null)
				textView.AttributedText = textAttr;
 
			if (textView is MauiTextView mauiTextView)
			{
				var phAttr = mauiTextView.AttributedPlaceholderText?.WithCharacterSpacing(textStyle.CharacterSpacing);
				if (phAttr != null)
					mauiTextView.AttributedPlaceholderText = phAttr;
			}
		}
 
		public static void UpdateMaxLength(this UITextView textView, IEditor editor)
		{
			var newText = textView.AttributedText.TrimToMaxLength(editor.MaxLength);
			if (newText != null && textView.AttributedText != newText)
				textView.AttributedText = newText;
		}
 
		public static void UpdateIsTextPredictionEnabled(this UITextView textView, IEditor editor)
		{
			if (editor.IsTextPredictionEnabled)
				textView.AutocorrectionType = UITextAutocorrectionType.Yes;
			else
				textView.AutocorrectionType = UITextAutocorrectionType.No;
		}
 
		public static void UpdateIsSpellCheckEnabled(this UITextView textView, IEditor editor)
		{
			if (editor.IsSpellCheckEnabled)
				textView.SpellCheckingType = UITextSpellCheckingType.Yes;
			else
				textView.SpellCheckingType = UITextSpellCheckingType.No;
		}
 
		public static void UpdateFont(this UITextView textView, ITextStyle textStyle, IFontManager fontManager)
		{
			var font = textStyle.Font;
			var uiFont = fontManager.GetFont(font, UIFont.LabelFontSize);
			textView.Font = uiFont;
		}
 
		public static void UpdateIsReadOnly(this UITextView textView, IEditor editor)
		{
			textView.UpdateEditable(editor);
		}
 
		public static void UpdateIsEnabled(this UITextView textView, IEditor editor)
		{
			textView.UpdateEditable(editor);
		}
 
		internal static void UpdateEditable(this UITextView textView, IEditor editor)
		{
			var isEditable = editor.IsEnabled && !editor.IsReadOnly;
 
			textView.Editable = isEditable;
 
			// If the input accessory view is set, we need to hide it when the editor is read-only
			// otherwise it will appear when the editor recieves focus.
			if (textView.InputAccessoryView is { } view)
				view.Hidden = !isEditable;
		}
 
		public static void UpdateKeyboard(this UITextView textView, IEditor editor)
		{
			var keyboard = editor.Keyboard;
 
			textView.ApplyKeyboard(keyboard);
 
			if (keyboard is not CustomKeyboard)
			{
				textView.UpdateIsTextPredictionEnabled(editor);
				textView.UpdateIsSpellCheckEnabled(editor);
			}
 
			textView.ReloadInputViews();
		}
 
		public static void UpdateCursorPosition(this UITextView textView, IEditor editor)
		{
			var selectedTextRange = textView.SelectedTextRange;
			if (selectedTextRange == null)
				return;
			if (textView.GetOffsetFromPosition(textView.BeginningOfDocument, selectedTextRange.Start) != editor.CursorPosition)
				UpdateCursorSelection(textView, editor);
		}
 
		public static void UpdateSelectionLength(this UITextView textView, IEditor editor)
		{
			var selectedTextRange = textView.SelectedTextRange;
			if (selectedTextRange == null)
				return;
			if (textView.GetOffsetFromPosition(selectedTextRange.Start, selectedTextRange.End) != editor.SelectionLength)
				UpdateCursorSelection(textView, editor);
		}
 
		public static void UpdateHorizontalTextAlignment(this UITextView textView, IEditor editor)
		{
			textView.TextAlignment = editor.HorizontalTextAlignment.ToPlatformHorizontal(textView.EffectiveUserInterfaceLayoutDirection);
		}
 
		public static void UpdateVerticalTextAlignment(this MauiTextView textView, IEditor editor)
		{
			textView.VerticalTextAlignment = editor.VerticalTextAlignment;
		}
 
		public static void UpdatePlaceholder(this MauiTextView textView, IEditor editor) =>
			textView.PlaceholderText = editor.Placeholder;
 
		public static void UpdatePlaceholderColor(this MauiTextView textView, IEditor editor) =>
			textView.PlaceholderTextColor = editor.PlaceholderColor?.ToPlatform() ?? ColorExtensions.PlaceholderColor;
 
		static void UpdateCursorSelection(this UITextView textView, IEditor editor)
		{
			if (!editor.IsReadOnly)
			{
				UITextPosition start = GetSelectionStart(textView, editor, out int startOffset);
				UITextPosition end = GetSelectionEnd(textView, editor, start, startOffset);
 
				textView.SelectedTextRange = textView.GetTextRange(start, end);
			}
		}
 
		static UITextPosition GetSelectionStart(UITextView textView, IEditor editor, out int startOffset)
		{
			int cursorPosition = editor.CursorPosition;
 
			UITextPosition start = textView.GetPosition(textView.BeginningOfDocument, cursorPosition) ?? textView.EndOfDocument;
			startOffset = Math.Max(0, (int)textView.GetOffsetFromPosition(textView.BeginningOfDocument, start));
 
			if (startOffset != cursorPosition)
				editor.CursorPosition = startOffset;
 
			return start;
		}
 
		static UITextPosition GetSelectionEnd(UITextView textView, IEditor editor, UITextPosition start, int startOffset)
		{
			int selectionLength = editor.SelectionLength;
			int textFieldLength = textView.Text == null ? 0 : textView.Text.Length;
			// Get the desired range in respect to the actual length of the text we are working with
			UITextPosition end = textView.GetPosition(start, Math.Min(textFieldLength - editor.CursorPosition, selectionLength)) ?? start;
			int endOffset = Math.Max(startOffset, (int)textView.GetOffsetFromPosition(textView.BeginningOfDocument, end));
 
			int newSelectionLength = Math.Max(0, endOffset - startOffset);
			if (newSelectionLength != selectionLength)
				editor.SelectionLength = newSelectionLength;
 
			return end;
		}
	}
}