Reputation: 1775
I want to make a gradient border of view like the following picture:
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
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
Reputation: 355
Thanx Tiago Mendes for answer.
I improved functionality:
And fixed some issues to ensure correct masking and given border width:
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
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
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
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
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
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