bch*_*rry 74 core-graphics calayer uikit cashapelayer ios
我试图通过使用CAShapeLayer并在其上设置圆形路径来绘制一个描边圆.但是,这种方法在渲染到屏幕时的效果始终低于使用borderRadius或直接在CGContextRef中绘制路径.
以下是所有三种方法的结果:
请注意,第三个渲染效果不佳,尤其是在顶部和底部的笔划内.
我已将该contentsScale
属性设置为[UIScreen mainScreen].scale
.
这是我对这三个圆圈的绘图代码.使CAShapeLayer顺利绘制的缺失是什么?
@interface BCViewController ()
@end
@interface BCDrawingView : UIView
@end
@implementation BCDrawingView
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = nil;
self.opaque = YES;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
[[UIColor whiteColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), NULL);
[[UIColor redColor] setStroke];
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 1);
[[UIBezierPath bezierPathWithOvalInRect:CGRectInset(self.bounds, 4, 4)] stroke];
}
@end
@interface BCShapeView : UIView
@end
@implementation BCShapeView
+ (Class)layerClass
{
return [CAShapeLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = nil;
CAShapeLayer *layer = (id)self.layer;
layer.lineWidth = 1;
layer.fillColor = NULL;
layer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(self.bounds, 4, 4)].CGPath;
layer.strokeColor = [UIColor redColor].CGColor;
layer.contentsScale = [UIScreen mainScreen].scale;
layer.shouldRasterize = NO;
}
return self;
}
@end
@implementation BCViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *borderView = [[UIView alloc] initWithFrame:CGRectMake(24, 104, 36, 36)];
borderView.layer.borderColor = [UIColor redColor].CGColor;
borderView.layer.borderWidth = 1;
borderView.layer.cornerRadius = 18;
[self.view addSubview:borderView];
BCDrawingView *drawingView = [[BCDrawingView alloc] initWithFrame:CGRectMake(20, 40, 44, 44)];
[self.view addSubview:drawingView];
BCShapeView *shapeView = [[BCShapeView alloc] initWithFrame:CGRectMake(20, 160, 44, 44)];
[self.view addSubview:shapeView];
UILabel *borderLabel = [UILabel new];
borderLabel.text = @"CALayer borderRadius";
[borderLabel sizeToFit];
borderLabel.center = CGPointMake(borderView.center.x + 26 + borderLabel.bounds.size.width/2.0, borderView.center.y);
[self.view addSubview:borderLabel];
UILabel *drawingLabel = [UILabel new];
drawingLabel.text = @"drawRect: UIBezierPath";
[drawingLabel sizeToFit];
drawingLabel.center = CGPointMake(drawingView.center.x + 26 + drawingLabel.bounds.size.width/2.0, drawingView.center.y);
[self.view addSubview:drawingLabel];
UILabel *shapeLabel = [UILabel new];
shapeLabel.text = @"CAShapeLayer UIBezierPath";
[shapeLabel sizeToFit];
shapeLabel.center = CGPointMake(shapeView.center.x + 26 + shapeLabel.bounds.size.width/2.0, shapeView.center.y);
[self.view addSubview:shapeLabel];
}
@end
Run Code Online (Sandbox Code Playgroud)
编辑:对于那些看不出差异的人,我在彼此之上绘制圆圈并放大:
在这里,我绘制了一个红色圆圈drawRect:
,然后drawRect:
在它上面再次以绿色绘制了一个相同的圆圈.请注意红色的有限流血.这两个圈子都是"平滑的"(和cornerRadius
实现相同):
在第二个示例中,您将看到问题.我曾经使用CAShapeLayer
红色一次,并且再次使用drawRect:
相同路径的实现,但是以绿色显示.请注意,您可以看到更多的不一致与下面的红色圆圈更多的流血.它显然是以不同(更糟)的方式绘制的.
Gle*_*Low 68
谁知道有很多方法画圆圈?
TL; DR:如果你想使用
CAShapeLayer
,仍然可以得到流畅的圈子里,你将需要使用shouldRasterize
和rasterizationScale
认真.
原版的
这是你的原版CAShapeLayer
和drawRect
版本的差异.我使用Retina Display在我的iPad Mini上制作了一个屏幕截图,然后在Photoshop中对其进行了按摩,并将其吹得高达200%.你可以清楚地看到,CAShapeLayer
版本有明显的差异,特别是在左右边缘(差异中最暗的像素).
在屏幕范围内进行栅格化
让我们在屏幕尺度上进行栅格化,这应该2.0
在视网膜设备上.添加此代码:
layer.rasterizationScale = [UIScreen mainScreen].scale;
layer.shouldRasterize = YES;
Run Code Online (Sandbox Code Playgroud)
请注意,甚至视网膜设备上的rasterizationScale
默认设置1.0
也会导致默认模糊shouldRasterize
.
圆现在有点平滑,但坏位(差异中最暗的像素)已经移动到顶部和底部边缘.没有比没有光栅化好多了!
以2倍的屏幕比例进行栅格化
layer.rasterizationScale = 2.0 * [UIScreen mainScreen].scale;
layer.shouldRasterize = YES;
Run Code Online (Sandbox Code Playgroud)
这样可以在2倍屏幕范围内光栅化路径,或者4.0
在视网膜设备上光栅化.
圆形现在明显更平滑,差异更轻,均匀分布.
我还在Instruments:Core Animation中运行了这个,并没有看到Core Animation Debug Options有任何重大差异.然而它可能会更慢,因为它不会缩小屏幕上的屏幕外位图.您可能还需要shouldRasterize = NO
在动画时临时设置.
什么行不通
单独设置shouldRasterize = YES
.在视网膜设备上,这看起来很模糊,因为rasterizationScale != screenScale
.
设置contentScale = screenScale
.既然CAShapeLayer
没有画入contents
,无论是否是光栅化,这都不会影响演绎.
感谢Humaan的 Jay Hollywood ,一位敏锐的平面设计师,他首先向我指出.
小智 9
啊,我前段时间遇到了同样的问题(它仍然是iOS 5然后是iirc),我在代码中写了以下注释:
/*
ShapeLayer
----------
Fixed equivalent of CAShapeLayer.
CAShapeLayer is meant for animatable bezierpath
and also doesn't cache properly for retina display.
ShapeLayer converts its path into a pixelimage,
honoring any displayscaling required for retina.
*/
Run Code Online (Sandbox Code Playgroud)
圆形下方的实心圆圈会使其填充颜色流失.根据颜色,这将是非常明显的.在用户交互过程中,形状会变得更糟,这让我得出结论,无论图层比例因子如何,shapelayer总是使用1.0的比例因子进行渲染,因为它用于动画目的.
即,如果您特别需要对bezier路径的形状进行动画更改,而不是通过常用图层属性可设置的任何其他属性,则只使用CAShapeLayer .
我最终决定编写一个简单的ShapeLayer来缓存自己的结果,但是你可以尝试实现displayLayer:或drawLayer:inContext:
就像是:
- (void)displayLayer:(CALayer *)layer
{
UIImage *image = nil;
CGContextRef context = UIImageContextBegin(layer.bounds.size, NO, 0.0);
if (context != nil)
{
[layer renderInContext:context];
image = UIImageContextEnd();
}
layer.contents = image;
}
Run Code Online (Sandbox Code Playgroud)
我没有尝试过,但知道结果会很有趣......
归档时间: |
|
查看次数: |
36226 次 |
最近记录: |