File: iOS\CollectionView\ItemsViewRenderer.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System.ComponentModel;
using Foundation;
using Microsoft.Maui.Controls.Platform;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
{
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public abstract class ItemsViewRenderer<TItemsView, TViewController> : ViewRenderer<TItemsView, UIView>
		where TItemsView : ItemsView
		where TViewController : ItemsViewController<TItemsView>
	{
		ItemsViewLayout _layout;
		bool _disposed;
		bool? _defaultHorizontalScrollVisibility;
		bool? _defaultVerticalScrollVisibility;
 
		protected TItemsView ItemsView => Element;
 
		public override UIViewController ViewController => Controller;
 
		protected TViewController Controller { get; private set; }
 
		[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
		protected ItemsViewRenderer()
		{
			AutoPackage = false;
		}
 
		public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
		{
			return Control.GetSizeRequest(widthConstraint, heightConstraint, 0, 0);
		}
 
		protected override void OnElementChanged(ElementChangedEventArgs<TItemsView> e)
		{
			TearDownOldElement(e.OldElement);
			SetUpNewElement(e.NewElement);
 
			base.OnElementChanged(e);
		}
 
		protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs changedProperty)
		{
			base.OnElementPropertyChanged(sender, changedProperty);
 
			if (changedProperty.Is(Microsoft.Maui.Controls.ItemsView.ItemsSourceProperty))
			{
				UpdateItemsSource();
			}
			else if (changedProperty.Is(Microsoft.Maui.Controls.ItemsView.ItemTemplateProperty))
			{
				UpdateLayout();
			}
			else if (changedProperty.IsOneOf(Microsoft.Maui.Controls.ItemsView.EmptyViewProperty,
				Microsoft.Maui.Controls.ItemsView.EmptyViewTemplateProperty))
			{
				Controller.UpdateEmptyView();
			}
			else if (changedProperty.Is(Microsoft.Maui.Controls.ItemsView.HorizontalScrollBarVisibilityProperty))
			{
				UpdateHorizontalScrollBarVisibility();
			}
			else if (changedProperty.Is(Microsoft.Maui.Controls.ItemsView.VerticalScrollBarVisibilityProperty))
			{
				UpdateVerticalScrollBarVisibility();
			}
			else if (changedProperty.Is(Microsoft.Maui.Controls.ItemsView.ItemsUpdatingScrollModeProperty))
			{
				UpdateItemsUpdatingScrollMode();
			}
			else if (changedProperty.Is(VisualElement.FlowDirectionProperty))
			{
				UpdateFlowDirection();
			}
			else if (changedProperty.Is(VisualElement.IsVisibleProperty))
			{
				UpdateVisibility();
			}
		}
 
		protected abstract ItemsViewLayout SelectLayout();
 
		protected virtual void TearDownOldElement(TItemsView oldElement)
		{
			if (oldElement == null)
			{
				return;
			}
 
			// Stop listening for ScrollTo requests
			oldElement.ScrollToRequested -= ScrollToRequested;
		}
 
		protected virtual void SetUpNewElement(TItemsView newElement)
		{
			if (newElement == null)
			{
				return;
			}
 
			UpdateLayout();
			Controller = CreateController(newElement, _layout);
			SetNativeControl(Controller.View);
			Controller.CollectionView.BackgroundColor = UIColor.Clear;
			UpdateHorizontalScrollBarVisibility();
			UpdateVerticalScrollBarVisibility();
			UpdateItemsUpdatingScrollMode();
			UpdateFlowDirection();
			UpdateVisibility();
 
			// Listen for ScrollTo requests
			newElement.ScrollToRequested += ScrollToRequested;
		}
 
		protected virtual void UpdateLayout()
		{
			_layout = SelectLayout();
 
			if (Controller != null)
			{
				Controller.UpdateLayout(_layout);
			}
		}
 
		protected virtual void UpdateItemSizingStrategy()
		{
			UpdateLayout();
		}
 
		protected virtual void UpdateItemsUpdatingScrollMode()
		{
			_layout.ItemsUpdatingScrollMode = ItemsView.ItemsUpdatingScrollMode;
		}
 
		protected virtual void UpdateFlowDirection()
		{
			if (Element == null)
			{
				return;
			}
 
			Controller.UpdateFlowDirection();
		}
 
		protected virtual void UpdateItemsSource()
		{
			UpdateItemsUpdatingScrollMode();
			Controller.UpdateItemsSource();
		}
 
		protected virtual void UpdateVisibility()
		{
			Controller?.UpdateVisibility();
		}
 
		protected abstract TViewController CreateController(TItemsView newElement, ItemsViewLayout layout);
 
		NSIndexPath DetermineIndex(ScrollToRequestEventArgs args)
		{
			if (args.Mode == ScrollToMode.Position)
			{
				if (args.GroupIndex == -1)
				{
					return NSIndexPath.Create(0, args.Index);
				}
 
				return NSIndexPath.Create(args.GroupIndex, args.Index);
			}
 
			return Controller.GetIndexForItem(args.Item);
		}
 
		void UpdateVerticalScrollBarVisibility()
		{
			if (_defaultVerticalScrollVisibility == null)
				_defaultVerticalScrollVisibility = Controller.CollectionView.ShowsVerticalScrollIndicator;
 
			switch (Element.VerticalScrollBarVisibility)
			{
				case ScrollBarVisibility.Always:
					Controller.CollectionView.ShowsVerticalScrollIndicator = true;
					break;
				case ScrollBarVisibility.Never:
					Controller.CollectionView.ShowsVerticalScrollIndicator = false;
					break;
				case ScrollBarVisibility.Default:
					Controller.CollectionView.ShowsVerticalScrollIndicator = _defaultVerticalScrollVisibility.Value;
					break;
			}
		}
 
		void UpdateHorizontalScrollBarVisibility()
		{
			if (_defaultHorizontalScrollVisibility == null)
				_defaultHorizontalScrollVisibility = Controller.CollectionView.ShowsHorizontalScrollIndicator;
 
			switch (Element.HorizontalScrollBarVisibility)
			{
				case ScrollBarVisibility.Always:
					Controller.CollectionView.ShowsHorizontalScrollIndicator = true;
					break;
				case ScrollBarVisibility.Never:
					Controller.CollectionView.ShowsHorizontalScrollIndicator = false;
					break;
				case ScrollBarVisibility.Default:
					Controller.CollectionView.ShowsHorizontalScrollIndicator = _defaultHorizontalScrollVisibility.Value;
					break;
			}
		}
 
		protected virtual void ScrollToRequested(object sender, ScrollToRequestEventArgs args)
		{
			using (var indexPath = DetermineIndex(args))
			{
				if (!IsIndexPathValid(indexPath))
				{
					// Specified path wasn't valid, or item wasn't found
					return;
				}
 
				Controller.CollectionView.ScrollToItem(indexPath,
					args.ScrollToPosition.ToCollectionViewScrollPosition(_layout.ScrollDirection), args.IsAnimated);
			}
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_disposed)
			{
				return;
			}
 
			_disposed = true;
 
			if (disposing)
			{
				TearDownOldElement(Element);
 
				Controller?.Dispose();
				Controller = null;
			}
 
			base.Dispose(disposing);
		}
 
		protected bool IsIndexPathValid(NSIndexPath indexPath)
		{
			if (indexPath.Item < 0 || indexPath.Section < 0)
			{
				return false;
			}
 
			var collectionView = Controller.CollectionView;
			if (indexPath.Section >= collectionView.NumberOfSections())
			{
				return false;
			}
 
			if (indexPath.Item >= collectionView.NumberOfItemsInSection(indexPath.Section))
			{
				return false;
			}
 
			return true;
		}
	}
}