From: Rui Marinho Date: Tue, 19 Mar 2019 18:10:10 +0000 (+0000) Subject: [MacOS] Move ImageRenderer to use NSImageView (#5600) X-Git-Tag: accepted/tizen/5.5/unified/20200421.150457~462 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ce148bf4c81f7d1da5ef74c7a2a43cca6ca73331;p=platform%2Fcore%2Fcsapi%2Fxsf.git [MacOS] Move ImageRenderer to use NSImageView (#5600) * IMAGE https://github.com/xamarin/Xamarin.Forms/issues/5204 * Updated "no image source" behavior - return base value * Added test case * Updated test case number * fixed file name * [MacOS] Move ImageRenderer to use NSImageView * [Macos,iOS] Fix sharing coding --- diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5204.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5204.cs new file mode 100644 index 0000000..234d347 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5204.cs @@ -0,0 +1,32 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; + +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 5204, "[MacOS] Image size issue (not rendered if skip setting WidthRequest and HeightRequest", PlatformAffected.macOS)] + public class Issue5204 : TestContentPage + { + protected override void Init() + { + Title = "You should see image"; + Content = new StackLayout + { + VerticalOptions = LayoutOptions.CenterAndExpand, + HorizontalOptions = LayoutOptions.CenterAndExpand, + Children = { + new Image + { + BackgroundColor = Color.Black, + Source = "https://user-images.githubusercontent.com/10124814/53306353-27302b80-389d-11e9-98ce-690db32f1ee3.jpg" + } + } + }; + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index 667b86a..652f814 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -894,6 +894,7 @@ + diff --git a/Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs b/Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs index 6d7cd3c..20d2560 100644 --- a/Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs +++ b/Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs @@ -1,19 +1,14 @@ using System; using AppKit; using CoreAnimation; +using CoreGraphics; namespace Xamarin.Forms.Platform.MacOS { - internal class FormsNSImageView : NSView + internal class FormsNSImageView : NSImageView { bool _isOpaque; - public FormsNSImageView() - { - Layer = new CALayer(); - WantsLayer = true; - } - public void SetIsOpaque(bool isOpaque) { _isOpaque = isOpaque; diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/ImageExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/ImageExtensions.cs new file mode 100644 index 0000000..cadda0c --- /dev/null +++ b/Xamarin.Forms.Platform.MacOS/Extensions/ImageExtensions.cs @@ -0,0 +1,23 @@ +using System; +using CoreAnimation; +using Foundation; + +namespace Xamarin.Forms.Platform.MacOS +{ + public static class ImageExtensions + { + public static NSString ToNSViewContentMode(this Aspect aspect) + { + switch (aspect) + { + case Aspect.AspectFill: + return CALayer.GravityResizeAspectFill; + case Aspect.Fill: + return CALayer.GravityResize; + case Aspect.AspectFit: + default: + return CALayer.GravityResizeAspect; + } + } + } +} diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs index 8450cf4..8b22895 100644 --- a/Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs +++ b/Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs @@ -1,15 +1,19 @@ using System; using System.ComponentModel; +using System.Threading.Tasks; using AppKit; -using CoreAnimation; -using CoreGraphics; namespace Xamarin.Forms.Platform.MacOS { - public class ImageRenderer : ViewRenderer + public class ImageRenderer : ViewRenderer, IImageVisualElementRenderer { bool _isDisposed; + public ImageRenderer() + { + ImageElementManager.Init(this); + } + protected override void Dispose(bool disposing) { if (_isDisposed) @@ -17,9 +21,10 @@ namespace Xamarin.Forms.Platform.MacOS if (disposing) { - CGImage oldUIImage; - if (Control != null && (oldUIImage = Control.Layer.Contents) != null) + NSImage oldUIImage; + if (Control != null && (oldUIImage = Control.Image) != null) { + ImageElementManager.Dispose(this); oldUIImage.Dispose(); } } @@ -29,7 +34,7 @@ namespace Xamarin.Forms.Platform.MacOS base.Dispose(disposing); } - protected override void OnElementChanged(ElementChangedEventArgs e) + protected override async void OnElementChanged(ElementChangedEventArgs e) { if (e.NewElement != null) { @@ -38,94 +43,50 @@ namespace Xamarin.Forms.Platform.MacOS var imageView = new FormsNSImageView(); SetNativeControl(imageView); } - SetAspect(); - SetImage(e.OldElement); - SetOpacity(); + + await TrySetImage(e.OldElement as Image); } base.OnElementChanged(e); } - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); + if (e.PropertyName == Image.SourceProperty.PropertyName) - SetImage(); - else if (e.PropertyName == Image.IsOpaqueProperty.PropertyName) - SetOpacity(); - else if (e.PropertyName == Image.AspectProperty.PropertyName) - SetAspect(); + await TrySetImage().ConfigureAwait(false); } - void SetAspect() + protected virtual async Task TrySetImage(Image previous = null) { - switch (Element.Aspect) + // 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 { - case Aspect.AspectFill: - Control.Layer.ContentsGravity = CALayer.GravityResizeAspectFill; - break; - case Aspect.Fill: - Control.Layer.ContentsGravity = CALayer.GravityResize; - break; - case Aspect.AspectFit: - default: - Control.Layer.ContentsGravity = CALayer.GravityResizeAspect; - break; + await SetImage(previous).ConfigureAwait(false); } - } - - async void SetImage(Image oldElement = null) - { - var source = Element.Source; - - if (oldElement != null) + catch (Exception ex) { - var oldSource = oldElement.Source; - if (Equals(oldSource, source)) - return; - - var imageSource = oldSource as FileImageSource; - if (imageSource != null && source is FileImageSource && imageSource.File == ((FileImageSource)source).File) - return; - - Control.Layer.Contents = null; + Internals.Log.Warning(nameof(ImageRenderer), "Error loading image: {0}", ex); } - - IImageSourceHandler handler; - - Element.SetIsLoading(true); - - if (source != null && (handler = Internals.Registrar.Registered.GetHandlerForObject(source)) != null) + finally { - NSImage nsImage; - try - { - nsImage = await handler.LoadImageAsync(source, scale: (float)NSScreen.MainScreen.BackingScaleFactor); - } - catch (OperationCanceledException) - { - nsImage = null; - } - - var imageView = Control; - if (imageView != null) - imageView.Layer.Contents = nsImage != null ? nsImage.CGImage : null; - if (nsImage != null) - nsImage.Dispose(); - - if (!_isDisposed) - ((IVisualElementController)Element).NativeSizeChanged(); + ((IImageController)Element)?.SetIsLoading(false); } - else - Control.Layer.Contents = null; - - if (!_isDisposed) - Element.SetIsLoading(false); } - void SetOpacity() + protected async Task SetImage(Image oldElement = null) { - (Control as FormsNSImageView)?.SetIsOpaque(Element.IsOpaque); + await ImageElementManager.SetImage(this, Element, oldElement).ConfigureAwait(false); } + + void IImageVisualElementRenderer.SetImage(NSImage image) => Control.Image = image; + + bool IImageVisualElementRenderer.IsDisposed => _isDisposed; + + NSImageView IImageVisualElementRenderer.GetImage() => Control; } } \ No newline at end of file diff --git a/Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj b/Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj index 15dd3ce..6f45df7 100644 --- a/Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj +++ b/Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj @@ -241,6 +241,13 @@ DisposeHelpers.cs + + Renderers\ImageElementManager.cs + + + Renderers\IImageVisualElementRenderer.cs + + diff --git a/Xamarin.Forms.Platform.iOS/Renderers/IImageVisualElementRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/IImageVisualElementRenderer.cs index 59f9b24..b8e9153 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/IImageVisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/IImageVisualElementRenderer.cs @@ -1,11 +1,17 @@ -using UIKit; - +#if __MOBILE__ +using NativeImageView = UIKit.UIImageView; +using NativeImage = UIKit.UIImage; namespace Xamarin.Forms.Platform.iOS +#else +using NativeImageView = AppKit.NSImageView; +using NativeImage = AppKit.NSImage; +namespace Xamarin.Forms.Platform.MacOS +#endif { public interface IImageVisualElementRenderer : IVisualNativeElementRenderer { - void SetImage(UIImage image); + void SetImage(NativeImage image); bool IsDisposed { get; } - UIImageView GetImage(); + NativeImageView GetImage(); } } \ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ImageElementManager.cs b/Xamarin.Forms.Platform.iOS/Renderers/ImageElementManager.cs index 70283fc..38ba61f 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/ImageElementManager.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/ImageElementManager.cs @@ -1,14 +1,18 @@ using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using Foundation; -using UIKit; +#if __MOBILE__ +using UIKit; +using NativeImage = UIKit.UIImage; namespace Xamarin.Forms.Platform.iOS +#else +using AppKit; +using CoreAnimation; +using NativeImage = AppKit.NSImage; +namespace Xamarin.Forms.Platform.MacOS +#endif { public static class ImageElementManager { @@ -70,8 +74,11 @@ namespace Xamarin.Forms.Platform.iOS { return; } - +#if __MOBILE__ Control.ContentMode = imageElement.Aspect.ToUIViewContentMode(); +#else + Control.Layer.ContentsGravity = imageElement.Aspect.ToNSViewContentMode(); +#endif } public static void SetOpacity(IImageVisualElementRenderer renderer, IImageElement imageElement) @@ -83,8 +90,11 @@ namespace Xamarin.Forms.Platform.iOS { return; } - +#if __MOBILE__ Control.Opaque = imageElement.IsOpaque; +#else + (Control as FormsNSImageView)?.SetIsOpaque(imageElement.IsOpaque); +#endif } public static async Task SetImage(IImageVisualElementRenderer renderer, IImageElement imageElement, Image oldElement = null) @@ -133,14 +143,19 @@ namespace Xamarin.Forms.Platform.iOS (imageElement as IViewController)?.NativeSizeChanged(); } - internal static async Task GetNativeImageAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken)) + internal static async Task GetNativeImageAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken)) { IImageSourceHandler handler; if (source != null && (handler = Internals.Registrar.Registered.GetHandlerForObject(source)) != null) { try { - return await handler.LoadImageAsync(source, scale: (float)UIScreen.MainScreen.Scale, cancelationToken: cancellationToken); +#if __MOBILE__ + float scale = (float)UIScreen.MainScreen.Scale; +#else + float scale = (float)NSScreen.MainScreen.BackingScaleFactor; +#endif + return await handler.LoadImageAsync(source, scale: scale, cancelationToken: cancellationToken); } catch (OperationCanceledException ex) { diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs index d337b85..46873f5 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs @@ -8,7 +8,6 @@ using Foundation; using UIKit; using Xamarin.Forms.Internals; using RectangleF = CoreGraphics.CGRect; -using System.Linq; namespace Xamarin.Forms.Platform.iOS {