简体   繁体   English

用另一个CALayer屏蔽CALayer

[英]Masking a CALayer with another CALayer

I'm trying to make a donut shape with CALayers. 我正试图用CALayers制作甜甜圈。 One CALayer will be a large circle, the other one will be a smaller circle positioned in its center, masking it. 一个CALayer将是一个大圆圈,另一个将是一个位于其中心的较小圆圈,掩盖它。

The large circle displays fine, but whenever I call circle.mask = circleMask; 大圆圈显示正常,但每当我调用circle.mask = circleMask; then the view appears empty. 然后视图显示为空。

Here's my code: 这是我的代码:

AriDonut.h AriDonut.h

#import <UIKit/UIKit.h>

@interface AriDonut : UIView
-(id)initWithRadius:(float)radius;
@end

AriDonut.m AriDonut.m

#import "AriDonut.h"
#import <QuartzCore/QuartzCore.h>

@implementation AriDonut

-(id)initWithRadius:(float)radius{
    self = [super initWithFrame:CGRectMake(0, 0, radius, radius)];
    if(self){

        //LARGE CIRCLE
        CALayer *circle = [CALayer layer];
        circle.bounds = CGRectMake(0, 0, radius, radius);
        circle.backgroundColor = [UIColor redColor].CGColor;
        circle.cornerRadius = radius/2;
        circle.position = CGPointMake(radius/2, radius/2);

        //SMALL CIRLCE
        CALayer *circleMask = [CALayer layer];
        circleMask.bounds = CGRectMake(0, 0, 10, 10);
        circleMask.cornerRadius = radius/2;
        circleMask.position = circle.position;

        //circle.mask = circleMask;

        [self.layer addSublayer:circle];

    }

    return self;
}

I've tried setting the large circle's superlayer nil like this: 我试过像这样设置大圆圈的超级层nil:

CALayer *theSuper = circle.superlayer;
theSuper = nil;

But it didin't make a difference. 但它并没有什么不同。

I also tried setting Circle's masksToBounds property to YES and NO, but it didn't make a difference. 我也尝试将Circle的masksToBounds属性设置为YES和NO,但它没有任何区别。

Any thoughts? 有什么想法吗?

Indeed, as @David indicates the current (iOS 5.1) CALayer masks can't be reversed, which poses a problem if you want to use them to make a transparent hole a simple circular CALayer. 实际上,由于@David表示当前(iOS 5.1)CALayer蒙版无法反转,如果您想使用它们将透明孔制作为简单的圆形CALayer,则会出现问题。

What you can do to get a donut is make a circular CALayer's backgroundColor transparent, but give it a borderColor and a wide borderWidth . 你可以做一个圆环CALayer的backgroundColor透明,但给它一个borderColor和一个宽borderWidth Here's the dunkin' code: 这是dunkin'代码:

    CALayer *theDonut = [CALayer layer];
    theDonut.bounds = CGRectMake(0,0, radius, radius);
    theDonut.cornerRadius = radius/2;
    theDonut.backgroundColor = [UIColor clearColor].CGColor;

    theDonut.borderWidth = radius/5;
    theDonut.borderColor = [UIColor orangeColor].CGColor;

    [self.layer addSublayer:theDonut];

This is pretty easy using UIBezierPath and a CAShapeLayer as a masking layer. 使用UIBezierPath和CAShapeLayer作为屏蔽层非常容易。 Code sample written as though it's in a UIView subclass. 编写的代码示例就像它在UIView子类中一样。

Objective-C: Objective-C的:

CGRect outerRect = self.bounds;
CGFloat inset = 0.2 * outerRect.size.width; // adjust as necessary for more or less meaty donuts
CGFloat innerDiameter = outerRect.size.width - 2.0 * inset;
CGRect innerRect = CGRectMake(inset, inset, innerDiameter, innerDiameter);
UIBezierPath *outerCircle = [UIBezierPath bezierPathWithRoundedRect:outerRect cornerRadius:outerRect.size.width * 0.5];
UIBezierPath *innerCircle = [UIBezierPath bezierPathWithRoundedRect:innerRect cornerRadius:innerRect.size.width * 0.5];
[outerCircle appendPath:innerCircle];
CAShapeLayer *maskLayer = [CAShapeLayer new];
maskLayer.fillRule = kCAFillRuleEvenOdd; // Going from the outside of the layer, each time a path is crossed, add one. Each time the count is odd, we are "inside" the path.
maskLayer.path = outerCircle.CGPath;
self.layer.mask = maskLayer;

Swift: 迅速:

let outerRect = self.bounds
let inset: CGFloat = 0.2 * outerRect.width // adjust as necessary for more or less meaty donuts
let innerDiameter = outerRect.width - 2.0 * inset
let innerRect = CGRect(x: inset, y: inset, width: innerDiameter, height: innerDiameter)
let outerCircle = UIBezierPath(roundedRect: outerRect, cornerRadius: outerRect.width * 0.5)
let innerCircle = UIBezierPath(roundedRect: innerRect, cornerRadius: innerRect.width * 0.5)
outerCircle.appendPath(innerCircle)
let mask = CAShapeLayer()
mask.fillRule = kCAFillRuleEvenOdd
mask.path = outerCircle.CGPath
self.layer.mask = mask

It is the alpha value of the masking layers content that is used as a mask. 它是用作遮罩的遮罩层内容的alpha值。 (If you would add the mask as a sublayer instead of using it as a mask. Everything that is covered by the sublayer would be visible when used as a mask. Everything that is not covered by the sublayer would be hidden when used as a mask.) (如果要将蒙版添加为子图层而不是将其用作蒙版。当用作蒙版时,子图层覆盖的所有内容都将可见。当用作蒙版时,子图层未覆盖的所有内容都将被隐藏。)

Since your small circle is fully transparent , everything is masked away (is hidden). 由于您的小圆圈是完全透明的,所以一切都被掩盖了(隐藏)。 If you set the backgroundColor of it to any, fully opaque color (only the alpha value is used for the mask) then it will let those pixels through. 如果你将它的backgroundColor设置为任何完全不透明的颜色(只有alpha值用于蒙版),那么它将让这些像素通过。

Note that this is the reverse of what you want. 请注意,这与您想要的相反。 This will leave you with only "the hole of the donut" visible. 这将使您只看到“甜甜圈的洞”可见。 There is no built in way to do a reverse mask Instead you would have to draw the content of the mask some other way like using a CAShapeLayer or using drawInContext: . 没有内置的方法来做反向掩码相反,您必须以其他方式绘制掩码的内容,例如使用CAShapeLayer或使用drawInContext: .

I succeeded with a CAShapeLayer masking a CALayer . 我成功地用CAShapeLayer掩盖了CALayer To specify the shape of the masking CAShapeLayer I used UIBezierPath . 要指定屏蔽CAShapeLayer的形状,我使用了UIBezierPath

I posted the code in my answer to this question: How to Get the reverse path of a UIBezierPath . 我在这个问题的答案中发布了代码: 如何获取UIBezierPath的反向路径 For the donut shape uncomment the commented line. 对于甜甜圈形状取消注释评论线。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM