【问题标题】:Masking a CALayer with another CALayer用另一个 CALayer 屏蔽一个 CALayer
【发布时间】:2026-01-09 05:35:01
【问题描述】:

我正在尝试使用 CALayers 制作甜甜圈形状。一个 CALayer 将是一个大圆圈,另一个将是一个位于其中心的较小圆圈,将其掩盖。

大圆圈显示正常,但每当我调用circle.mask = circleMask; 时,视图就会显示为空。

这是我的代码:

AriDonut.h

#import <UIKit/UIKit.h>

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

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

我试过像这样设置大圆圈的超级层 nil:

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

但这并没有什么不同。

我也尝试将 Circle 的 masksToBounds 属性设置为 YES 和 NO,但没有任何区别。

有什么想法吗?

【问题讨论】:

    标签: ios uiview core-animation calayer


    【解决方案1】:

    使用 UIBezierPath 和 CAShapeLayer 作为遮罩层非常容易。就像在 UIView 子类中一样编写的代码示例。

    目标-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;
    

    斯威夫特:

    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
    

    【讨论】:

      【解决方案2】:

      我成功地用CAShapeLayer 掩盖了CALayer。要指定遮罩的形状CAShapeLayer,我使用了UIBezierPath

      我在对这个问题的回答中发布了代码:How to Get the reverse path of a UIBezierPath。对于甜甜圈形状,取消注释注释行。

      【讨论】:

        【解决方案3】:

        确实,正如@David 所指出的,当前(iOS 5.1)CALayer 蒙版无法反转,如果您想使用它们将透明孔制作成简单的圆形 CALayer,则会出现问题。

        要得到一个甜甜圈,你可以做一个圆形 CALayer 的 backgroundColor 透明,但给它一个 borderColor 和一个宽 borderWidth。这是邓肯的代码:

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

        【讨论】:

          【解决方案4】:

          它是用作遮罩的遮罩层内容的 alpha 值。 (如果您将蒙版添加为子图层而不是将其用作蒙版。当用作蒙版时,子图层覆盖的所有内容都将可见。当用作蒙版时,子图层未覆盖的所有内容都将隐藏.)

          因为你的小圆圈是完全透明的,所以一切都被掩盖了(被隐藏了)。如果你将它的backgroundColor 设置为任何完全不透明的颜色(只有 alpha 值用于遮罩),那么它会让这些像素通过。

          请注意,这与您想要的相反。这将使您只看到“甜甜圈的洞”。没有内置的方式来做反向蒙版相反,您必须以其他方式绘制蒙版的内容,例如使用 CAShapeLayer 或使用 drawInContext:

          【讨论】: