how to make a gradient border of UIView? - ios

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;

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

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 = CGPoint(x: 0.5, y: 0.0),
endPoint: CGPoint = CGPoint(x: 0.5, y: 1.0),
andRoundCornersWithRadius cornerRadius: CGFloat = 0) {
let existingBorder = gradientBorderLayer()
let border = existingBorder ?? CAGradientLayer()
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 { return $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 exists = (existingBorder != 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
}
}
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 {
switch side {
case .topLeft: return CGPoint(x: 0.0, y: 0.0)
case .top: return CGPoint(x: 0.5, y: 0.0)
case .topRight: return CGPoint(x: 1.0, y: 0.0)
case .right: return CGPoint(x: 0.0, y: 0.5)
case .bottomRight: return CGPoint(x: 1.0, y: 1.0)
case .bottom: return CGPoint(x: 0.5, y: 1.0)
case .bottomLeft: return CGPoint(x: 0.0, y: 1.0)
case .left: return CGPoint(x: 1.0, y: 0.5)
}
}
}
So final usage is:
view.gradientBorder(width: 3, colors: [.red, .orange], startPoint: .unitCoordinate(.top), endPoint: .unitCoordinate(.bottom), andRoundCornersWithRadius: 12)

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

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)])

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.

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

Related

How to make radial gradient to be faded in the middle of view in Objective C

I want to make radial gradient view as above attached image but I am not able to make fade in the middle, here is my code for gradient.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view_background.bounds;
gradient.colors = #[(id)[UIColor colorWithRed:0.93 green:0.95 blue:0.22 alpha:1].CGColor, (id)[UIColor colorWithRed:0.08 green:0.42 blue:0.32 alpha:1].CGColor];
gradient.endPoint = CGPointMake(0.0, 0.5);
gradient.startPoint = CGPointMake(0.5, 1.0);
[view_background.layer insertSublayer: gradient atIndex:0];
Try the code below. Make RadialGradientView as IBDesignable.
RadialGradientView.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
IB_DESIGNABLE
#interface RadialGradientView : UIView
#end
NS_ASSUME_NONNULL_END
RadialGradientView.m
#import "RadialGradientView.h"
#import <CoreGraphics/CoreGraphics.h>
#implementation RadialGradientView
- (void)drawRect:(CGRect)rect {
// Drawing code
UIColor* radialColor = [[UIColor colorWithRed:0.93 green:0.95 blue:0.22 alpha:1] CGColor];
UIColor* outerColor = [[UIColor colorWithRed:0.08 green:0.42 blue:0.32 alpha:1] CGColor];
[self.layer setCornerRadius: 10.0];
[self.layer setMasksToBounds:YES];
NSArray* colors = [[NSArray alloc] initWithObjects: radialColor, outerColor, nil];
CGFloat endRadius = sqrt(pow(self.frame.size.width / 1.9, 2) + pow(self.frame.size.height / 1.9, 2));
CGPoint startCenter = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 12);
CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
CGGradientRef gradient = CGGradientCreateWithColors(nil, (CFArrayRef)colors, nil);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawRadialGradient(context, gradient, startCenter, 0.0, center, endRadius, 0);
}
#end
Swift
#IBDesignable class RadialGradientView: UIView {
#IBInspectable var radialColor: UIColor = UIColor(red: 0.93, green: 0.95, blue: 0.22, alpha: 1.0)
#IBInspectable var outerColor: UIColor = UIColor(red: 0.08, green: 0.42, blue: 0.32, alpha: 1.0)
override func draw(_ rect: CGRect) {
layer.cornerRadius = 10.0
layer.masksToBounds = true
let colors = [radialColor.cgColor, outerColor.cgColor]
let endRadius = sqrt(pow(frame.width/1.9, 2) + pow(frame.height/1.9, 2))
let startCenter = CGPoint(x: bounds.size.width / 2, y: bounds.size.height / 12)
let center = CGPoint(x: bounds.size.width / 2, y: bounds.size.height / 2)
let gradient = CGGradient(colorsSpace: nil, colors: colors as CFArray, locations: nil)
let context = UIGraphicsGetCurrentContext()
context?.drawRadialGradient(gradient!, startCenter: startCenter, startRadius: 0.0, endCenter: center, endRadius: endRadius, options: CGGradientDrawingOptions.drawsBeforeStartLocation)
}
}
Screenshot

Draw line with oblique border

How to draw line with oblique border like this:
I drew only a normal line:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 30.f);
CGContextSetStrokeColorWithColor(context, [UIColor personalBlue].CGColor);
CGContextMoveToPoint(context, 0.f, 0.f);
CGContextAddLineToPoint(context,self.bounds.size.width, 0.f);
CGContextStrokePath(context);
I use CAShapeLayer:
class TriangleView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
self.contentMode = UIViewContentMode.redraw
}
override func draw(_ rect: CGRect) {
let mask = CAShapeLayer()
mask.frame = self.layer.bounds
let width = self.layer.frame.size.width
let height = self.layer.frame.size.height
let path = CGMutablePath.init()
path.move(to: CGPoint(x: width, y: 0))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.addLine(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: 50))
mask.path = path
self.layer.mask = mask
let shape = CAShapeLayer()
shape.frame = self.bounds
shape.path = path
shape.lineWidth = 3.0
shape.strokeColor = UIColor.white.cgColor
shape.fillColor = UIColor.white.cgColor
self.layer.insertSublayer(shape, at: 0)
}
}
The result is this:

How to fill a bezier path with gradient color

I have a UIBezierPath inside my custom UIView draw(_ rect: CGRect) function. I would like to fill the path with a gradient color. Please can anybody guide me how can I do that.
I need to fill the clip with a gradient color and then stroke the path with black color.
There are some posts in SO which does not solve the problem. For example Swift: Gradient along a bezier path (using CALayers) this post guides how to draw on a layer in UIView but not in a UIBezierPath.
NB: I am working on Swift-3
To answer this question of yours,
I have a UIBezierPath inside my custom UIView draw(_ rect: CGRect)
function. I would like to fill the path with a gradient color.
Lets say you have an oval path,
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 100, height: 100))
To create a gradient,
let gradient = CAGradientLayer()
gradient.frame = path.bounds
gradient.colors = [UIColor.magenta.cgColor, UIColor.cyan.cgColor]
We need a mask layer for gradient,
let shapeMask = CAShapeLayer()
shapeMask.path = path.cgPath
Now set this shapeLayer as mask of gradient layer and add it to view's layer as subLayer
gradient.mask = shapeMask
yourCustomView.layer.addSublayer(gradient)
Update
Create a base layer with stroke and add before creating gradient layer.
let shape = CAShapeLayer()
shape.path = path.cgPath
shape.lineWidth = 2.0
shape.strokeColor = UIColor.black.cgColor
self.view.layer.addSublayer(shape)
let gradient = CAGradientLayer()
gradient.frame = path.bounds
gradient.colors = [UIColor.magenta.cgColor, UIColor.cyan.cgColor]
let shapeMask = CAShapeLayer()
shapeMask.path = path.cgPath
gradient.mask = shapeMask
self.view.layer.addSublayer(gradient)
You can do this directly in Core Graphics without using CALayer classes. Use bezierPath.addClip() to set the bezier path as the clipping region. Any subsequent drawing commands will be masked to that region.
I use this wrapper function in one of my projects:
func drawLinearGradient(inside path:UIBezierPath, start:CGPoint, end:CGPoint, colors:[UIColor])
{
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
defer { ctx.restoreGState() } // clean up graphics state changes when the method returns
path.addClip() // use the path as the clipping region
let cgColors = colors.map({ $0.cgColor })
guard let gradient = CGGradient(colorsSpace: nil, colors: cgColors as CFArray, locations: nil)
else { return }
ctx.drawLinearGradient(gradient, start: start, end: end, options: [])
}
The other answers here don't seem to work (At least in newer iOS versions?)
Here's a working example of a bezier path being created and the stroke being a gradient stroke, adjust the CGPoints and CGColors as needed:
CGPoint start = CGPointMake(300, 400);
CGPoint end = CGPointMake(350, 400);
CGPoint control = CGPointMake(300, 365);
UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:start];
[path addQuadCurveToPoint:end controlPoint:control];
CAShapeLayer *shapeLayer = [CAShapeLayer new];
shapeLayer.path = path.CGPath;
shapeLayer.strokeColor = [UIColor whiteColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2.0;
//[self.view.layer addSublayer:shapeLayer];
CGRect gradientBounds = CGRectMake(path.bounds.origin.x-shapeLayer.lineWidth, path.bounds.origin.y-shapeLayer.lineWidth, path.bounds.size.width+shapeLayer.lineWidth*2.0, path.bounds.size.height+shapeLayer.lineWidth*2.0);
CAGradientLayer *gradientLayer = [CAGradientLayer new];
gradientLayer.frame = gradientBounds;
gradientLayer.bounds = gradientBounds;
gradientLayer.colors = #[(id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor];
gradientLayer.mask = shapeLayer;
[self.view.layer addSublayer:gradientLayer];
Swift 5 version of #AlbertRenshaw's (ObjC) answer, and wrapped in a UIView class.
What it displays looks spherical but on it's side.
A CGAffineTransform could be applied to rotate it 90º for a cool effect.
import UIKit
class SphereView : UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
let centerPoint = CGPoint(x: frame.width / 2, y: frame.height / 2)
let radius = frame.height / 2
let circlePath = UIBezierPath(arcCenter: centerPoint,
radius: radius,
startAngle: 0,
endAngle: .pi * 2,
clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath;
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 2.0;
let gradientBounds = CGRectMake(
circlePath.bounds.origin.x - shapeLayer.lineWidth,
circlePath.bounds.origin.y - shapeLayer.lineWidth,
circlePath.bounds.size.width + shapeLayer.lineWidth * 2.0,
circlePath.bounds.size.height + shapeLayer.lineWidth * 2.0)
let gradientLayer = CAGradientLayer()
gradientLayer.frame = gradientBounds;
gradientLayer.bounds = gradientBounds;
gradientLayer.colors = [UIColor.red, UIColor.blue].map { $0.cgColor }
gradientLayer.mask = shapeLayer;
self.layer.addSublayer(gradientLayer)
}
}
Usage in a UIViewController:
override func viewDidLoad() {
view.backgroundColor = .white
let sphereView = SphereView()
sphereView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(sphereView)
NSLayoutConstraint.activate([
sphereView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
sphereView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
sphereView.widthAnchor.constraint(equalToConstant: 150),
sphereView.heightAnchor.constraint(equalToConstant: 150),
])
}

Fade the end of a label text

I have one UILabel, and i want to fade the end of the string, that is going to be out of bounds. What is the better solution for this?
Should i calculate the width of the label, compare it with the string width, and if string width is bigger than label's, i should fade last two letters? How exactly should i do that?
I hope it will be easy. Please write your solutions. Thanks!
I prefer to use this method for calculating the width:
CGRect labelRect = [text
boundingRectWithSize:labelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{
NSFontAttributeName : [UIFont systemFontOfSize:14]
}
context:nil];
Swift 4.0 example:
let gradient = CAGradientLayer()
gradient.frame = titleLabel.bounds
gradient.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]
gradient.startPoint = CGPoint(x: 0.1, y: 0.0)
gradient.endPoint = CGPoint(x: 0.95, y: 0.0)
label.lineBreakMode = .byClipping
label.layer.mask = gradient
result:
You can fade one line label using CAGradientLayer
CAGradientLayer *l = [CAGradientLayer layer];
l.frame = self.textLabel.bounds;
l.colors = #[(id)[UIColor whiteColor].CGColor, (id)[UIColor clearColor].CGColor];
l.startPoint = CGPointMake(0.1f, 1.0f);
l.endPoint = CGPointMake(0.95f, 1.0f);
self.textLabel.layer.mask = l;
Works only from layoutSubviews() method
override func layoutSubviews() {
super.layoutSubviews()
let maskLayer = CALayer()
maskLayer.frame = label.bounds
let gradientLayer = CAGradientLayer()
gradientLayer.frame = label.bounds
gradientLayer.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.7, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 0.95, y: 0.0)
maskLayer.addSublayer(gradientLayer)
label.layer.mask = maskLayer
}
2023. Typical OUTLINE solution:
It does not take in to account language direction, it assumes background is white, etc etc.
Note that you have to explicitly decide on and set the length of the fade, which, of course, will be constant through out the app, you can't use ".1 of the width of the text". Here we use the height of the label which works well.
class FadeyLabel: UIILabel {
lazy var fade: CAGradientLayer = {
let g = CAGradientLayer()
g.colors = [UIColor.white.withAlphaComponent(0).cgColor,
UIColor.white.withAlphaComponent(1).cgColor]
g.startPoint = CGPoint(x: 0, y: 0)
g.endPoint = CGPoint(x: 1, y: 0)
layer.addSublayer(g)
return g
}()
override func layoutSubviews() {
super.layoutSubviews()
fade.frame = CGRect(
origin: CGPoint(x: bounds.width - bounds.height, y: 0),
size: CGSize(width: bounds.height, height: bounds.height))
}
}
I've found the answer - i should use GTMFadeTruncatingLabelTest class from google

Dashed line border around UIView

How do I add dashed line border around UIView.
Something Like this
Another method if you like sublayers. In your custom view's init, put this (_border is an ivar):
_border = [CAShapeLayer layer];
_border.strokeColor = [UIColor colorWithRed:67/255.0f green:37/255.0f blue:83/255.0f alpha:1].CGColor;
_border.fillColor = nil;
_border.lineDashPattern = #[#4, #2];
[self.layer addSublayer:_border];
And in your layoutsubviews, put this:
_border.path = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
_border.frame = self.bounds;
You can set the border with this pattern using Layer and Bezier path like below examples.
Objective-C
CAShapeLayer *yourViewBorder = [CAShapeLayer layer];
yourViewBorder.strokeColor = [UIColor blackColor].CGColor;
yourViewBorder.fillColor = nil;
yourViewBorder.lineDashPattern = #[#2, #2];
yourViewBorder.frame = yourView.bounds;
yourViewBorder.path = [UIBezierPath bezierPathWithRect:yourView.bounds].CGPath;
[yourView.layer addSublayer:yourViewBorder];
Swift 3.1
var yourViewBorder = CAShapeLayer()
yourViewBorder.strokeColor = UIColor.black.cgColor
yourViewBorder.lineDashPattern = [2, 2]
yourViewBorder.frame = yourView.bounds
yourViewBorder.fillColor = nil
yourViewBorder.path = UIBezierPath(rect: yourView.bounds).cgPath
yourView.layer.addSublayer(yourViewBorder)
You can also set different types of design using pattern image like below example.
[yourView.layer setBorderWidth:5.0];
[yourView.layer setBorderColor:[[UIColor colorWithPatternImage:[UIImage imageNamed:#"DotedImage.png"]] CGColor]];///just add image name and create image with dashed or doted drawing and add here
Here you've to add <QuartzCore/QuartzCore> framework in the project and import it with below line in YourViewController.m file.
#import <QuartzCore/QuartzCore.h>
For those of you working in Swift, this class extension on UIView makes it easy. This was based on sunshineDev's answer.
extension UIView {
func addDashedBorder() {
let color = UIColor.red.cgColor
let shapeLayer:CAShapeLayer = CAShapeLayer()
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.bounds = shapeRect
shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 2
shapeLayer.lineJoin = CAShapeLayerLineJoin.round
shapeLayer.lineDashPattern = [6,3]
shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 5).cgPath
self.layer.addSublayer(shapeLayer)
}
}
To use it:
anyView.addDashedBorder()
Here is a UIView subclass that can work for any project, it also works for round views:
import UIKit
class CustomDashedView: UIView {
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = cornerRadius > 0
}
}
#IBInspectable var dashWidth: CGFloat = 0
#IBInspectable var dashColor: UIColor = .clear
#IBInspectable var dashLength: CGFloat = 0
#IBInspectable var betweenDashesSpace: CGFloat = 0
var dashBorder: CAShapeLayer?
override func layoutSubviews() {
super.layoutSubviews()
dashBorder?.removeFromSuperlayer()
let dashBorder = CAShapeLayer()
dashBorder.lineWidth = dashWidth
dashBorder.strokeColor = dashColor.cgColor
dashBorder.lineDashPattern = [dashLength, betweenDashesSpace] as [NSNumber]
dashBorder.frame = bounds
dashBorder.fillColor = nil
if cornerRadius > 0 {
dashBorder.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
} else {
dashBorder.path = UIBezierPath(rect: bounds).cgPath
}
layer.addSublayer(dashBorder)
self.dashBorder = dashBorder
}
}
This way you can edit from the Storyboard like this:
A pair of results:
Swift 3:
import UIKit
class UIViewWithDashedLineBorder: UIView {
override func draw(_ rect: CGRect) {
let path = UIBezierPath(roundedRect: rect, cornerRadius: 0)
UIColor.purple.setFill()
path.fill()
UIColor.orange.setStroke()
path.lineWidth = 5
let dashPattern : [CGFloat] = [10, 4]
path.setLineDash(dashPattern, count: 2, phase: 0)
path.stroke()
}
}
Use in a storyboard (as custom class) or directly in code:
let v = UIViewWithDashedLineBorder(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
Result:
Building upon what Prasad G has suggested I created a method inside a UIImage Extras class with the following:
- (CAShapeLayer *) addDashedBorderWithColor: (CGColorRef) color {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGSize frameSize = self.size;
CGRect shapeRect = CGRectMake(0.0f, 0.0f, frameSize.width, frameSize.height);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake( frameSize.width/2,frameSize.height/2)];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:color];
[shapeLayer setLineWidth:5.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
[NSArray arrayWithObjects:[NSNumber numberWithInt:10],
[NSNumber numberWithInt:5],
nil]];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
[shapeLayer setPath:path.CGPath];
return shapeLayer;
}
It's important to point out that if you define your shape's position as (0,0), the bottom corner of the border will be placed in the center of the image, that's why I set it to: (frameSize.width/2,frameSize.height/2)
I then use my method to get the dashed border using the UIImage of my UIImageView and add the CAShapeLayer as a sublayer of the UIImageView layer:
[myImageView.layer addSublayer:[myImageView.image addDashedBorderWithColor:[[UIColor whiteColor] CGColor]]];
Use CGContextSetLineDash() method.
CGFloat dashPattern[]= {3.0, 2};
context =UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// And draw with a blue fill color
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 4.0);
CGContextSetLineDash(context, 0.0, dashPattern, 2);
CGContextAddRect(context, self.bounds);
// Close the path
CGContextClosePath(context);
CGContextStrokePath(context);
// Fill & stroke the path
CGContextDrawPath(context, kCGPathFillStroke);
I think it will be helpful to you.
For this you need add CAShapeLayer for that particular object
CAShapeLayer * dotborder = [CAShapeLayer layer];
dotborder.strokeColor = [UIColor redColor].CGColor;//your own color
dotborder.fillColor = nil;
dotborder.lineDashPattern = #[#4, #2];//your own patten
[codeBtn.layer addSublayer:dotborder];
dotborder.path = [UIBezierPath bezierPathWithRect:codeBtn.bounds].CGPath;
dotborder.frame = codeBtn.bounds;
Swift 4.2
Based off rmooney's answer as a UIView extension with configurable parameters that have default values set.
Note this does not work if the view has self.translatesAutoresizingMaskIntoConstraints = false
extension UIView {
func addDashedBorder(_ color: UIColor = UIColor.black, withWidth width: CGFloat = 2, cornerRadius: CGFloat = 5, dashPattern: [NSNumber] = [3,6]) {
let shapeLayer = CAShapeLayer()
shapeLayer.bounds = bounds
shapeLayer.position = CGPoint(x: bounds.width/2, y: bounds.height/2)
shapeLayer.fillColor = nil
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = width
shapeLayer.lineJoin = CAShapeLayerLineJoin.round // Updated in swift 4.2
shapeLayer.lineDashPattern = dashPattern
shapeLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
self.layer.addSublayer(shapeLayer)
}
}
• Swift 5
• Works with autolayout
• Works with the corner radius
import UIKit
class DashedBorderView: UIView {
private let dashedLineColor = UIColor.black.cgColor
private let dashedLinePattern: [NSNumber] = [6, 3]
private let dashedLineWidth: CGFloat = 4
private let borderLayer = CAShapeLayer()
init() {
super.init(frame: CGRect.zero)
borderLayer.strokeColor = dashedLineColor
borderLayer.lineDashPattern = dashedLinePattern
borderLayer.backgroundColor = UIColor.clear.cgColor
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.lineWidth = dashedLineWidth
layer.addSublayer(borderLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
borderLayer.frame = bounds
borderLayer.path = UIBezierPath(roundedRect: rect, cornerRadius: layer.cornerRadius).cgPath
}
}
Swift version of the QuartzCore answer.
import QuartzCore
let dottedPattern = UIImage(named: "dottedPattern")
myView.layer.borderWidth = 1
myView.layer.borderColor = UIColor(patternImage: dottedPattern!).CGColor
The CAShapeLayer approach works, but the QuartzCore approach is better at handling a Table View reload, if the UIView is inside a cell.
For the image, you can use something like this (it's really small):
I tend to prefer vector over PNGs when I can get away with it:
Within Sketch, create a 4x4 pixel rectangle.
Make a total of four of these
Group them into a foursquare, alternating colors
Export the group as PDF
Within Images.xcassets, create a New Image Set called dottedPattern
Change the Scale Factors to Single Vector
Drop in your PDF
For Xamarin.iOS dashed/dotted border.
dottedLayer = new CAShapeLayer();
dottedLayer.StrokeColor = UIColor.FromRGB(202, 202, 208).CGColor;
dottedLayer.FillColor = null;
dottedLayer.LineDashPattern = new[] { new NSNumber(4), new NSNumber(2) };
dottedLayer.Path = UIBezierPath.FromRect(YourView.Bounds).CGPath; //for square
dottedLayer.Path = UIBezierPath.FromRoundedRect(YourView.Bounds, 5).CGPath; //for rounded corners
dottedLayer.Frame = YourView.Bounds;
YourView.Layer.AddSublayer(dottedLayer);
In Swift 3
let border = CAShapeLayer();
border.strokeColor = UIColor.black.cgColor;
border.fillColor = nil;
border.lineDashPattern = [4, 4];
border.path = UIBezierPath(rect: theView.bounds).cgPath
border.frame = theView.bounds;
theView.layer.addSublayer(border);
For Swift 5
extension UIView {
func addDashBorder() {
let color = UIColor.white.cgColor
let shapeLayer:CAShapeLayer = CAShapeLayer()
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.bounds = shapeRect
shapeLayer.name = "DashBorder"
shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 1.5
shapeLayer.lineJoin = .round
shapeLayer.lineDashPattern = [2,4]
shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 10).cgPath
self.layer.masksToBounds = false
self.layer.addSublayer(shapeLayer)
}
}
How to add
vw.addDashBorder()
How to remove border again
let _ = vw.layer.sublayers?.filter({$0.name == "DashBorder"}).map({$0.removeFromSuperlayer()})
This is if you wanted it in Swift 2
func addDashedLineBorderWithColor(color:UIColor) {
let _ = self.sublayers?.filter({$0.name == "DashedBorder"}).map({$0.removeFromSuperlayer()})
let border = CAShapeLayer();
border.name = "DashedBorder"
border.strokeColor = color.CGColor;
border.fillColor = nil;
border.lineDashPattern = [4, 4];
border.path = UIBezierPath(rect: self.bounds).CGPath
border.frame = self.bounds;
self.addSublayer(border);
}
try bellow code
- (void)drawRect:(CGRect)rect {
//// Color Declarations
UIColor* fillColor = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1];
UIColor* strokeColor = [UIColor colorWithRed: 0.29 green: 0.565 blue: 0.886 alpha: 1];
//// Rectangle Drawing
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius: 6];
[fillColor setFill];
[rectanglePath fill];
[strokeColor setStroke];
rectanglePath.lineWidth = 1;
CGFloat rectanglePattern[] = {6, 2, 6, 2};
[rectanglePath setLineDash: rectanglePattern count: 4 phase: 0];
[rectanglePath stroke];
[super drawRect:rect];
}
for one like bellow
Swift solution with custom class worked with autolayout
customized from #Iain Smith
class DashedBorderView: UIView {
#IBInspectable var cornerRadius: CGFloat = 4
#IBInspectable var borderColor: UIColor = UIColor.black
#IBInspectable var dashPaintedSize: Int = 2
#IBInspectable var dashUnpaintedSize: Int = 2
let dashedBorder = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
//custom initialization
self.layer.addSublayer(dashedBorder)
applyDashBorder()
}
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
applyDashBorder()
}
func applyDashBorder() {
dashedBorder.strokeColor = borderColor.cgColor
dashedBorder.lineDashPattern = [NSNumber(value: dashPaintedSize), NSNumber(value: dashUnpaintedSize)]
dashedBorder.fillColor = nil
dashedBorder.cornerRadius = cornerRadius
dashedBorder.path = UIBezierPath(rect: self.bounds).cgPath
dashedBorder.frame = self.bounds
}
}
extension UIView{
func addDashedLineBorder() {
let color = UIColor.black.cgColor
let shapeLayer:CAShapeLayer = CAShapeLayer()
let frameSize = (self.frame.size)
let shapeRect = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
shapeLayer.bounds = shapeRect
shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 1
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = [2,2]
shapeLayer.path = UIBezierPath(rect: shapeRect).cgPath
self.layer.addSublayer(shapeLayer)
}
}
and call this function in viewdidLoad() with delay:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// Your code with delay
self.YourView.addDashedBorder()
}
Swift 4,5 :- addDashedBorder
extension UIView {
func setCellDeshBorder(color: UIColor = .appBlue?.withAlphaComponent(0.50) ?? UIColor()) {
let shapeLayer:CAShapeLayer = CAShapeLayer()
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.bounds = shapeRect
shapeLayer.name = "DashBorder"
shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = 1.5
shapeLayer.lineJoin = .round
shapeLayer.lineDashPattern = [2,4]
shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 10).cgPath
self.layer.masksToBounds = false
self.layer.addSublayer(shapeLayer)
}
func removeCellDeshBorder() {
_ = self.layer.sublayers?.filter({$0.name == "DashBorder"}).map({$0.removeFromSuperlayer()})
}
}
I ended up creating a IB Designable using some of #Chris implementation:
CurvedDashedBorderUIVIew.h:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface CurvedDashedBorderUIVIew : UIView
#property (nonatomic) IBInspectable CGFloat cornerRadius;
#property (nonatomic) IBInspectable UIColor *borderColor;
#property (nonatomic) IBInspectable int dashPaintedSize;
#property (nonatomic) IBInspectable int dashUnpaintedSize;
#property (strong, nonatomic) CAShapeLayer *border;
#end
CurvedDashedBorderUIVIew.m:
#import "CurvedDashedBorderUIVIew.h"
#implementation CurvedDashedBorderUIVIew
- (instancetype)init
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setup];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
-(void)setup
{
_border = [CAShapeLayer layer];
[self.layer addSublayer:_border];
}
-(void)layoutSubviews {
[super layoutSubviews];
self.layer.cornerRadius = self.cornerRadius;
_border.strokeColor = self.borderColor.CGColor;
_border.fillColor = nil;
_border.lineDashPattern = #[[NSNumber numberWithInt:_dashPaintedSize],
[NSNumber numberWithInt:_dashUnpaintedSize]];
_border.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.cornerRadius].CGPath;
_border.frame = self.bounds;
}
#end
then just set it up in the xib/storyboard:
You can simply create a IBDesignable class like this:
import UIKit
#IBDesignable
class BorderedView: UIView {
#IBInspectable var cornerRadius: CGFloat = 0
#IBInspectable var borderWidth: CGFloat = 0
#IBInspectable var borderColor: UIColor = UIColor.clear
override func draw(_ rect: CGRect) {
let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
path.lineWidth = borderWidth
borderColor.setStroke()
let dashPattern : [CGFloat] = [10, 4]
path.setLineDash(dashPattern, count: 2, phase: 0)
path.stroke()
}
}
Then just subclass your view with BorderedView from Xcode.
This way you can set the border color and border width very easily from the interface builder!
Swift 5+
import UIKit
class DashedBorderView: UIView {
private let borderLayer = CAShapeLayer()
init(color: UIColor, width: CGFloat = 1) {
super.init(frame: CGRect.zero)
let pattern: [NSNumber] = [NSNumber(value: Float(5 * width)), NSNumber(value: Float(3 * width))]
borderLayer.backgroundColor = nil
borderLayer.fillColor = nil
borderLayer.lineDashPattern = pattern
borderLayer.lineWidth = width
borderLayer.strokeColor = color.cgColor
layer.addSublayer(borderLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
borderLayer.frame = bounds
borderLayer.path = UIBezierPath(roundedRect: rect, cornerRadius: layer.cornerRadius).cgPath
}
}
How to use:
// f.e. inside UIViewController
let viewWithDashedBorder = DashedBorderView(color: .red, width: 2)
view.addSubview(viewWithDashedBorder)
In swift 4 I created an UIView extension with the following function:
func borderDash(withRadius cornerRadius: Float, borderWidth: Float, borderColor: UIColor, dashSize: Int) {
let currentFrame = self.bounds
let shapeLayer = CAShapeLayer()
let path = CGMutablePath()
let radius = CGFloat(cornerRadius)
// Points - Eight points that define the round border. Each border is defined by two points.
let topLeftPoint = CGPoint(x: radius, y: 0)
let topRightPoint = CGPoint(x: currentFrame.size.width - radius, y: 0)
let middleRightTopPoint = CGPoint(x: currentFrame.size.width, y: radius)
let middleRightBottomPoint = CGPoint(x: currentFrame.size.width, y: currentFrame.size.height - radius)
let bottomRightPoint = CGPoint(x: currentFrame.size.width - radius, y: currentFrame.size.height)
let bottomLeftPoint = CGPoint(x: radius, y: currentFrame.size.height)
let middleLeftBottomPoint = CGPoint(x: 0, y: currentFrame.size.height - radius)
let middleLeftTopPoint = CGPoint(x: 0, y: radius)
// Points - Four points that are the center of the corners borders.
let cornerTopRightCenter = CGPoint(x: currentFrame.size.width - radius, y: radius)
let cornerBottomRightCenter = CGPoint(x: currentFrame.size.width - radius, y: currentFrame.size.height - radius)
let cornerBottomLeftCenter = CGPoint(x: radius, y: currentFrame.size.height - radius)
let cornerTopLeftCenter = CGPoint(x: radius, y: radius)
// Angles - The corner radius angles.
let topRightStartAngle = CGFloat(Double.pi * 3 / 2)
let topRightEndAngle = CGFloat(0)
let bottomRightStartAngle = CGFloat(0)
let bottmRightEndAngle = CGFloat(Double.pi / 2)
let bottomLeftStartAngle = CGFloat(Double.pi / 2)
let bottomLeftEndAngle = CGFloat(Double.pi)
let topLeftStartAngle = CGFloat(Double.pi)
let topLeftEndAngle = CGFloat(Double.pi * 3 / 2)
// Drawing a border around a view.
path.move(to: topLeftPoint)
path.addLine(to: topRightPoint)
path.addArc(center: cornerTopRightCenter,
radius: radius,
startAngle: topRightStartAngle,
endAngle: topRightEndAngle,
clockwise: false)
path.addLine(to: middleRightBottomPoint)
path.addArc(center: cornerBottomRightCenter,
radius: radius,
startAngle: bottomRightStartAngle,
endAngle: bottmRightEndAngle,
clockwise: false)
path.addLine(to: bottomLeftPoint)
path.addArc(center: cornerBottomLeftCenter,
radius: radius,
startAngle: bottomLeftStartAngle,
endAngle: bottomLeftEndAngle,
clockwise: false)
path.addLine(to: middleLeftTopPoint)
path.addArc(center: cornerTopLeftCenter,
radius: radius,
startAngle: topLeftStartAngle,
endAngle: topLeftEndAngle,
clockwise: false)
// Path is set as the shapeLayer object's path.
shapeLayer.path = path;
shapeLayer.backgroundColor = UIColor.clear.cgColor
shapeLayer.frame = currentFrame
shapeLayer.masksToBounds = false
shapeLayer.setValue(0, forKey: "isCircle")
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = borderColor.cgColor
shapeLayer.lineWidth = CGFloat(borderWidth)
shapeLayer.lineDashPattern = [NSNumber(value: dashSize), NSNumber(value: dashSize)]
shapeLayer.lineCap = kCALineCapRound
self.layer.addSublayer(shapeLayer)
self.layer.cornerRadius = radius;
}
If you want this to work with cornerRadius then try this
tagView.clipsToBounds = YES;
tagView.layer.cornerRadius = 20.0f;
tagView.backgroundColor = [UIColor groupTableViewBackgroundColor];
CAShapeLayer *yourViewBorder = [CAShapeLayer layer];
yourViewBorder.strokeColor = [UIColor blackColor].CGColor;
yourViewBorder.fillColor = nil;
yourViewBorder.lineDashPattern = #[#2, #2];
yourViewBorder.frame = tagView.bounds;
// Create the path for to make circle
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:tagView.bounds
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(20, 20)];
yourViewBorder.path = maskPath.CGPath;
[tagView.layer addSublayer:yourViewBorder];
Improvement for #Chris's answer.
extension UIView {
func dashLinedBorder() -> CAShapeLayer {
let viewBorder = CAShapeLayer()
viewBorder.strokeColor = UIColor.black.cgColor
viewBorder.lineDashPattern = [4, 2]
viewBorder.fillColor = nil
self.layer.addSublayer(viewBorder)
return viewBorder
}
}
Define your CAShapeLayer inside the ViewController,
var viewBillingProofCAShapeLayer: CAShapeLayer!
override func viewDidLoad() {
self.viewBillingProofCAShapeLayer = self.viewBillingProofInner.dashLinedBorder()
}
then override viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
self.viewBillingProofCAShapeLayer.path = UIBezierPath(roundedRect: self.viewBillingProofInner.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 4, height: 4)).cgPath
self.viewBillingProofCAShapeLayer.frame = self.viewBillingProofInner.bounds
}
SwiftUI
var body: some View {
Rectangle()
.strokeBorder(style: StrokeStyle(lineWidth: 4, dash: [10]))
}

Resources