--- /dev/null
+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
<Compile Include="$(MSBuildThisFileDirectory)Issue3318.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue4493.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5172.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Issue5204.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">
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;
--- /dev/null
+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;
+ }
+ }
+ }
+}
using System;
using System.ComponentModel;
+using System.Threading.Tasks;
using AppKit;
-using CoreAnimation;
-using CoreGraphics;
namespace Xamarin.Forms.Platform.MacOS
{
- public class ImageRenderer : ViewRenderer<Image, NSView>
+ public class ImageRenderer : ViewRenderer<Image, NSImageView>, IImageVisualElementRenderer
{
bool _isDisposed;
+ public ImageRenderer()
+ {
+ ImageElementManager.Init(this);
+ }
+
protected override void Dispose(bool disposing)
{
if (_isDisposed)
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();
}
}
base.Dispose(disposing);
}
- protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
+ protected override async void OnElementChanged(ElementChangedEventArgs<Image> e)
{
if (e.NewElement != null)
{
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<IImageSourceHandler>(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
<Compile Include="..\Xamarin.Forms.Platform.iOS\DisposeHelpers.cs">
<Link>DisposeHelpers.cs</Link>
</Compile>
+ <Compile Include="..\Xamarin.Forms.Platform.iOS\Renderers\ImageElementManager.cs">
+ <Link>Renderers\ImageElementManager.cs</Link>
+ </Compile>
+ <Compile Include="..\Xamarin.Forms.Platform.iOS\Renderers\IImageVisualElementRenderer.cs">
+ <Link>Renderers\IImageVisualElementRenderer.cs</Link>
+ </Compile>
+ <Compile Include="Extensions\ImageExtensions.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Xamarin.Forms.Platform\Xamarin.Forms.Platform.csproj">
-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
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
{
{
return;
}
-
+#if __MOBILE__
Control.ContentMode = imageElement.Aspect.ToUIViewContentMode();
+#else
+ Control.Layer.ContentsGravity = imageElement.Aspect.ToNSViewContentMode();
+#endif
}
public static void SetOpacity(IImageVisualElementRenderer renderer, IImageElement imageElement)
{
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)
(imageElement as IViewController)?.NativeSizeChanged();
}
- internal static async Task<UIImage> GetNativeImageAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken))
+ internal static async Task<NativeImage> GetNativeImageAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken))
{
IImageSourceHandler handler;
if (source != null && (handler = Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(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)
{
using UIKit;
using Xamarin.Forms.Internals;
using RectangleF = CoreGraphics.CGRect;
-using System.Linq;
namespace Xamarin.Forms.Platform.iOS
{