File: Platform\iOS\ImageViewExtensions.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)

using System;
using System.Threading;
using System.Threading.Tasks;
using CoreGraphics;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Platform
{
	public static class ImageViewExtensions
	{
		public static void Clear(this UIImageView imageView)
		{
			// stop the animation if there is one
			imageView.StopAnimating();
			imageView.AnimationImages = null;
			imageView.Image = null;
		}
 
		public static void UpdateAspect(this UIImageView imageView, IImage image)
		{
			imageView.ContentMode = image.Aspect.ToUIViewContentMode();
			imageView.ClipsToBounds = imageView.ContentMode == UIViewContentMode.ScaleAspectFill || imageView.ContentMode == UIViewContentMode.Center;
		}
 
		public static void UpdateIsAnimationPlaying(this UIImageView imageView, IImageSourcePart image)
		{
			if (image.IsAnimationPlaying)
			{
				if (!imageView.IsAnimating)
					imageView.StartAnimating();
			}
			else
			{
				if (imageView.IsAnimating)
					imageView.StopAnimating();
			}
		}
 
		// TODO: This method does not appear to be used, should we obsolete in net9?
		public static void UpdateSource(this UIImageView imageView, UIImage? uIImage, IImageSourcePart image)
		{
			imageView.Image = uIImage;
			imageView.UpdateIsAnimationPlaying(image);
		}
 
		// TODO: This method does not appear to be used, should we obsolete in net9?
		public static Task<IImageSourceServiceResult<UIImage>?> UpdateSourceAsync(
			this UIImageView imageView,
			IImageSourcePart image,
			IImageSourceServiceProvider services,
			CancellationToken cancellationToken = default)
		{
			float scale = imageView.Window?.GetDisplayDensity() ?? 1.0f;
 
			imageView.Clear();
			return image.UpdateSourceAsync(imageView, services, (uiImage) =>
			{
				imageView.Image = uiImage;
			}, scale, cancellationToken);
		}
 
		/// <summary>
		/// Gets the size that fits on the screen for a <see cref="UIImageView"/> to be consistent cross-platform.
		/// </summary>
		/// <remarks>The default iOS implementation of SizeThatFits only returns the image's dimensions and ignores the constraints.</remarks>
		/// <param name="imageView">The <see cref="UIImageView"/> to be measured.</param>
		/// <param name="constraints">The specified size constraints.</param>
		/// <param name="padding"></param>
		/// <returns>The size where the image would fit depending on the aspect ratio.</returns>
		internal static CGSize SizeThatFitsImage(
			this UIImageView imageView,
			CGSize constraints,
			Thickness padding = default)
		{
			// If there's no image, we don't need to take up any space
			if (imageView.Image is null)
			{
				return new CGSize(0, 0);
			}
 
			CGSize imageSize = imageView.Image.Size;
			double imageWidth = imageSize.Width;
			double imageHeight = imageSize.Height;
 
			var horizontalThickness = padding.HorizontalThickness;
			var verticalThickness = padding.VerticalThickness;
 
			var widthConstraint = constraints.Width - horizontalThickness;
			var heightConstraint = constraints.Height - verticalThickness;
 
 
			var constrainedWidth = Math.Min(imageWidth, widthConstraint);
			var constrainedHeight = Math.Min(imageHeight, heightConstraint);
 
			// In cases where we the image must fit its given constraints, we must shrink based on the smallest dimension (scale factor)
			// that can fit it
			if (imageView.ContentMode == UIViewContentMode.ScaleAspectFit)
			{
				var widthRatio = constrainedWidth / imageWidth;
				var heightRatio = constrainedHeight / imageHeight;
				var scaleFactor = Math.Min(widthRatio, heightRatio);
 
				return new CGSize(imageWidth * scaleFactor + horizontalThickness, imageHeight * scaleFactor + verticalThickness);
			}
 
			// Cases where AspectMode is ScaleToFill or Center
			return new CGSize(constrainedWidth + horizontalThickness, constrainedHeight + verticalThickness);
		}
	}
}