File: iOS\Renderers\TimePickerRenderer.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
{
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public class TimePickerRenderer : TimePickerRendererBase<UITextField>
	{
		[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
		public TimePickerRenderer()
		{
 
		}
 
		[PortHandler]
		protected override UITextField CreateNativeControl()
		{
			return new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
		}
	}
 
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public abstract class TimePickerRendererBase<TControl> : ViewRenderer<TimePicker, TControl>
		where TControl : UITextField
	{
		UIDatePicker _picker;
		UIColor _defaultTextColor;
		bool _disposed;
		bool _useLegacyColorManagement;
 
		internal UIDatePicker Picker => _picker;
 
		IElementController ElementController => Element as IElementController;
 
		[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
		public TimePickerRendererBase()
		{
 
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_disposed)
				return;
 
			_disposed = true;
 
			if (disposing)
			{
				_defaultTextColor = null;
 
				if (_picker != null)
				{
					_picker.RemoveFromSuperview();
					_picker.ValueChanged -= OnValueChanged;
					_picker.Dispose();
					_picker = null;
				}
 
				if (Control != null)
				{
					Control.EditingDidBegin -= OnStarted;
					Control.EditingDidEnd -= OnEnded;
				}
			}
 
			base.Dispose(disposing);
		}
 
 
		protected abstract override TControl CreateNativeControl();
 
		[PortHandler]
		protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
		{
			if (e.NewElement != null)
			{
				if (Control == null)
				{
					var entry = CreateNativeControl();
 
					entry.EditingDidBegin += OnStarted;
					entry.EditingDidEnd += OnEnded;
 
					_picker = new UIDatePicker { Mode = UIDatePickerMode.Time, TimeZone = new NSTimeZone("UTC") };
 
					if (Forms.IsiOS14OrNewer)
					{
						_picker.PreferredDatePickerStyle = UIKit.UIDatePickerStyle.Wheels;
					}
 
					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) =>
					{
						UpdateElementTime();
						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();
 
					_picker.ValueChanged += OnValueChanged;
 
					entry.AccessibilityTraits = UIAccessibilityTrait.Button;
 
					SetNativeControl(entry);
				}
 
				UpdateFont();
				UpdateTime();
				UpdateTextColor();
				UpdateCharacterSpacing();
				UpdateFlowDirection();
			}
 
			base.OnElementChanged(e);
		}
 
		protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			base.OnElementPropertyChanged(sender, e);
 
			if (e.PropertyName == TimePicker.TimeProperty.PropertyName || e.PropertyName == TimePicker.FormatProperty.PropertyName)
			{
				UpdateTime();
				UpdateCharacterSpacing();
			}
			else if (e.PropertyName == TimePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
				UpdateTextColor();
			else if (e.PropertyName == TimePicker.CharacterSpacingProperty.PropertyName)
				UpdateCharacterSpacing();
			else if (e.PropertyName == TimePicker.FontAttributesProperty.PropertyName ||
					 e.PropertyName == TimePicker.FontFamilyProperty.PropertyName || e.PropertyName == TimePicker.FontSizeProperty.PropertyName)
				UpdateFont();
			else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
				UpdateFlowDirection();
		}
 
		[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 OnValueChanged(object sender, EventArgs e)
		{
			if (Element.OnThisPlatform().UpdateMode() == UpdateMode.Immediately)
			{
				UpdateElementTime();
			}
		}
 
		void UpdateFlowDirection()
		{
			(Control as UITextField).UpdateTextAlignment(Element);
		}
 
		[PortHandler]
		protected internal virtual void UpdateFont()
		{
			Control.Font = Element.ToUIFont();
		}
 
		[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;
		}
 
		[PortHandler]
		void UpdateCharacterSpacing()
		{
			var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing);
 
			if (textAttr != null)
				Control.AttributedText = textAttr;
		}
 
		[PortHandler]
		void UpdateTime()
		{
			_picker.Date = new DateTime(1, 1, 1).Add(Element.Time).ToNSDate();
			string iOSLocale = NSLocale.CurrentLocale.CountryCode;
			var cultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures)
							  .Where(c => c.Name.EndsWith("-" + iOSLocale)).FirstOrDefault();
			if (cultureInfos == null)
				cultureInfos = CultureInfo.InvariantCulture;
 
			if (String.IsNullOrEmpty(Element.Format))
			{
				string timeformat = cultureInfos.DateTimeFormat.ShortTimePattern;
				NSLocale locale = new NSLocale(cultureInfos.TwoLetterISOLanguageName);
				Control.Text = DateTime.Today.Add(Element.Time).ToString(timeformat, cultureInfos);
				_picker.Locale = locale;
			}
			else
			{
				Control.Text = DateTime.Today.Add(Element.Time).ToString(Element.Format, cultureInfos);
			}
 
			if (Element.Format?.Contains('H', StringComparison.Ordinal) == true)
			{
				var ci = new System.Globalization.CultureInfo("de-DE");
				NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName);
				_picker.Locale = locale;
			}
			else if (Element.Format?.Contains('h', StringComparison.Ordinal) == true)
			{
				var ci = new System.Globalization.CultureInfo("en-US");
				NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName);
				_picker.Locale = locale;
			}
			Element.InvalidateMeasureNonVirtual(Controls.Internals.InvalidationTrigger.MeasureChanged);
		}
 
		[PortHandler]
		void UpdateElementTime()
		{
			ElementController.SetValueFromRenderer(TimePicker.TimeProperty, _picker.Date.ToDateTime() - new DateTime(1, 1, 1));
		}
	}
}