Search code examples
iosobjective-cuiviewcalayercashapelayer

Setting CALayer maskToBounds causing layer to disappear


I am trying to create a circle which is filled depending upon a certain percentage. Here is pic of the effect I am going for:

enter image description here

I have a UIView and to this I add a CAShapeLayer which draws the circle. I am then creating another UIShapeLayer as a square to match the UIView containing the circle. Then setting the height of this depending on the figure. So if the square is 100px high and the value is 10% then I set the square to 10px so that it fills 10% of circle.

The image below shows 50% of the circle being filled. As you can see it covers the UIView as well as the circle. However when I try to set the CAShapeLayer to mask to bounds ( circle.maskToBounds) the circle complete disappears along with the square that I am adding as it's subview.

enter image description here

Here is my code:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
    [self drawCircleInView:view];
    [self.view addSubview:view];

}

- (void)drawCircleInView:(UIView *)v
{
    // Set up the shape of the circle
    int radius = v.frame.size.height / 2;
    CAShapeLayer *circle = [CAShapeLayer layer];
    // Make a circular shape
    circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2*radius, 2*radius)
                                         cornerRadius:radius].CGPath;

    // Center the shape in self.view
    circle.position = CGPointMake(CGRectGetMidX(v.bounds)-radius,
                              CGRectGetMidY(v.bounds)-radius);

    // Configure the apperence of the circle
    circle.fillColor = [UIColor blueColor].CGColor;
    circle.strokeColor = [UIColor clearColor].CGColor;
    circle.lineWidth = 5;
    circle.masksToBounds = YES;  // HERE IS THE LINE OF CODE THAT MAKES THE CIRCLE DISAPPEAR

    CAShapeLayer *sublayer = [CAShapeLayer layer];
    sublayer.backgroundColor = [UIColor orangeColor].CGColor;
    sublayer.opacity = .5f;
    sublayer.frame = CGRectMake(0, 100, 200, 100);
    [circle addSublayer:sublayer];


    // Add to parent layer
    [v.layer addSublayer:circle];
}

I am wondering why setting circle.masksToBounds = YES is making the circle and the sublayer disappear completely. My understanding is that by setting this it should only show the sublayer over the circle.

Many thanks in advance.


Solution

  • Try this:

    - (void)drawCircleInView:(UIView *)v {
        // Set up the shape of the circle
        CGSize size = v.bounds.size;
        CALayer *layer = [CALayer layer];
        layer.frame = v.bounds;
        layer.backgroundColor = [UIColor blueColor].CGColor;
    
        CALayer *sublayer = [CALayer layer];
        sublayer.frame = CGRectMake(0, size.height/2, size.width, size.height/2);
        sublayer.backgroundColor = [UIColor orangeColor].CGColor;
        sublayer.opacity = .5f;
    
        CAShapeLayer *mask = [CAShapeLayer layer];
        mask.frame = v.bounds;
        mask.path = [UIBezierPath bezierPathWithOvalInRect:v.bounds].CGPath;
        mask.fillColor = [UIColor blackColor].CGColor;
        mask.strokeColor = [UIColor clearColor].CGColor;
    
        [layer addSublayer:sublayer];
        [layer setMask:mask];
    
        // Add to parent layer
        [v.layer addSublayer:layer];
    }
    
    1. maskToBounds is "to bounds" you are not setting bounds nor frame.
    2. It's not necessary, but you'd better to set frame of CAShapeLayer, to prevent unnecessary confusion.
    3. bounds is rectangle, not CAShapeLayer's shape. Use mask layer to mask by shape instead.
    4. circle and sublayer is not need to be CAShapeLayer because it will be masked by shaped mask layer.