使用CALayer的变换后,抗锯齿UIView边缘

26

我有一个使用 CALayer 的变换旋转的 UIView 对象:

// Create uiview object.
UIImageView *block = [[UIImageView alloc] initWithFrame....]

// Apply rotation.
CATransform3D basicTrans = CATransform3DIdentity;
basicTrans.m34 = 1.0/-distance;
blockImage.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);

旋转对象的边缘没有抗锯齿。我需要对它们进行抗锯齿处理。 请帮忙,如何实现?

4个回答

45

一种实现方法是将图像放置在另一个视图中,该视图比其宽高各增加5像素。更大的视图应该有一个透明栅格化边框,以平滑UIImageView的边缘:

view.layer.borderWidth = 3; 
view.layer.borderColor = [UIColor clearColor].CGColor; 
view.layer.shouldRasterize = YES; 
view.layer.rasterizationScale = [[UIScreen mainScreen] scale];

然后,将您的UIImageView放置在此父视图内并居中(每个边缘周围2.5像素)。

最后,旋转父视图而不是图像视图。

这非常有效-您还可以封装整个过程以创建层次结构的类。


9
为避免在Retina屏幕上出现模糊,您可能需要添加view.layer.rasterizationScale = [[UIScreen mainScreen] scale]; - Khanh Nguyen
这个注释应该是答案的一部分。设置“shouldRasterize”属性是不够的! - Luca Davanzo
tounaobun的答案表现更好。请查看我下面的评论。 - Roger Oba

42

只需在您的Info.plist中添加此键值对:UIViewEdgeAntialiasing设置为YES


完美且最简单的解决方案。 - Nikita Sharma Sahu

32

检查 CALayerallowsEdgeAntialiasing 属性。

block.layer.allowsEdgeAntialiasing = YES; // iOS7 and above.

1
就像上面的(Info.plist)一样,但控制更细粒度!太好了! - horseshoe7
1
被接受的答案确实像这个一样有效,但是这个更好。两者消耗相同的 RAM,但此解决方案为用户提供了更高的 FPS。 - Roger Oba
该属性的文档说明如果 Info.plist 值不存在,则该值为 NO。显然,您不能仅设置此属性,而必须与 plist 一起使用。我尚未测试如果未设置属性会发生什么,但根据 AddisDev 的答案判断,在这种情况下默认为 yes。因此,如果存在性能问题并且您不需要抗锯齿,则似乎应将此值设置为 NO。 - Victor Engel

3

当我围绕z轴旋转时,遇到了类似的问题。将shouldRasterize设置为YES可以防止锯齿边缘,但会降低性能。在我的情况下,我正在重用视图(及其图层),并且保持shouldRasterize = YES会减慢速度。

解决方案是,在不再需要光栅化后立即关闭它。但由于动画在另一个线程上运行,因此无法知道动画何时完成...直到我发现了一个非常有用的CATransaction方法。以下是我使用的实际代码,它应该说明其用途:

// Create a key frame animation
CAKeyframeAnimation *wiggle = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSInteger frequency = 5; // Higher value for faster vibration
NSInteger amplitude = 25; // Higher value for lower amplitude
// Create the values it will pass through    
NSMutableArray *valuesArray = [[NSMutableArray alloc] init];
NSInteger direction = 1;

[valuesArray addObject:@0.0];

for (NSInteger i = frequency; i > 0; i--, direction *= -1) {
    [valuesArray addObject:@((direction * M_PI_4 * (CGFloat)i / (CGFloat)amplitude))];
}

[valuesArray addObject:@0.0];
[wiggle setValues:valuesArray];

// Set the duration
[wiggle setAdditive:YES];
[wiggle setValueFunction:[CAValueFunction functionWithName:kCAValueFunctionRotateZ]];
[wiggle setDuration:0.6];

// Turn on rasterization to prevent jagged edges (anti-aliasing issues)
viewToRotate.layer.shouldRasterize = YES;

// ************ Important step **************
// Very usefull method. Block returns after ALL animations have completed.
[CATransaction setCompletionBlock:^{
    viewToRotate.layer.shouldRasterize = NO;
}];
// Animate the layer
[viewToRotate.layer addAnimation:wiggle forKey:@"wiggleAnimation"];

这对我非常好用。

我还没有尝试在隐式动画中使用它(即由于非视图关联层的可动属性值变化而发生的动画),但只要在属性更改之前调用CATransaction方法,我认为它应该可以工作,以保证块被提供给CATransaction在动画开始之前。


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