Search code examples
ioscalayergradientrounded-corners

How to create a gradient perimeter using CALayer


I've been trying for some time now, using CAGradientLayer to produce this gradient rounded rect.

Initially I tried having a gradient background using the .colors property, however this is only a background fill. Trying this approach, I tried to add another CALayer inside that had a black background, however i could never get the radius correct, and it would create a line of various thickness at the rounded corners.

Is there a better way to create this rounded rect border with a gradient to it? Will CALayer not achieve this and should I move over to UIBezierPath or CGContextRef?

Code for failed attempt:
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(12*twelthWidth - squareCentre.x - squareSize.width, squareCentre.y, squareSize.width, squareSize.height)];
       // Create the rounded layer, and mask it using the rounded mask layer
        CAGradientLayer *roundedLayer = [CAGradientLayer layer];
        [roundedLayer setFrame:view.bounds];
        roundedLayer.cornerRadius = view.bounds.size.width/5;
        roundedLayer.masksToBounds = YES;
        roundedLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor], (id)[[UIColor blueColor] CGColor], nil];
        roundedLayer.borderWidth = 4;
        roundedLayer.borderColor = [UIColor clearColor].CGColor;

        // Add these two layers as sublayers to the view

        int BorderWidth = 5;

        CALayer *solidColour = [CALayer layer];
        solidColour.cornerRadius = view.bounds.size.width/5;
        solidColour.masksToBounds = YES;
        solidColour.backgroundColor = [UIColor blackColor].CGColor;
        [solidColour setFrame:CGRectMake(BorderWidth, BorderWidth, roundedLayer.bounds.size.width - 2*BorderWidth, roundedLayer.bounds.size.height - 2*BorderWidth)];


        [view.layer insertSublayer:roundedLayer atIndex:0];
        [view.layer insertSublayer:solidColour above:roundedLayer];
        [self.view addSubview:view];

Which produces:

enter image description here

Whereby the corners aren't right. Could it be that I need to calculate a different radius for the second layer?

Edit

After setting the radius of the solid colour to solidColour.bounds.size.width/5, it still isn't right - It goes too thin at the corners.

enter image description here


Solution

  • The problem you are seeing is because the inner and outer corner radius are the same. That is what causes the line thickness to vary. This illustration from CSS-Tricks highlights the issue (even thought you aren't using CSS, the problem is still the same):

    enter image description here

    The solution is to calculate the inner radius as:

    innerRadis = outerRadius - lineThickness
    

    As shown in this illustration by Joshua Hibbert:

    enter image description here