2

我正在使用此代码设置导航栏的背景,该代码在 Retina 和非 Retina 显示器中效果很好。有一个@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;
}

以下是应用 Hue 后在非 Retina 和 Retina 显示中的结果:

非视网膜

视网膜

4

4 回答 4

3

忽略这些 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对象,该对象具有正确的“比例”信息,可以正确显示在UINavigationBar.

于 2012-08-12T11:22:07.430 回答
2

我从未尝试过 CoreImage,但对于 CoreGraphics,您需要使用UIGraphics.BeginImageContextWithOptions并指定0缩放(因此它将自动为 Retina 和非 Retina 显示器完成)。

所以我会尝试的第一件事是替换你的:

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 ();

更新FromContext在 iOS 中不可用(它是 OSX 特定的),所以上面的代码不起作用。

于 2012-08-04T13:02:44.963 回答
0

向 MonoTouch 团队提交了一个错误。将很快发布解决方案。

于 2012-08-07T20:09:54.967 回答
0

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

黑客 #1

通过替换此行来强制修剪图像的范围:

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);

但是,您在调整后的图像上“丢失”了 Retina 分辨率(它使用 @2x 图像作为源,但在应用过滤器后仅显示其左下象限,这要归功于图像原点从左下角开始) .

黑客 #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);

不幸的是,您再次“丢失”了 Retina 分辨率,但至少图像显示正确(只是下采样到 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);
}

最终破解的好处是图像看起来正确(即保留了 Retina 分辨率)。

我仍在寻找“完美”的解决方案,但至少这些想法以一种或另一种方式“解决”你的问题......

于 2012-08-12T10:06:10.040 回答