remykits
remykits

Reputation: 1775

how to make a gradient border of UIView?

I want to make a gradient border of view like the following picture:

Image

but I don't know how do it exactly , i.e. what the gradient color I should use to do it? how to set my view to show a border like the image?

I'm using the following code to get a border:

 self.view.layer.borderColor = [UIColor orangeColor].CGColor;
 self.view.layer.borderWidth = 2.0f;

Upvotes: 27

Views: 18949

Answers (7)

abura
abura

Reputation: 1

You forgot to assign the name to the border sublayer.

if !isAlreadyAdded 
{
  border.name = Self.kLayerNameGradientBorder 
  layer.addSublayer(border)
}

Please correct the code.

Upvotes: 0

eli7ah
eli7ah

Reputation: 355

Thanx Tiago Mendes for answer.

I improved functionality:

  • added corner radius.

And fixed some issues to ensure correct masking and given border width:

  • improved gradient layer frame;
  • improved mask path rounding rect.

Swift 5

public extension UIView {
    private static let kLayerNameGradientBorder = "GradientBorderLayer"

    func gradientBorder(
        width: CGFloat,
        colors: [UIColor],
        startPoint: CGPoint = .init(x: 0.5, y: 0),
        endPoint: CGPoint = .init(x: 0.5, y: 1),
        andRoundCornersWithRadius cornerRadius: CGFloat = 0
    ) {
        let existingBorder = gradientBorderLayer()
        let border = existingBorder ?? .init()
        border.frame = CGRect(
            x: bounds.origin.x, 
            y: bounds.origin.y,
            width: bounds.size.width + width, 
            height: bounds.size.height + width
        )
        border.colors = colors.map { $0.cgColor }
        border.startPoint = startPoint
        border.endPoint = endPoint

        let mask = CAShapeLayer()
        let maskRect = CGRect(
            x: bounds.origin.x + width/2,
            y: bounds.origin.y + width/2,
            width: bounds.size.width - width,
            height: bounds.size.height - width
        )
        mask.path = UIBezierPath(
            roundedRect: maskRect, 
            cornerRadius: cornerRadius
        ).cgPath
        mask.fillColor = UIColor.clear.cgColor
        mask.strokeColor = UIColor.white.cgColor
        mask.lineWidth = width

        border.mask = mask

        let isAlreadyAdded = (existingBorder != nil)
        if !isAlreadyAdded {
            layer.addSublayer(border)
        }
    }

    private func gradientBorderLayer() -> CAGradientLayer? {
        let borderLayers = layer.sublayers?.filter {
            $0.name == UIView.kLayerNameGradientBorder
        }
        if borderLayers?.count ?? 0 > 1 {
            fatalError()
        }
        return borderLayers?.first as? CAGradientLayer
    }
}

And for more readable declaration of startPoint and endPoint of gradient layer I use this code:

public extension CGPoint {
    
    enum CoordinateSide {
        case topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
    }
    
    static func unitCoordinate(_ side: CoordinateSide) -> CGPoint {
        let x: CGFloat
        let y: CGFloat

        switch side {
        case .topLeft:      x = 0.0; y = 0.0
        case .top:          x = 0.5; y = 0.0
        case .topRight:     x = 1.0; y = 0.0
        case .right:        x = 0.0; y = 0.5
        case .bottomRight:  x = 1.0; y = 1.0
        case .bottom:       x = 0.5; y = 1.0
        case .bottomLeft:   x = 0.0; y = 1.0
        case .left:         x = 1.0; y = 0.5
        }
        return .init(x: x, y: y)
    }
}

So final usage is:

view.gradientBorder(
    width: 3, 
    colors: [.red, .orange], 
    startPoint: .unitCoordinate(.top),
    endPoint: .unitCoordinate(.bottom),
    andRoundCornersWithRadius: 12
)

Upvotes: 17

Tiago Mendes
Tiago Mendes

Reputation: 5156

Here is another solution that is working on swift 4

import UIKit

public extension UIView {

    private static let kLayerNameGradientBorder = "GradientBorderLayer"

    func setGradientBorder(
        width: CGFloat,
        colors: [UIColor],
        startPoint: CGPoint = CGPoint(x: 0.5, y: 0),
        endPoint: CGPoint = CGPoint(x: 0.5, y: 1)
    ) {
        let existedBorder = gradientBorderLayer()
        let border = existedBorder ?? CAGradientLayer()
        border.frame = bounds
        border.colors = colors.map { return $0.cgColor }
        border.startPoint = startPoint
        border.endPoint = endPoint

        let mask = CAShapeLayer()
        mask.path = UIBezierPath(roundedRect: bounds, cornerRadius: 0).cgPath
        mask.fillColor = UIColor.clear.cgColor
        mask.strokeColor = UIColor.white.cgColor
        mask.lineWidth = width

        border.mask = mask

        let exists = existedBorder != nil
        if !exists {
            layer.addSublayer(border)
        }
    }


    private func gradientBorderLayer() -> CAGradientLayer? {
        let borderLayers = layer.sublayers?.filter { return $0.name == UIView.kLayerNameGradientBorder }
        if borderLayers?.count ?? 0 > 1 {
            fatalError()
        }
        return borderLayers?.first as? CAGradientLayer
    }

}

How to use:

view.setGradientBorder(width: 10, colors: [UIColor(red: 47, green: 198, blue: 176), UIColor(red: 67, green: 210, blue: 128)])

Upvotes: 4

Christos Chadjikyriacou
Christos Chadjikyriacou

Reputation: 3749

This what i did and it worked perfectly

   extension CALayer {
    func addGradienBorder(colors:[UIColor],width:CGFloat = 1) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame =  CGRect(origin: CGPointZero, size: self.bounds.size)
        gradientLayer.startPoint = CGPointMake(0.0, 0.5)
        gradientLayer.endPoint = CGPointMake(1.0, 0.5)
        gradientLayer.colors = colors.map({$0.CGColor})

        let shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = width
        shapeLayer.path = UIBezierPath(rect: self.bounds).CGPath
        shapeLayer.fillColor = nil
        shapeLayer.strokeColor = UIColor.blackColor().CGColor
        gradientLayer.mask = shapeLayer

        self.addSublayer(gradientLayer)
    }

}

Upvotes: 25

Rohit Khandelwal
Rohit Khandelwal

Reputation: 1778

You can make gradient border of view and corner radius(if you want) using this--

self.yourView.layer.cornerRadius=4;

self.yourView.layer.masksToBounds=YES;

CAGradientLayer *gradient = [CAGradientLayer layer];

gradient.frame = self.yourView.bounds;

gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:255/255.0 green:226/255.0 blue:138/255.0 alpha:1.0] CGColor], (id)[[UIColor colorWithRed:255/255.0 green:198/255.0 blue:91/255.0 alpha:0.9] CGColor],(id)[[UIColor colorWithRed:255/255.0 green:226/255.0 blue:138/255.0 alpha:1.0] CGColor], nil];

gradient.startPoint = CGPointMake(0.0, 0.0);

gradient.endPoint = CGPointMake(1, 1);

CAShapeLayer *shapeLayer =[[CAShapeLayer alloc] init];

shapeLayer.lineWidth = 15; // higher number higher border width

shapeLayer.path = [UIBezierPath bezierPathWithRect:self.yourView.bounds].CGPath;

shapeLayer.fillColor = nil;

shapeLayer.strokeColor = [UIColor blackColor].CGColor;

gradient.mask = shapeLayer;

[self.yourView.layer insertSublayer:gradient atIndex:0];

this will help you! Thanks

Upvotes: 7

JAHelia
JAHelia

Reputation: 7932

objective-c version of Christos Hadjikyriacou's answer

  @implementation CALayer(Border)

-(void) addGradientBorder {

CAGradientLayer *gradientLayer =  [[CAGradientLayer alloc] init];
gradientLayer.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
gradientLayer.startPoint = CGPointMake(0.0, 0.5);
gradientLayer.endPoint = CGPointMake(1.0, 0.5);
gradientLayer.colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor];

CAShapeLayer *shapeLayer =[[CAShapeLayer alloc] init];
shapeLayer.lineWidth = 0.5;
shapeLayer.path = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
shapeLayer.fillColor = nil;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
gradientLayer.mask = shapeLayer;
[self addSublayer : gradientLayer];
}
@end

Upvotes: 1

jverrijt
jverrijt

Reputation: 696

Here is how you would do it with Core Graphics. Create a UIView subclass and in its drawRect: create a gradient and overlay that with a black content rectangle:

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    // Create and draw the gradient
    UIColor *gradientColorTop = [UIColor orangeColor];
    UIColor *gradientColorBottom = [UIColor yellowColor];
    NSArray *gradientColors = @[(id) gradientColorTop.CGColor, (id) gradientColorBottom.CGColor];
    CGFloat locations[] = {0.1, 0.80};

    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)(gradientColors), locations);

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));

    UIBezierPath *gradientBorder = [UIBezierPath bezierPathWithRoundedRect:rect
    byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10.0, 10.0)];
    [gradientBorder addClip];

    CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);

    // Draw the inner content rectangle
    UIBezierPath *contentPath = [UIBezierPath bezierPathWithRect:CGRectInset(rect, 20.0, 20.0)];
    [[UIColor blackColor] setFill];
    [contentPath fill];

    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

That would produce a result similar to what you are trying to achieve.

Upvotes: 2

Related Questions