File: iOS\ViewRenderer.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System;
using System.ComponentModel;
 
using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls.Platform;
 
#if __MOBILE__
using ObjCRuntime;
using UIKit;
using NativeColor = UIKit.UIColor;
using NativeControl = UIKit.UIControl;
using NativeView = UIKit.UIView;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
#else
using NativeView = AppKit.NSView;
using NativeColor = CoreGraphics.CGColor;
using NativeControl = AppKit.NSControl;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.MacOS
#endif
{
	public interface ITabStop
	{
		NativeView TabStop { get; }
	}
 
	[Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.ViewRenderer instead")]
	public abstract class ViewRenderer : ViewRenderer<View, NativeView>
	{
	}
 
	[Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.ViewRenderer instead")]
	public abstract class ViewRenderer<TView, TNativeView> : VisualElementRenderer<TView>, IVisualNativeElementRenderer, ITabStop where TView : View where TNativeView : NativeView
	{
		string _defaultAccessibilityLabel;
		string _defaultAccessibilityHint;
		bool? _defaultIsAccessibilityElement;
		bool? _defaultAccessibilityElementsHidden;
 
		NativeColor _defaultColor;
 
		event EventHandler<PropertyChangedEventArgs> _elementPropertyChanged;
		event EventHandler _controlChanging;
		event EventHandler _controlChanged;
 
		private protected bool IsElementOrControlEmpty => Element == null || Control == null;
 
		protected virtual TNativeView CreateNativeControl()
		{
			return default(TNativeView);
		}
 
		public TNativeView Control { get; private set; }
		NativeView IVisualNativeElementRenderer.Control => Control;
 
 
		event EventHandler<PropertyChangedEventArgs> IVisualNativeElementRenderer.ElementPropertyChanged
		{
			add { _elementPropertyChanged += value; }
			remove { _elementPropertyChanged -= value; }
		}
 
		event EventHandler IVisualNativeElementRenderer.ControlChanging
		{
			add { _controlChanging += value; }
			remove { _controlChanging -= value; }
		}
		event EventHandler IVisualNativeElementRenderer.ControlChanged
		{
			add { _controlChanged += value; }
			remove { _controlChanged -= value; }
		}
 
 
		NativeView ITabStop.TabStop => Control;
#if __MOBILE__
		public override void LayoutSubviews()
		{
			base.LayoutSubviews();
			if (Control != null && Element != null)
				Control.Frame = new RectangleF(0, 0, (nfloat)Element.Width, (nfloat)Element.Height);
		}
 
		public override SizeF SizeThatFits(SizeF size)
		{
			if (Control == null)
				return (base.SizeThatFits(size));
 
			return Control.SizeThatFits(size);
		}
#else
		public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
		{
			return (Control ?? NativeView).GetSizeRequest(widthConstraint, heightConstraint);
		}
 
		public override void Layout()
		{
			if (Control != null)
				Control.Frame = new RectangleF(0, 0, (nfloat)Element.Width, (nfloat)Element.Height);
			base.Layout();
		}
#endif
 
		/// <summary>
		/// Determines whether the native control is disposed of when this renderer is disposed
		/// Can be overridden in deriving classes 
		/// </summary>
		protected virtual bool ManageNativeControlLifetime => true;
 
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				_elementPropertyChanged = null;
				_controlChanging = null;
				_controlChanged = null;
 
				if (Control != null && ManageNativeControlLifetime)
				{
					Control.RemoveFromSuperview();
					Control.Dispose();
					Control = null;
				}
			}
 
			base.Dispose(disposing);
		}
 
		protected override void OnElementChanged(ElementChangedEventArgs<TView> e)
		{
			base.OnElementChanged(e);
 
			if (e.OldElement != null)
				e.OldElement.FocusChangeRequested -= ViewOnFocusChangeRequested;
 
			if (e.NewElement != null)
			{
				if (Control != null && e.OldElement != null && e.OldElement.BackgroundColor != e.NewElement.BackgroundColor || e.NewElement.BackgroundColor != null)
					SetBackgroundColor(e.NewElement.BackgroundColor);
 
				if (Control != null && e.OldElement != null && e.OldElement.Background != e.NewElement.Background)
					SetBackground(e.NewElement.Background);
 
				e.NewElement.FocusChangeRequested += ViewOnFocusChangeRequested;
			}
 
			UpdateIsEnabled();
			UpdateFlowDirection();
		}
 
		protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (Control != null)
			{
				if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
					UpdateIsEnabled();
				else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
					SetBackgroundColor(Element.BackgroundColor);
				else if (e.PropertyName == VisualElement.BackgroundProperty.PropertyName)
					SetBackground(Element.Background);
				else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
					UpdateFlowDirection();
			}
 
			base.OnElementPropertyChanged(sender, e);
			_elementPropertyChanged?.Invoke(this, e);
		}
 
		protected override void OnRegisterEffect(PlatformEffect effect)
		{
			base.OnRegisterEffect(effect);
			effect.Control = Control;
		}
 
		protected override void SetAccessibilityHint()
		{
			_defaultAccessibilityHint = Control.SetAccessibilityHint(Element, _defaultAccessibilityHint);
		}
 
		protected override void SetAccessibilityLabel()
		{
			_defaultAccessibilityLabel = Control.SetAccessibilityLabel(Element, _defaultAccessibilityLabel);
		}
 
		protected override void SetIsAccessibilityElement()
		{
			_defaultIsAccessibilityElement = Control.SetIsAccessibilityElement(Element, _defaultIsAccessibilityElement);
		}
		protected override void SetAccessibilityElementsHidden()
		{
			_defaultAccessibilityElementsHidden = Control.SetAccessibilityElementsHidden(Element, _defaultAccessibilityElementsHidden);
		}
 
		protected override void SetAutomationId(string id)
		{
			if (Control == null)
				base.SetAutomationId(id);
			else
			{
				AccessibilityIdentifier = id + "_Container";
				Control.AccessibilityIdentifier = id;
			}
		}
 
		protected override void SetBackgroundColor(Color color)
		{
			if (IsElementOrControlEmpty)
				return;
#if __MOBILE__
			if (color == null)
				Control.BackgroundColor = _defaultColor;
			else
				Control.BackgroundColor = color.ToPlatform();
#else
			Control.Layer.BackgroundColor = color == null ? _defaultColor : color.ToCGColor();
#endif
		}
 
		protected override void SetBackground(Brush brush)
		{
			if (IsElementOrControlEmpty)
				return;
 
			Control.UpdateBackground(brush);
		}
 
		protected void SetNativeControl(TNativeView uiview)
		{
			_controlChanging?.Invoke(this, EventArgs.Empty);
#if __MOBILE__
			_defaultColor = uiview.BackgroundColor;
#else
			uiview.WantsLayer = true;
			_defaultColor = uiview.Layer.BackgroundColor;
#endif
			Control = uiview;
 
			UpdateBackgroundColor();
 
			UpdateBackground();
 
			UpdateIsEnabled();
 
			UpdateFlowDirection();
 
			AddSubview(uiview);
 
			_controlChanged?.Invoke(this, EventArgs.Empty);
		}
 
#if __MOBILE__
		public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
		{
#pragma warning disable CA1422 // Validate platform compatibility
			base.TraitCollectionDidChange(previousTraitCollection);
#pragma warning restore CA1422 // Validate platform compatibility			// Make sure the control adheres to changes in UI theme
			if (Forms.IsiOS13OrNewer && previousTraitCollection?.UserInterfaceStyle != TraitCollection.UserInterfaceStyle)
				Control?.SetNeedsDisplay();
		}
 
		internal override void SendVisualElementInitialized(VisualElement element, NativeView nativeView)
		{
			base.SendVisualElementInitialized(element, Control);
		}
#endif
 
		void UpdateBackgroundColor()
		{
			if (IsElementOrControlEmpty)
				return;
 
			if (Element.BackgroundColor != null)
				SetBackgroundColor(Element.BackgroundColor);
		}
 
		void UpdateBackground()
		{
			if (IsElementOrControlEmpty)
				return;
 
			Brush brush = Element.Background;
 
			if (!Brush.IsNullOrEmpty(brush))
				SetBackground(brush);
		}
 
		void UpdateIsEnabled()
		{
			if (IsElementOrControlEmpty)
				return;
 
			var uiControl = Control as NativeControl;
			if (uiControl == null)
				return;
			uiControl.Enabled = Element.IsEnabled;
		}
 
		[PortHandler]
		void UpdateFlowDirection()
		{
			if (IsElementOrControlEmpty)
				return;
 
			Control.UpdateFlowDirection(Element);
		}
 
		[PortHandler]
		void ViewOnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs focusRequestArgs)
		{
			if (Control == null)
				return;
 
#if __MOBILE__
			focusRequestArgs.Result = focusRequestArgs.Focus ? Control.BecomeFirstResponder() : Control.ResignFirstResponder();
#else
			focusRequestArgs.Result = focusRequestArgs.Focus ? Control.Window.MakeFirstResponder(Control) : Control.Window.MakeFirstResponder(null);
#endif
		}
	}
}