一个快速而简单的抗锯齿技巧适用于旋转的UIImageView吗?

26

我有一个UIImageView(完整框架和矩形),正在使用CGAffineTransform旋转它。 UIImageView的UIImage填充整个框架。当图像旋转并绘制时,边缘出现明显的锯齿。有什么办法可以让它看起来更好吗?很明显它没有与背景进行反锯齿处理。


图像是否正在内部进行抗锯齿处理 - 是边缘有锯齿还是整个图像都有锯齿?顺便说一句,如果可能的话,我建议使用CALayer。 - hatfinch
这是为iPhone设计的,所以我相信它已经使用了CALayer。 - Meltemi
确实,但如果不需要,为什么要携带所有的UIView/UIResponder负担呢? - hatfinch
好的,在我的情况下,这两个视图需要注册触摸。 - Meltemi
只有边缘是锯齿状的...图像本身抗锯齿效果很好。 - Meltemi
看看Johan Kool的回答...非常简单,而且像魔法一样有效! - Alex Zak
8个回答

55
默认情况下,iOS 上的 CoreAnimation 图层边缘没有抗锯齿效果。但是,在 Info.plist 中可以设置一个键来启用边缘的抗锯齿效果:UIViewEdgeAntialiasing。

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html

如果您不想启用此选项以避免性能开销,则可采用一种解决方法,即在图像边缘添加1像素的透明边框。这意味着图像的“边缘”不再位于边缘上,因此不需要特别处理!

2
这太棒了!如此简单,却又如此强大!:D - Alex Zak
1
很疯狂,这不是最佳答案。 - Epaga
有什么提示可以说明这对效率的影响吗? - MonsieurDart
@MathieuGodart 我自己没有测试过,因为在我需要的地方它运行得足够好。使用Instruments工具来确定它对你的性能影响有多大。当然,苹果默认关闭此功能是有原因的。 - Johan Kool
我注意到在 Info.plist 文件中将 UIViewEdgeAntialiasing 设置为 YES 会影响所有 UIAlertView 的圆角。 - Kevin Hirsch

28

新API - iOS 6/7

正如@Chris所指出的那样,它也适用于iOS 6,但直到iOS 7才公开。

自iOS 7以来,CALayer有一个新属性allowsEdgeAntialiasing,在不为应用程序中所有视图启用它的情况下,该属性完全满足您此时的需求!这是CALayer的一个属性,因此要为UIView启用它,您可以使用myView.layer.allowsEdgeAntialiasing = YES


这是一个很棒的API。请注意,它也适用于iOS 6(尽管显然只在iOS 7 SDK中添加/启用)。 - Chris
这可能是解决这个问题的最佳问题。 - Minsoo kim

17

只需在您的图像上添加1像素的透明边框即可

CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0);
[image drawInRect:CGRectMake(1,1,image.size.width-2,image.size.height-2)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

这里也能很好地工作。FYI:更简单的插入计算:CGRectInset(imageRect,1.0,1.0) - mattorb

10

请记得设置适当的抗锯齿选项:

CGContextSetAllowsAntialiasing(theContext, true);
CGContextSetShouldAntialias(theContext, true);

5
在旋转图片之前,在图片周围添加一个1像素的透明边框是解决这个问题的最佳方法。 - Nathan de Vries
2
有人能提供一下这个1像素透明边框的技巧吗?我已经设置了UIView的layer.borderColor和layer.borderWidth,但是对我没有用。 - akaru
我的图像是通过glReadPixels(OpenGL ES帧缓冲捕获)传输的,因此我能够直接在RGBA内存缓冲区上设置alpha值。对于处于相同情况下的人,请确保在CGImageCreate调用中不使用预乘功能(即我提供了kCGImageAlphaLast | kCGBitmapByteOrder32Big作为CGBitmapInfo参数)。 - Quintin Willison
1px 透明像素为什么可以修复问题的解释:锯齿状线条是 Cocoa 绘制的多边形边缘,由于您的纹理颜色延伸到了边缘上,因此它无法进行抗锯齿处理。但是边缘内部的图像可以进行抗锯齿处理。 - Dimitris
好的,那么如何使用它呢?还是只能用于绘制代码? - Vyachaslav Gerchicov
显示剩余3条评论

6
只需在plist中添加“使用边缘抗锯齿渲染”并将其设置为YES,即可正常工作。

5

2
我发现获得平滑的边缘和清晰的图像的最佳方法是这样做:
CGRect imageRect = CGRectMake(0, 0, self.photo.image.size.width, self.photo.image.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0);
[self.photo.image drawInRect:CGRectMake(1, 1, self.photo.image.size.width - 2, self.photo.image.size.height - 2)];
self.photo.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

像一些人描述的那样添加Info.plist键对性能有很大影响,如果你使用它,那么基本上是应用于所有地方而不仅仅是你需要的一个地方。

另外,不要只使用UIGraphicsBeginImageContext(imageRect.size); 否则图层会模糊。你必须像我展示的那样使用UIGraphicsBeginImageContextWithOptions


1
这是关于UIGraphicsBeginImageContextWithOptions的一个很好的观点...通常你必须将不透明选项设置为NO。 我正在绘制线条(即,在透明背景上的线条),然后使用renderInContext将线条的图像绘制到现有照片中。当然,使用不透明“NO”非常重要。 所以,这对我来说非常明显-在iWasRobbed告诉我之后 :) 谢谢,iWasRobbed! :) - Fattie
@JoeBlow 很高兴能帮到你! - iwasrobbed

0

我从这里找到了这个解决方案,它非常完美:

+ (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame transparentInsets:(UIEdgeInsets)insets {
    CGSize imageSizeWithBorder = CGSizeMake(frame.size.width + insets.left + insets.right, frame.size.height + insets.top + insets.bottom);
    // Create a new context of the desired size to render the image
    UIGraphicsBeginImageContextWithOptions(imageSizeWithBorder, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Clip the context to the portion of the view we will draw
    CGContextClipToRect(context, (CGRect){{insets.left, insets.top}, frame.size});
    // Translate it, to the desired position
    CGContextTranslateCTM(context, -frame.origin.x + insets.left, -frame.origin.y + insets.top);

    // Render the view as image
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];

    // Fetch the image
    UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();

    // Cleanup
    UIGraphicsEndImageContext();

    return renderedImage;
}

用法:

UIImage *image = [UIImage renderImageFromView:view withRect:view.bounds transparentInsets:UIEdgeInsetsZero];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接