2016-09-15 00:56:17 +03:00
|
|
|
using System;
|
2019-01-03 20:46:40 +03:00
|
|
|
using System.Drawing;
|
2016-03-22 23:02:25 +03:00
|
|
|
using System.ComponentModel;
|
|
|
|
using System.IO;
|
|
|
|
using System.Threading;
|
2016-09-15 00:56:17 +03:00
|
|
|
using System.Threading.Tasks;
|
2016-03-22 23:02:25 +03:00
|
|
|
using Foundation;
|
2016-09-15 00:56:17 +03:00
|
|
|
using UIKit;
|
2017-04-25 21:16:25 +03:00
|
|
|
using Xamarin.Forms.Internals;
|
2016-03-22 23:02:25 +03:00
|
|
|
using RectangleF = CoreGraphics.CGRect;
|
2019-01-03 20:46:40 +03:00
|
|
|
using System.Linq;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
namespace Xamarin.Forms.Platform.iOS
|
|
|
|
{
|
|
|
|
public static class ImageExtensions
|
|
|
|
{
|
|
|
|
public static UIViewContentMode ToUIViewContentMode(this Aspect aspect)
|
|
|
|
{
|
|
|
|
switch (aspect)
|
|
|
|
{
|
|
|
|
case Aspect.AspectFill:
|
|
|
|
return UIViewContentMode.ScaleAspectFill;
|
|
|
|
case Aspect.Fill:
|
|
|
|
return UIViewContentMode.ScaleToFill;
|
|
|
|
case Aspect.AspectFit:
|
|
|
|
default:
|
|
|
|
return UIViewContentMode.ScaleAspectFit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 01:45:28 +03:00
|
|
|
public class ImageRenderer : ViewRenderer<Image, UIImageView>, IImageVisualElementRenderer
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
bool _isDisposed;
|
|
|
|
|
2018-11-04 01:45:28 +03:00
|
|
|
public ImageRenderer() : base()
|
|
|
|
{
|
|
|
|
ImageElementManager.Init(this);
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
protected override void Dispose(bool disposing)
|
|
|
|
{
|
|
|
|
if (_isDisposed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (disposing)
|
|
|
|
{
|
|
|
|
UIImage oldUIImage;
|
|
|
|
if (Control != null && (oldUIImage = Control.Image) != null)
|
|
|
|
{
|
2018-11-04 01:45:28 +03:00
|
|
|
ImageElementManager.Dispose(this);
|
2016-03-22 23:02:25 +03:00
|
|
|
oldUIImage.Dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_isDisposed = true;
|
|
|
|
|
|
|
|
base.Dispose(disposing);
|
|
|
|
}
|
|
|
|
|
2017-04-25 21:16:25 +03:00
|
|
|
protected override async void OnElementChanged(ElementChangedEventArgs<Image> e)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
if (Control == null)
|
|
|
|
{
|
|
|
|
var imageView = new UIImageView(RectangleF.Empty);
|
|
|
|
imageView.ContentMode = UIViewContentMode.ScaleAspectFit;
|
|
|
|
imageView.ClipsToBounds = true;
|
|
|
|
SetNativeControl(imageView);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.NewElement != null)
|
|
|
|
{
|
2018-11-04 01:45:28 +03:00
|
|
|
await TrySetImage(e.OldElement as Image);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
base.OnElementChanged(e);
|
|
|
|
}
|
|
|
|
|
2017-04-25 21:16:25 +03:00
|
|
|
protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
base.OnElementPropertyChanged(sender, e);
|
|
|
|
|
2018-11-04 01:45:28 +03:00
|
|
|
if (e.PropertyName == Image.SourceProperty.PropertyName)
|
|
|
|
await TrySetImage().ConfigureAwait(false);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2017-04-25 21:16:25 +03:00
|
|
|
protected virtual async Task TrySetImage(Image previous = null)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2017-04-25 21:16:25 +03:00
|
|
|
// By default we'll just catch and log any exceptions thrown by SetImage so they don't bring down
|
|
|
|
// the application; a custom renderer can override this method and handle exceptions from
|
|
|
|
// SetImage differently if it wants to
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
await SetImage(previous).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Log.Warning(nameof(ImageRenderer), "Error loading image: {0}", ex);
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
((IImageController)Element)?.SetIsLoading(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected async Task SetImage(Image oldElement = null)
|
|
|
|
{
|
2018-11-04 01:45:28 +03:00
|
|
|
await ImageElementManager.SetImage(this, Element, oldElement).ConfigureAwait(false);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2018-11-04 01:45:28 +03:00
|
|
|
void IImageVisualElementRenderer.SetImage(UIImage image) => Control.Image = image;
|
2017-04-25 21:16:25 +03:00
|
|
|
|
2018-11-04 01:45:28 +03:00
|
|
|
bool IImageVisualElementRenderer.IsDisposed => _isDisposed;
|
|
|
|
|
|
|
|
UIImageView IImageVisualElementRenderer.GetImage() => Control;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2018-11-04 01:45:28 +03:00
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
public interface IImageSourceHandler : IRegisterable
|
|
|
|
{
|
|
|
|
Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed class FileImageSourceHandler : IImageSourceHandler
|
|
|
|
{
|
|
|
|
public Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1f)
|
|
|
|
{
|
|
|
|
UIImage image = null;
|
|
|
|
var filesource = imagesource as FileImageSource;
|
2017-04-25 21:16:25 +03:00
|
|
|
var file = filesource?.File;
|
|
|
|
if (!string.IsNullOrEmpty(file))
|
|
|
|
image = File.Exists(file) ? new UIImage(file) : UIImage.FromBundle(file);
|
|
|
|
|
|
|
|
if (image == null)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2017-04-25 21:16:25 +03:00
|
|
|
Log.Warning(nameof(FileImageSourceHandler), "Could not find image: {0}", imagesource);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
2017-04-25 21:16:25 +03:00
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
return Task.FromResult(image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed class StreamImagesourceHandler : IImageSourceHandler
|
|
|
|
{
|
|
|
|
public async Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1f)
|
|
|
|
{
|
2016-09-15 00:56:17 +03:00
|
|
|
UIImage image = null;
|
2016-03-22 23:02:25 +03:00
|
|
|
var streamsource = imagesource as StreamImageSource;
|
2017-04-25 21:16:25 +03:00
|
|
|
if (streamsource?.Stream != null)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2016-05-04 16:08:54 +03:00
|
|
|
using (var streamImage = await ((IStreamImageSource)streamsource).GetStreamAsync(cancelationToken).ConfigureAwait(false))
|
2016-03-24 22:41:07 +03:00
|
|
|
{
|
|
|
|
if (streamImage != null)
|
|
|
|
image = UIImage.LoadFromData(NSData.FromStream(streamImage), scale);
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
2017-04-25 21:16:25 +03:00
|
|
|
|
|
|
|
if (image == null)
|
|
|
|
{
|
|
|
|
Log.Warning(nameof(StreamImagesourceHandler), "Could not load image: {0}", streamsource);
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
return image;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed class ImageLoaderSourceHandler : IImageSourceHandler
|
|
|
|
{
|
|
|
|
public async Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1f)
|
|
|
|
{
|
|
|
|
UIImage image = null;
|
|
|
|
var imageLoader = imagesource as UriImageSource;
|
2017-04-25 21:16:25 +03:00
|
|
|
if (imageLoader?.Uri != null)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2016-03-24 22:41:07 +03:00
|
|
|
using (var streamImage = await imageLoader.GetStreamAsync(cancelationToken).ConfigureAwait(false))
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
if (streamImage != null)
|
|
|
|
image = UIImage.LoadFromData(NSData.FromStream(streamImage), scale);
|
|
|
|
}
|
|
|
|
}
|
2017-04-25 21:16:25 +03:00
|
|
|
|
|
|
|
if (image == null)
|
|
|
|
{
|
|
|
|
Log.Warning(nameof(ImageLoaderSourceHandler), "Could not load image: {0}", imageLoader);
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
return image;
|
|
|
|
}
|
|
|
|
}
|
2019-01-03 20:46:40 +03:00
|
|
|
|
|
|
|
public sealed class FontImageSourceHandler : IImageSourceHandler
|
|
|
|
{
|
|
|
|
public Task<UIImage> LoadImageAsync(
|
|
|
|
ImageSource imagesource,
|
|
|
|
CancellationToken cancelationToken = default(CancellationToken),
|
|
|
|
float scale = 1f)
|
|
|
|
{
|
|
|
|
UIImage image = null;
|
|
|
|
var fontsource = imagesource as FontImageSource;
|
|
|
|
if (fontsource != null)
|
|
|
|
{
|
|
|
|
var iconcolor = fontsource.Color != Color.Default ? fontsource.Color : Color.White;
|
|
|
|
var imagesize = new SizeF((float)fontsource.Size, (float)fontsource.Size);
|
|
|
|
var hasFontFamily = fontsource.FontFamily != null && UIFont.FamilyNames.Contains(fontsource.FontFamily);
|
|
|
|
var font = hasFontFamily ?
|
|
|
|
UIFont.FromName(fontsource.FontFamily, (float)fontsource.Size) :
|
|
|
|
UIFont.SystemFontOfSize((float)fontsource.Size);
|
|
|
|
|
|
|
|
UIGraphics.BeginImageContextWithOptions(imagesize, false, 0f);
|
|
|
|
var attString = new NSAttributedString(fontsource.Glyph, font: font, foregroundColor: iconcolor.ToUIColor());
|
|
|
|
var ctx = new NSStringDrawingContext();
|
|
|
|
var boundingRect = attString.GetBoundingRect(imagesize, (NSStringDrawingOptions)0, ctx);
|
|
|
|
attString.DrawString(new RectangleF(
|
|
|
|
imagesize.Width / 2 - boundingRect.Size.Width / 2,
|
|
|
|
imagesize.Height / 2 - boundingRect.Size.Height / 2,
|
|
|
|
imagesize.Width,
|
|
|
|
imagesize.Height));
|
|
|
|
image = UIGraphics.GetImageFromCurrentImageContext();
|
|
|
|
UIGraphics.EndImageContext();
|
|
|
|
|
|
|
|
if (iconcolor != Color.Default)
|
|
|
|
image = image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
|
|
|
|
}
|
|
|
|
return Task.FromResult(image);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|