File: Handlers\Entry\EntryHandler.iOS.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
using Foundation;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Handlers
{
	public partial class EntryHandler : ViewHandler<IEntry, MauiTextField>
	{
		readonly MauiTextFieldProxy _proxy = new();
 
		protected override MauiTextField CreatePlatformView() =>
			new MauiTextField
			{
				BorderStyle = UITextBorderStyle.RoundedRect,
				ClipsToBounds = true
			};
 
		public override void SetVirtualView(IView view)
		{
			base.SetVirtualView(view);
 
			_proxy.SetVirtualView(PlatformView);
		}
 
		protected override void ConnectHandler(MauiTextField platformView)
		{
			_proxy.Connect(VirtualView, platformView);
		}
 
		protected override void DisconnectHandler(MauiTextField platformView)
		{
			_proxy.Disconnect(platformView);
		}
 
		public static void MapText(IEntryHandler handler, IEntry entry)
		{
			handler.PlatformView?.UpdateText(entry);
 
			// Any text update requires that we update any attributed string formatting
			MapFormatting(handler, entry);
		}
 
		public static void MapTextColor(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateTextColor(entry);
 
		public static void MapIsPassword(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateIsPassword(entry);
 
		public static void MapHorizontalTextAlignment(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateHorizontalTextAlignment(entry);
 
		public static void MapVerticalTextAlignment(IEntryHandler handler, IEntry entry) =>
			handler?.PlatformView?.UpdateVerticalTextAlignment(entry);
 
		public static void MapIsTextPredictionEnabled(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateIsTextPredictionEnabled(entry);
 
		public static void MapIsSpellCheckEnabled(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateIsSpellCheckEnabled(entry);
 
		public static void MapMaxLength(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateMaxLength(entry);
 
		public static void MapPlaceholder(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdatePlaceholder(entry);
 
		public static void MapPlaceholderColor(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdatePlaceholder(entry);
 
		public static void MapIsReadOnly(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateIsReadOnly(entry);
 
		public static void MapKeyboard(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateKeyboard(entry);
 
		public static void MapReturnType(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateReturnType(entry);
 
		public static void MapFont(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateFont(entry, handler.GetRequiredService<IFontManager>());
 
		public static void MapCharacterSpacing(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateCharacterSpacing(entry);
 
		public static void MapCursorPosition(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateCursorPosition(entry);
 
		public static void MapSelectionLength(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateSelectionLength(entry);
 
		public static void MapClearButtonVisibility(IEntryHandler handler, IEntry entry) =>
			handler.PlatformView?.UpdateClearButtonVisibility(entry);
 
		public static void MapFormatting(IEntryHandler handler, IEntry entry)
		{
			handler.PlatformView?.UpdateMaxLength(entry);
 
			// Update all of the attributed text formatting properties
			handler.PlatformView?.UpdateCharacterSpacing(entry);
 
			// Setting any of those may have removed text alignment settings,
			// so we need to make sure those are applied, too
			handler.PlatformView?.UpdateHorizontalTextAlignment(entry);
		}
 
		protected virtual bool OnShouldReturn(UITextField view) =>
			_proxy.OnShouldReturn(view);
 
		class MauiTextFieldProxy
		{
			bool _set;
			WeakReference<IEntry>? _virtualView;
 
			IEntry? VirtualView => _virtualView is not null && _virtualView.TryGetTarget(out var v) ? v : null;
 
			public void Connect(IEntry virtualView, MauiTextField platformView)
			{
				_virtualView = new(virtualView);
 
				platformView.ShouldReturn += OnShouldReturn;
				platformView.EditingDidBegin += OnEditingBegan;
				platformView.EditingChanged += OnEditingChanged;
				platformView.EditingDidEnd += OnEditingEnded;
				platformView.TextPropertySet += OnTextPropertySet;
				platformView.ShouldChangeCharacters += OnShouldChangeCharacters;
			}
 
			public void Disconnect(MauiTextField platformView)
			{
				_virtualView = null;
 
				platformView.ShouldReturn -= OnShouldReturn;
				platformView.EditingDidBegin -= OnEditingBegan;
				platformView.EditingChanged -= OnEditingChanged;
				platformView.EditingDidEnd -= OnEditingEnded;
				platformView.TextPropertySet -= OnTextPropertySet;
				platformView.ShouldChangeCharacters -= OnShouldChangeCharacters;
 
				if (_set)
					platformView.SelectionChanged -= OnSelectionChanged;
 
				_set = false;
			}
 
			public void SetVirtualView(MauiTextField platformView)
			{
				if (!_set)
					platformView.SelectionChanged += OnSelectionChanged;
				_set = true;
			}
 
			public bool OnShouldReturn(UITextField view)
			{
				KeyboardAutoManager.GoToNextResponderOrResign(view);
 
				VirtualView?.Completed();
 
				return false;
			}
 
			void OnEditingBegan(object? sender, EventArgs e)
			{
				if (sender is MauiTextField platformView && VirtualView is IEntry virtualView)
				{
					platformView.UpdateSelectionLength(virtualView);
					virtualView.IsFocused = true;
				}
			}
 
			void OnEditingChanged(object? sender, EventArgs e)
			{
				if (sender is MauiTextField platformView)
				{
					VirtualView?.UpdateText(platformView.Text);
				}
			}
 
			void OnEditingEnded(object? sender, EventArgs e)
			{
				if (sender is MauiTextField platformView && VirtualView is IEntry virtualView)
				{
					virtualView.UpdateText(platformView.Text);
					virtualView.IsFocused = false;
				}
			}
 
			void OnTextPropertySet(object? sender, EventArgs e)
			{
				if (sender is MauiTextField platformView)
				{
					VirtualView?.UpdateText(platformView.Text);
				}
			}
 
			bool OnShouldChangeCharacters(UITextField textField, NSRange range, string replacementString) =>
				VirtualView?.TextWithinMaxLength(textField.Text, range, replacementString) ?? false;
 
			void OnSelectionChanged(object? sender, EventArgs e)
			{
				if (sender is MauiTextField platformView && VirtualView is IEntry virtualView)
				{
					var cursorPosition = platformView.GetCursorPosition();
					var selectedTextLength = platformView.GetSelectedTextLength();
 
					if (virtualView.CursorPosition != cursorPosition)
						virtualView.CursorPosition = cursorPosition;
 
					if (virtualView.SelectionLength != selectedTextLength)
						virtualView.SelectionLength = selectedTextLength;
				}
			}
		}
	}
}