MonoTouch:双倍外观在Retina显示屏上调整色调时的图像大小

我使用在Retina和非Retina显示器中工作良好的代码来设置NavBar的背景。 有一个@ 2x和正常的图像。 所以,一切都好:

UINavigationBar.Appearance.SetBackgroundImage( GetImage(ImageTheme.menubar), UIBarMetrics.Default); 

现在,当我将这个ChangeHue()变换应用到图像以调整其色调时,在Retina上显示图像的大小是它的两倍。 非Retina显示器是好的:

  UINavigationBar.Appearance.SetBackgroundImage( ChangeHue(GetImage(ImageTheme.menubar)), UIBarMetrics.Default); ... UIImage ChangeHue(UIImage originalImage){ var hueAdjust = new CIHueAdjust() { Image = CIImage.FromCGImage(originalImage.CGImage), Angle = hue * (float)Math.PI / 180f // angles to radians }; var output = hueAdjust.OutputImage; var context = CIContext.FromOptions(null); var cgimage = context.CreateCGImage(output, output.Extent); var i = UIImage.FromImage(cgimage); return i; } 

这是应用色相后的非视网膜和视网膜显示的结果:

非Retina

视网膜

忽略这些HACK,然后在ChangeHue方法中编辑这一行:

 var i = UIImage.FromImage(cgimage); 

改为这样做:

 float scale = 1f; if (UIScreen.MainScreen.RespondsToSelector (new MonoTouch.ObjCRuntime.Selector ("scale"))) { scale = UIScreen.MainScreen.Scale; // will be 2.0 for Retina } var i = new UIImage(cgimage, scale, UIImageOrientation.Up); 

这应该返回一个UIImage对象, UIImage对象具有正确的“比例”信息,以正确显示在UINavigationBar

我从来没有尝试CoreImage,但对于CoreGraphics,你需要使用UIGraphics.BeginImageContextWithOptions和指定为0缩放(所以这将自动完成视网膜和非视网膜显示)。

所以我会尝试的第一件事是取代你的:

 var context = CIContext.FromOptions(null); 

与以下块:

 UIGraphics.BeginImageContextWithOptions (new SizeF (size, size), false, 0); using (var c = UIGraphics.GetCurrentContext ()) { var context = CIContext.FromContext (c); ... } UIGraphics.EndImageContext (); 

更新 :从iOS上不能使用FromContext (这是特定于OSX),所以上面的代码将无法正常工作。

MonoTouch团队提出了一个错误。 很快就会发布解决scheme。

我现在有三个“黑客”build议:

黑客#1

通过replace这一行来强制修剪图像:

 var cgimage = context.CreateCGImage(output, output.Extent); 

有了这个:

 var extent = output.Extent; if (UIScreen.MainScreen.RespondsToSelector (new MonoTouch.ObjCRuntime.Selector("scale"))) { if (UIScreen.MainScreen.Scale == 2f) { extent = new System.Drawing.RectangleF(extent.X, extent.Y, extent.Width / 2f, extent.Height / 2f); } } var cgimage = context.CreateCGImage(output, extent); 

但是,如果在调整后的图像上使用了@ 2x图像,则“丢失”Retina分辨率,但是在应用滤镜后仅显示左下象限,这要归功于从左下angular开始的图像原点) 。

黑客#2

沿着同样的路线,您可以缩放从ChangeHue方法返回的图像,使其不会在导航栏之外展开:

 var hued = ChangeHue (navBarImage); if (hued.RespondsToSelector(new MonoTouch.ObjCRuntime.Selector("scale"))) hued = hued.Scale (new System.Drawing.SizeF(320, 47)); UINavigationBar.Appearance.SetBackgroundImage (hued, UIBarMetrics.Default); 

不幸的是,你再次失去了视网膜分辨率,但至less图像显示正确(只是缩小到320宽)。

黑客#3

您可以将过滤的图像保存到磁盘,然后使用“磁盘”上的映像文件设置UIAppearance 。 代码看起来像这样:

 bool retina = false; if (UIScreen.MainScreen.RespondsToSelector (new MonoTouch.ObjCRuntime.Selector ("scale"))) { if (UIScreen.MainScreen.Scale == 2f) { retina = true; } } if (retina) { NSError err; // unitialized UIImage img = ChangeHue (navBarImage); img.AsPNG ().Save ("tempNavBar@2x.png", true, out err); if (err != null && err.Code != 0) { // error handling } UINavigationBar.Appearance.SetBackgroundImage (UIImage.FromFile ("tempNavBar.png"), UIBarMetrics.Default); } else { UINavigationBar.Appearance.SetBackgroundImage (ChangeHue (navBarImage), UIBarMetrics.Default); } 

这个最后的黑客的好处是图像看起来是正确的(即保留视网膜分辨率)。

我仍然在寻找“完美”的解决scheme,但至less这些想法是单向或者另一个“解决”你的问题。