Маскировка CALayer другим CALayer

Я пытаюсь сделать форму пончика с помощью 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;
}

Я попытался установить ноль суперслоя большого круга следующим образом:

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

Но это не имело значения.

Я также попытался установить для свойства Circle masksToBounds значения YES и NO, но это не имело значения.

есть идеи?


person Eric    schedule 30.07.2012    source источник


Ответы (4)


Действительно, как указывает @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];
person Eric    schedule 31.07.2012

Это довольно легко сделать, используя UIBezierPath и CAShapeLayer в качестве маскирующего слоя. Пример кода написан так, как будто он находится в подклассе UIView.

Цель-С:

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
person Kyle Robson    schedule 06.12.2015

Это альфа-значение содержимого маскирующих слоев, которое используется в качестве маски. (Если вы добавите маску в качестве подслоя вместо того, чтобы использовать ее в качестве маски. Все, что покрыто подслоем, будет видно при использовании в качестве маски. Все, что не покрыто подслоем, будет скрыто при использовании в качестве маски. .)

Поскольку ваш маленький круг полностью прозрачен, все замаскировано (спрятано). Если вы установите backgroundColor любого, полностью непрозрачного цвета (для маски используется только альфа-значение), то он пропустит эти пиксели.

Обратите внимание, что это противоположно тому, что вы хотите. Это оставит вас с видимой только «дыркой пончика». Нет встроенного способа сделать обратную маску. Вместо этого вам придется рисовать содержимое маски каким-то другим способом, например, с помощью CAShapeLayer или с помощью drawInContext:.

person David Rönnqvist    schedule 30.07.2012

Мне удалось с CAShapeLayer замаскировать CALayer. Чтобы указать форму маскирования CAShapeLayer, я использовал UIBezierPath.

Я отправил код в свой ответ на этот вопрос: Как получить обратный путь UIBezierPath. Для формы пончика раскомментируйте закомментированную строку.

person user511    schedule 08.03.2013