File: iOS\Renderers\DatePickerRenderer.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Foundation;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Platform;
using ObjCRuntime;
using UIKit;
using RectangleF = CoreGraphics.CGRect;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
{
	[PortHandler]
	internal class NoCaretField : UITextField
	{
		public NoCaretField() : base(new RectangleF())
		{
			SpellCheckingType = UITextSpellCheckingType.No;
			AutocorrectionType = UITextAutocorrectionType.No;
			AutocapitalizationType = UITextAutocapitalizationType.None;
		}
 
		public override RectangleF GetCaretRectForPosition(UITextPosition position)
		{
			return new RectangleF();
		}
	}
 
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public class DatePickerRenderer : DatePickerRendererBase<UITextField>
	{
		[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
		public DatePickerRenderer()
		{
 
		}
 
		[PortHandler]
		protected override UITextField CreateNativeControl()
		{
			return new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
		}
	}
 
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public abstract class DatePickerRendererBase<TControl> : ViewRenderer<DatePicker, TControl>
		where TControl : UITextField
	{
		UIDatePicker _picker;
		UIColor _defaultTextColor;
		bool _disposed;
		bool _useLegacyColorManagement;
 
		IElementController ElementController => Element as IElementController;
 
		internal UIDatePicker Picker => _picker;
 
		abstract protected override TControl CreateNativeControl();
 
		[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
		public DatePickerRendererBase()
		{
 
		}
 
		protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
		{
			base.OnElementChanged(e);
 
			if (e.NewElement == null)
				return;
 
			if (Control == null)
			{
				var entry = CreateNativeControl();
 
				entry.EditingDidBegin += OnStarted;
				entry.EditingDidEnd += OnEnded;
 
				_picker = new UIDatePicker { Mode = UIDatePickerMode.Date, TimeZone = new NSTimeZone("UTC") };
 
				if (Forms.IsiOS14OrNewer)
				{
					_picker.PreferredDatePickerStyle = UIKit.UIDatePickerStyle.Wheels;
				}
 
				_picker.ValueChanged += HandleValueChanged;
 
				var width = UIScreen.MainScreen.Bounds.Width;
				var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true };
				var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
				var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) =>
				{
					UpdateElementDate();
					entry.ResignFirstResponder();
				});
 
				toolbar.SetItems(new[] { spacer, doneButton }, false);
 
				entry.InputView = _picker;
				entry.InputAccessoryView = toolbar;
 
				entry.InputView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
				entry.InputAccessoryView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
 
				entry.InputAssistantItem.LeadingBarButtonGroups = null;
				entry.InputAssistantItem.TrailingBarButtonGroups = null;
 
				_defaultTextColor = entry.TextColor;
 
				_useLegacyColorManagement = e.NewElement.UseLegacyColorManagement();
 
				entry.AccessibilityTraits = UIAccessibilityTrait.Button;
 
				SetNativeControl(entry);
			}
 
			UpdateDateFromModel(false);
			UpdateFont();
			UpdateMaximumDate();
			UpdateMinimumDate();
			UpdateTextColor();
			UpdateCharacterSpacing();
			UpdateFlowDirection();
		}
 
		protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			base.OnElementPropertyChanged(sender, e);
 
			if (e.PropertyName == DatePicker.DateProperty.PropertyName || e.PropertyName == DatePicker.FormatProperty.PropertyName)
			{
				UpdateDateFromModel(true);
				UpdateCharacterSpacing();
			}
			else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName)
				UpdateMinimumDate();
			else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName)
				UpdateMaximumDate();
			else if (e.PropertyName == DatePicker.CharacterSpacingProperty.PropertyName)
				UpdateCharacterSpacing();
			else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
				UpdateTextColor();
			else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
				UpdateFlowDirection();
			else if (e.PropertyName == DatePicker.FontAttributesProperty.PropertyName ||
					 e.PropertyName == DatePicker.FontFamilyProperty.PropertyName || e.PropertyName == DatePicker.FontSizeProperty.PropertyName)
			{
				UpdateFont();
			}
		}
 
		[PortHandler]
		void HandleValueChanged(object sender, EventArgs e)
		{
			if (Element.OnThisPlatform().UpdateMode() == UpdateMode.Immediately)
			{
				UpdateElementDate();
			}
		}
 
		[PortHandler]
		void OnEnded(object sender, EventArgs eventArgs)
		{
			ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
		}
 
		[PortHandler]
		void OnStarted(object sender, EventArgs eventArgs)
		{
			ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
		}
 
		[PortHandler]
		void UpdateDateFromModel(bool animate)
		{
			if (_picker.Date.ToDateTime().Date != Element.Date.Date)
				_picker.SetDate(Element.Date.ToNSDate(), animate);
 
			// Can't use Element.Format because it won't display the correct format if the region and language are set differently
			if (string.IsNullOrWhiteSpace(Element.Format) || Element.Format.Equals("d", StringComparison.OrdinalIgnoreCase))
			{
				NSDateFormatter dateFormatter = new NSDateFormatter();
				dateFormatter.TimeZone = NSTimeZone.FromGMT(0);
 
				if (Element.Format?.Equals("D", StringComparison.Ordinal) == true)
				{
					dateFormatter.DateStyle = NSDateFormatterStyle.Long;
					var strDate = dateFormatter.StringFor(_picker.Date);
					Control.Text = strDate;
				}
				else
				{
					dateFormatter.DateStyle = NSDateFormatterStyle.Short;
					var strDate = dateFormatter.StringFor(_picker.Date);
					Control.Text = strDate;
				}
			}
			else if (Element.Format.Contains('/', StringComparison.Ordinal))
			{
				Control.Text = Element.Date.ToString(Element.Format, CultureInfo.InvariantCulture);
			}
			else
			{
				Control.Text = Element.Date.ToString(Element.Format);
			}
		}
 
		[PortHandler]
		void UpdateElementDate()
		{
			ElementController.SetValueFromRenderer(DatePicker.DateProperty, _picker.Date.ToDateTime().Date);
		}
 
		void UpdateFlowDirection()
		{
			(Control as UITextField).UpdateTextAlignment(Element);
		}
 
		[PortHandler]
		protected internal virtual void UpdateFont()
		{
			Control.Font = Element.ToUIFont();
		}
 
		[PortHandler]
		void UpdateCharacterSpacing()
		{
			var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing);
 
			if (textAttr != null)
				Control.AttributedText = textAttr;
		}
 
		[PortHandler]
		void UpdateMaximumDate()
		{
			_picker.MaximumDate = Element.MaximumDate.ToNSDate();
		}
 
		[PortHandler]
		void UpdateMinimumDate()
		{
			_picker.MinimumDate = Element.MinimumDate.ToNSDate();
		}
 
		[PortHandler]
		protected internal virtual void UpdateTextColor()
		{
			var textColor = Element.TextColor;
 
			if (textColor == null || (!Element.IsEnabled && _useLegacyColorManagement))
				Control.TextColor = _defaultTextColor;
			else
				Control.TextColor = textColor.ToPlatform();
 
			// HACK This forces the color to update; there's probably a more elegant way to make this happen
			Control.Text = Control.Text;
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_disposed)
				return;
 
			_disposed = true;
 
			if (disposing)
			{
				_defaultTextColor = null;
 
				if (_picker != null)
				{
					_picker.RemoveFromSuperview();
					_picker.ValueChanged -= HandleValueChanged;
					_picker.Dispose();
					_picker = null;
				}
 
				if (Control != null)
				{
					Control.EditingDidBegin -= OnStarted;
					Control.EditingDidEnd -= OnEnded;
				}
			}
 
			base.Dispose(disposing);
		}
	}
}