File: iOS\Renderers\RefreshViewRenderer.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System;
using System.ComponentModel;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Graphics;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
{
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public class RefreshViewRenderer : ViewRenderer<RefreshView, UIView>, IEffectControlProvider
	{
		bool _isDisposed;
		bool _isRefreshing;
		bool _usingLargeTitles;
		nfloat _originalY;
		nfloat _refreshControlHeight;
		UIView _refreshControlParent;
		UIRefreshControl _refreshControl;
 
		public bool IsRefreshing
		{
			get { return _isRefreshing; }
			set
			{
				_isRefreshing = value;
 
				if (Element != null && Element.IsRefreshing != _isRefreshing)
					Element.SetValueFromRenderer(RefreshView.IsRefreshingProperty, _isRefreshing);
 
				if (_isRefreshing != _refreshControl.Refreshing)
				{
					if (_isRefreshing)
					{
						TryOffsetRefresh(this, IsRefreshing);
						_refreshControl.BeginRefreshing();
					}
					else
					{
						_refreshControl.EndRefreshing();
						TryOffsetRefresh(this, IsRefreshing);
					}
				}
			}
		}
 
		[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
		public RefreshViewRenderer()
		{
 
		}
 
		protected override void OnElementChanged(ElementChangedEventArgs<RefreshView> e)
		{
			base.OnElementChanged(e);
 
			if (e.OldElement != null || Element == null)
				return;
 
			if (e.NewElement != null)
			{
				if (Control == null)
				{
					if (Forms.IsiOS11OrNewer)
					{
						var parentNav = e.NewElement.FindParentOfType<NavigationPage>();
						_usingLargeTitles = parentNav != null && parentNav.OnThisPlatform().PrefersLargeTitles();
					}
 
					_refreshControl = new UIRefreshControl();
					_refreshControl.ValueChanged += OnRefresh;
					_refreshControlParent = this;
				}
			}
 
			UpdateColors();
			UpdateIsEnabled();
			UpdateIsRefreshing();
		}
 
		protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			base.OnElementPropertyChanged(sender, e);
 
			if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
				UpdateIsEnabled();
			else if (e.PropertyName == RefreshView.IsRefreshingProperty.PropertyName)
				UpdateIsRefreshing();
			else if (e.IsOneOf(RefreshView.RefreshColorProperty, VisualElement.BackgroundColorProperty))
				UpdateColors();
		}
 
		protected override void SetBackgroundColor(Color color)
		{
			if (_refreshControl == null)
				return;
 
			_refreshControl.BackgroundColor = color?.ToPlatform();
		}
 
		protected override void SetBackground(Brush brush)
		{
			if (_refreshControl == null)
				return;
 
			_refreshControl.UpdateBackground(brush);
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_isDisposed)
				return;
 
			if (disposing && Control != null)
			{
				_refreshControl.ValueChanged -= OnRefresh;
				_refreshControl.Dispose();
				_refreshControl = null;
				_refreshControlParent = null;
			}
 
			_isDisposed = true;
			base.Dispose(disposing);
		}
 
		bool TryOffsetRefresh(UIView view, bool refreshing)
		{
			if (view is UIScrollView scrollView)
			{
				if (scrollView.ContentOffset.Y < 0)
					return true;
 
				if (refreshing)
					scrollView.SetContentOffset(new CoreGraphics.CGPoint(0, _originalY - _refreshControlHeight), true);
				else
					scrollView.SetContentOffset(new CoreGraphics.CGPoint(0, _originalY), true);
 
				return true;
			}
 
			if (view is WkWebViewRenderer)
			{
				return true;
			}
 
			if (view.Subviews == null)
				return false;
 
			for (int i = 0; i < view.Subviews.Length; i++)
			{
				var control = view.Subviews[i];
				if (TryOffsetRefresh(control, refreshing))
					return true;
			}
 
			return false;
		}
 
		bool TryRemoveRefresh(UIView view, int index = 0)
		{
			_refreshControlParent = view;
 
			if (_refreshControl.Superview != null)
				_refreshControl.RemoveFromSuperview();
 
			if (view is UIScrollView scrollView)
			{
				if (CanUseRefreshControlProperty())
					scrollView.RefreshControl = null;
 
				return true;
			}
 
			if (view.Subviews == null)
				return false;
 
			for (int i = 0; i < view.Subviews.Length; i++)
			{
				var control = view.Subviews[i];
				if (TryRemoveRefresh(control, i))
					return true;
			}
 
			return false;
		}
 
		bool TryInsertRefresh(UIView view, int index = 0)
		{
			_refreshControlParent = view;
 
			if (view is UIScrollView scrollView)
			{
				if (CanUseRefreshControlProperty())
					scrollView.RefreshControl = _refreshControl;
				else
					scrollView.InsertSubview(_refreshControl, index);
 
				scrollView.AlwaysBounceVertical = true;
 
				_originalY = scrollView.ContentOffset.Y;
				_refreshControlHeight = _refreshControl.Frame.Size.Height;
 
				return true;
			}
 
			if (view is WkWebViewRenderer webView)
			{
				webView.ScrollView.InsertSubview(_refreshControl, index);
				return true;
			}
 
			if (view.Subviews == null)
				return false;
 
			for (int i = 0; i < view.Subviews.Length; i++)
			{
				var control = view.Subviews[i];
				if (TryInsertRefresh(control, i))
					return true;
			}
 
			return false;
		}
 
		void UpdateColors()
		{
			if (Element == null || _refreshControl == null)
				return;
 
			if (Element.RefreshColor != null)
				_refreshControl.TintColor = Element.RefreshColor.ToPlatform();
 
			SetBackgroundColor(Element.BackgroundColor);
			SetBackground(Element.Background);
		}
 
		void UpdateIsRefreshing()
		{
			IsRefreshing = Element.IsRefreshing;
		}
 
		void UpdateIsEnabled()
		{
			bool isRefreshViewEnabled = Element.IsEnabled;
			_refreshControl.Enabled = isRefreshViewEnabled;
 
			UserInteractionEnabled = true;
 
			if (IsRefreshing)
				return;
 
			if (isRefreshViewEnabled)
				TryInsertRefresh(_refreshControlParent);
			else
				TryRemoveRefresh(_refreshControlParent);
 
			UserInteractionEnabled = true;
		}
 
		bool CanUseRefreshControlProperty()
		{
			return !_usingLargeTitles;
		}
 
		void OnRefresh(object sender, EventArgs e)
		{
			IsRefreshing = true;
		}
 
		void IEffectControlProvider.RegisterEffect(Effect effect)
		{
			VisualElementRenderer<VisualElement>.RegisterEffect(effect, this, NativeView);
		}
	}
}