2012-07-30 25 views
10

Ich versuche mit CALayers eine Doughnut-Form herzustellen. Ein CALayer wird ein großer Kreis sein, der andere wird ein kleinerer Kreis sein, der in seiner Mitte positioniert ist und ihn maskiert.Einen CALayer mit einem anderen CALayer maskieren

Der große Kreis zeigt gut, aber wenn ich circle.mask = circleMask; rufe, erscheint die Ansicht leer.

Hier ist mein Code:

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

ich den großen Kreis der Superlayer nil wie dieser Einstellung versucht haben:

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

Aber es machte keinen Unterschied.

Ich habe auch versucht, Circle masksToBounds Eigenschaft auf JA und NEIN, aber es machte keinen Unterschied.

Irgendwelche Gedanken?

Antwort

13

In der Tat, da @David die aktuelle (iOS 5.1) CALayer Masken nicht umgekehrt werden kann, was ein Problem darstellt, wenn Sie sie verwenden möchten, um ein transparentes Loch ein einfaches zirkuläres CALayer zu machen.

Was Sie tun können, um einen Donut zu erhalten, ist eine kreisförmige CALayers backgroundColor transparent, aber geben Sie eine borderColor und eine breite borderWidth. Hier ist der Dunkin' Code:

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

Es ist der Alpha-Wert des Inhalts der Maskierungsschichten, der als Maske verwendet wird. (Wenn Sie die Maske als Sublayer hinzufügen würden, anstatt sie als Maske zu verwenden. Alles, was von der Subschicht abgedeckt wird, wäre sichtbar, wenn sie als Maske verwendet würde. Alles, was nicht von der Subschicht bedeckt ist, würde als Maske verborgen sein .)

Da Ihr kleiner Kreis vollständig transparent ist, ist alles maskiert (ist versteckt). Wenn Sie backgroundColor davon auf eine beliebige, vollständig deckende Farbe setzen (nur der Alpha-Wert wird für die Maske verwendet), werden diese Pixel durchgelassen.

Beachten Sie, dass dies das Gegenteil von dem ist, was Sie wollen. Dadurch bleibt nur "das Loch des Donuts" sichtbar. Es gibt keine eingebaute Möglichkeit, eine umgekehrte Maske zu erstellen. Stattdessen müssten Sie den Inhalt der Maske auf eine andere Weise zeichnen, wie z. B. mit CAShapeLayer oder drawInContext:.

1

ich mit einem CAShapeLayer es gelungen, eine CALayer maskieren. Um die Form der Maskierung CAShapeLayer zu spezifizieren, benutzte ich UIBezierPath.

Ich habe den Code in meiner Antwort auf diese Frage: How to Get the reverse path of a UIBezierPath. Für die Donutform kommentieren Sie die kommentierte Zeile.

4

Dies ist ziemlich einfach mit UIBezierPath und einem CAShapeLayer als Maskierungsschicht. Codebeispiel so geschrieben, als ob es sich in einer UIView-Unterklasse befindet.

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