How to use lineJoin and lineJoinStyle with a UIBezierPath? - ios

I have a mask applied to a view using CAShapeLayer and UIBezierPath. I'd like to add a rounding effect to the line joins but it's not working. How do I round the corners of this shape?
You can plug the following into an Xcode playground.
import PlaygroundSupport
import UIKit
private class ProfileImageView: UIView {
private let imageView = UIImageView()
var image: UIImage?
override init(frame: CGRect) {
super.init(frame: frame)
imageView.clipsToBounds = true
imageView.backgroundColor = UIColor.black
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imageView)
imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
imageView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
return nil
}
override func draw(_ rect: CGRect) {
let h = rect.height
let w = rect.width
let path = UIBezierPath()
let shapeLayer = CAShapeLayer()
path.move(to: .zero)
path.addLine(to: CGPoint(x: w-32, y: 0))
path.addLine(to: CGPoint(x: w, y: 32))
path.addLine(to: CGPoint(x: w, y: h))
path.addLine(to: CGPoint(x: 32, y: h))
path.addLine(to: CGPoint(x: 0, y: h-32))
path.close()
path.lineJoinStyle = .round
shapeLayer.lineJoin = .round
shapeLayer.path = path.cgPath
layer.mask = shapeLayer
imageView.image = image
}
}
class VC: UIViewController {
override func loadView() {
view = UIView()
view.backgroundColor = .gray
let imgView = ProfileImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imgView)
imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imgView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
imgView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64).isActive = true
imgView.heightAnchor.constraint(equalTo: view.widthAnchor, constant: -64).isActive = true
}
}
PlaygroundPage.current.liveView = VC()

lineJoinStyle is only for stroked paths. Since yours is a mask, you need a filled path instead so I think you'll need to use path.addCurve to achieve rounded corners in your mask. Or depending on your shape and size you may be able to just apply lineWidth, strokeColor and lineJoinStyle to your CAShapeLayer and get the rounded effect you're looking for.

Still trying to guess at your goal, but maybe this is what you're looking for?
private class ProfileImageView: UIImageView {
public var cornerRadius: Double = 16 {
didSet {
setNeedsLayout()
}
}
public var angleRadius: Double = 24 {
didSet {
setNeedsLayout()
}
}
public var angleIndent: CGFloat = 32 {
didSet {
setNeedsLayout()
}
}
private let shapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
contentMode = .scaleAspectFill
layer.mask = shapeLayer
}
override func layoutSubviews() {
super.layoutSubviews()
let rect = bounds
let path = CGMutablePath()
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY),
tangent2End: CGPoint(x: rect.maxX, y: rect.minY),
radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: rect.maxX - angleIndent, y: rect.minY),
tangent2End: CGPoint(x: rect.maxX, y: rect.minY + angleIndent),
radius: angleRadius)
path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.minY + angleIndent),
tangent2End: CGPoint(x: rect.maxX, y: rect.maxY),
radius: angleRadius)
path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY),
tangent2End: CGPoint(x: rect.minX, y: rect.maxY),
radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: rect.minX + angleIndent, y: rect.maxY),
tangent2End: CGPoint(x: rect.minX, y: rect.maxY - angleIndent),
radius: angleRadius)
path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.maxY - angleIndent),
tangent2End: CGPoint(x: rect.minX, y: rect.minY),
radius: angleRadius)
path.closeSubpath()
shapeLayer.path = path
}
}
class VC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
guard let img = UIImage(named: "sampleImage") else {
fatalError("Could not load sample image!!")
}
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 20
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let imgView1 = ProfileImageView(frame: .zero)
imgView1.image = img
let imgView2 = ProfileImageView(frame: .zero)
imgView2.image = img
// top view uses default properties,
stackView.addArrangedSubview(imgView1)
// slightly different properties for the bottom view
imgView2.cornerRadius = 24
imgView2.angleRadius = 32
imgView2.angleIndent = 48
stackView.addArrangedSubview(imgView2)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
stackView.widthAnchor.constraint(equalTo: g.widthAnchor, constant: -100.0),
imgView1.heightAnchor.constraint(equalTo: imgView1.widthAnchor),
])
}
}

Related

CustomView with squiggle(wavy) top.(Swift)

I am trying to create a custom view a squiggle top and add an image view in the middle.
Something like this:
But I am not so used to UIBezierPath, so I am pretty confused.
This is what I have done so far.
class DemoView: UIView {
var path: UIBezierPath!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
complexShape()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
complexShape()
}
func complexShape() {
path = UIBezierPath()
path.move(to: CGPoint(x: 0.0, y: 0.0))
path.addLine(to: CGPoint(x: self.frame.size.width/2 - 50.0, y: 0.0))
path.addLine(to: CGPoint(x: self.frame.size.width/2, y: 0.0))
path.addCurve(to: CGPoint(x: self.frame.size.width, y: 50.0),
controlPoint1: CGPoint(x: self.frame.size.width + 50.0, y: 25.0),
controlPoint2: CGPoint(x: self.frame.size.width - 150.0, y: 50.0))
path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height))
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
self.layer.mask = shapeLayer
}
}
extension CGFloat {
func toRadians() -> CGFloat {
return self * .pi / 180.0
}
}
The method below will let you add the background wave effect to another view. All you then need to do for the foreground square is add another view. Play with the constants to change the wave shape/height.
func addWaveBackground(to view: UIView){
let leftDrop:CGFloat = 0.4
let rightDrop: CGFloat = 0.3
let leftInflexionX: CGFloat = 0.4
let leftInflexionY: CGFloat = 0.47
let rightInflexionX: CGFloat = 0.6
let rightInflexionY: CGFloat = 0.22
let backView = UIView(frame: view.frame)
backView.backgroundColor = .gray
view.addSubview(backView)
let backLayer = CAShapeLayer()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x:0, y: view.frame.height * leftDrop))
path.addCurve(to: CGPoint(x:view.frame.width, y: view.frame.height * rightDrop),
controlPoint1: CGPoint(x: view.frame.width * leftInflexionX, y: view.frame.height * leftInflexionY),
controlPoint2: CGPoint(x: view.frame.width * rightInflexionX, y: view.frame.height * rightInflexionY))
path.addLine(to: CGPoint(x:view.frame.width, y: 0))
path.close()
backLayer.fillColor = UIColor.blue.cgColor
backLayer.path = path.cgPath
backView.layer.addSublayer(backLayer)
}
Pass in the view you want to add the wave effect to (this will usually be the VC's main view).

How to draw a custom rounded rectangle in SWIFT?

I'm trying to draw a shape shown on the upper image programmatically.
This shape has custom rounded corners.
view.layer.cornerRadius = some value less than half diameter
This didn't work. Setting cornerRadius draws straight lines on every side(as seen on the bottom image) but the shape I'm trying to draw has no straight lines at all and it's not an oval.
I also tried below without luck. This code just draws an oval.
var path = UIBezierPath(ovalIn: CGRect(x: 000, y: 000, width: 000, height: 000))
I believe this can not be done by setting cornerRadius.
There should be something more.
I have no idea what class should I use and how.
Please anybody give me some direction.
Thanks!
I accomplished this by drawing a quadratic.
let dimention: CGFloat = some value
let path = UIBezierPath()
path.move(to: CGPoint(x: dimension/2, y: 0))
path.addQuadCurve(to: CGPoint(x: dimension, y: dimension/2),
controlPoint: CGPoint(x: dimension, y: 0))
path.addQuadCurve(to: CGPoint(x: dimension/2, y: dimension),
controlPoint: CGPoint(x: dimension, y: dimension))
path.addQuadCurve(to: CGPoint(x: 0, y: dimension/2),
controlPoint: CGPoint(x: 0, y: dimension))
path.addQuadCurve(to: CGPoint(x: dimension/2, y: 0),
controlPoint: CGPoint(x: 0, y: 0))
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 0, y: 0)
self.someView.layer.addSublayer(shapeLayer)
You have to add cipsToBounds to the code for getting the image with given cornerRadius.
Try the blow codes ,
view.layer.cornerRadius = some value
view.clipsToBounds = true
You can use following path for your custom shape
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 150, height: 150), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 8, height: 8))
You can change width and height according to your requirements and UI
class RoundView: UIView {
var roundCorner: UIRectCorner? = nil
var roundRadius:CGFloat = 0
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed(“nib name”, owner: self, options: nil))
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
isUserInteractionEnabled = true
}
override func layoutSubviews() {
super.layoutSubviews()
if roundCorner != nil {
// self.roundCorners([.topRight, .bottomLeft, .bottomRight], radius: 15)
self.roundCorners(roundCorner!, radius: roundRadius)
}
}
}

How to create one sided arrowed label

I am not looking out for code snippet. I am just curious to know how to develop a UI component which is shown below. I have multiple thoughts on creating it, but would love to know the best approach
Create a label and do some operations on its layer
Set background image and add text on label
Set image which has text on it
What will be the good approach to develop it. Any suggestions please.
You want to display a single line of text. You can use a UILabel for that.
You have a shape you want to fill. You can use a CAShapeLayer for that. It's best to wrap the shape layer in a UIView subclass so that UIKit can lay it out properly.
You want to put the text over the shape, so use a parent view to combine the label and the shape as subviews.
import UIKit
class TagView: UIView {
init() {
super.init(frame: .zero)
addSubview(background)
addSubview(label)
background.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentHuggingPriority(.defaultHigh + 10, for: .horizontal)
label.setContentHuggingPriority(.defaultHigh + 10, for: .vertical)
NSLayoutConstraint.activate([
background.heightAnchor.constraint(equalTo: label.heightAnchor, constant: 4),
background.centerYAnchor.constraint(equalTo: label.centerYAnchor),
background.widthAnchor.constraint(equalTo: label.widthAnchor, constant: 20),
background.centerXAnchor.constraint(equalTo: label.centerXAnchor, constant: -2),
leadingAnchor.constraint(equalTo: background.leadingAnchor),
trailingAnchor.constraint(equalTo: background.trailingAnchor),
topAnchor.constraint(equalTo: background.topAnchor),
bottomAnchor.constraint(equalTo: background.bottomAnchor),
])
}
let label = UILabel()
private let background = BackgroundView()
private class BackgroundView: UIView {
override class var layerClass: AnyClass { return CAShapeLayer.self }
override func layoutSubviews() {
super.layoutSubviews()
layoutShape()
}
private func layoutShape() {
let layer = self.layer as! CAShapeLayer
layer.fillColor = #colorLiteral(red: 0.731529057, green: 0.8821037412, blue: 0.9403864741, alpha: 1)
layer.strokeColor = nil
let bounds = self.bounds
let h2 = bounds.height / 2
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: h2))
path.addLine(to: CGPoint(x: h2, y: 0))
path.addLine(to: CGPoint(x: bounds.maxX - h2, y: 0))
path.addArc(withCenter: CGPoint(x: bounds.maxX - h2, y: h2), radius: h2, startAngle: -.pi/2, endAngle: .pi/2, clockwise: true)
path.addLine(to: CGPoint(x: h2, y: bounds.maxY))
path.close()
layer.path = path.cgPath
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
import PlaygroundSupport
let root = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
root.backgroundColor = .white
PlaygroundPage.current.liveView = root
let tag = TagView()
tag.translatesAutoresizingMaskIntoConstraints = false
tag.label.text = "CURRENT"
root.addSubview(tag)
NSLayoutConstraint.activate([
tag.centerXAnchor.constraint(equalTo: root.centerXAnchor),
tag.centerYAnchor.constraint(equalTo: root.centerYAnchor),
])
Result:

Add shadow to UIView whose shape has been changed by UIBezierPath

I've created an extension for UIView that allows me to make a concave shape.
extension UIView {
func createConcave(depth: CGFloat) {
let width = self.bounds.width
let height = self.bounds.height
let path = UIBezierPath()
let p0 = CGPoint(x: 0, y: 0)
let p2 = CGPoint(x: width, y: 0)
let p1 = CGPoint(x: width / 2, y: depth)
path.move(to: p0)
path.addQuadCurve(to: p2, controlPoint: p1)
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: 0, y: height))
path.addLine(to: p0)
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
self.layer.masksToBounds = false
}
}
What would be a good solution to add a shadow to the view that matches the shape? Would I have to specify the shadow path to be the same path as the concave shape?
You are masking the layer to the path. Thus anything, including the shadow, will be clipped by that mask.
Instead of masking, add sublayer.
E.g.
#IBDesignable
class ConcaveView: UIView {
#IBInspectable var depth: CGFloat = 10 { didSet { updatePath() } }
#IBInspectable var fillColor: UIColor = .red { didSet { shapeLayer.fillColor = fillColor.cgColor } }
private lazy var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = fillColor.cgColor
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowRadius = 5
shapeLayer.shadowOpacity = 1
shapeLayer.shadowOffset = .zero
return shapeLayer
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
layer.addSublayer(shapeLayer)
clipsToBounds = false
}
override func layoutSubviews() {
super.layoutSubviews()
updatePath()
}
func updatePath() {
let path = UIBezierPath()
let point0 = CGPoint(x: bounds.minX, y: bounds.minY)
let point2 = CGPoint(x: bounds.maxX, y: bounds.minY)
let point1 = CGPoint(x: bounds.width / 2, y: bounds.minY + depth)
path.move(to: point0)
path.addQuadCurve(to: point2, controlPoint: point1)
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.addLine(to: point0)
shapeLayer.path = path.cgPath
}
}
That yields:

Display only the corners of a UIView

How to display only the corners of a UIView?
let view = UIView()
view.layer.borderColor = UIColor.white.cgColor
view.layer.borderWidth = 2
let maskframe = UIView(frame: CGRect(x:0, y:0,
width:view.frame.width, height:view.frame.height))
view.layer.mask = maskframe.layer.`
This masks only the right edge and i dont understand how it works either.
Try with this class, here I use a custom view drawing using CoreGraphics, added some Inspectable variables to help with customization
//
// CornerView.swift
// CornersViewSO
//
// Created by Reinier Melian on 5/31/17.
// Copyright © 2017 Reinier Melian. All rights reserved.
//
import UIKit
import CoreGraphics
#IBDesignable
class CornerView: UIView {
#IBInspectable
var sizeMultiplier : CGFloat = 0.2{
didSet{
self.draw(self.bounds)
}
}
#IBInspectable
var lineWidth : CGFloat = 2{
didSet{
self.draw(self.bounds)
}
}
#IBInspectable
var lineColor : UIColor = UIColor.black{
didSet{
self.draw(self.bounds)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.backgroundColor = UIColor.clear
}
func drawCorners()
{
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.setLineWidth(lineWidth)
currentContext?.setStrokeColor(lineColor.cgColor)
//first part of top left corner
currentContext?.beginPath()
currentContext?.move(to: CGPoint(x: 0, y: 0))
currentContext?.addLine(to: CGPoint(x: self.bounds.size.width*sizeMultiplier, y: 0))
currentContext?.strokePath()
//top rigth corner
currentContext?.beginPath()
currentContext?.move(to: CGPoint(x: self.bounds.size.width - self.bounds.size.width*sizeMultiplier, y: 0))
currentContext?.addLine(to: CGPoint(x: self.bounds.size.width, y: 0))
currentContext?.addLine(to: CGPoint(x: self.bounds.size.width, y: self.bounds.size.height*sizeMultiplier))
currentContext?.strokePath()
//bottom rigth corner
currentContext?.beginPath()
currentContext?.move(to: CGPoint(x: self.bounds.size.width, y: self.bounds.size.height - self.bounds.size.height*sizeMultiplier))
currentContext?.addLine(to: CGPoint(x: self.bounds.size.width, y: self.bounds.size.height))
currentContext?.addLine(to: CGPoint(x: self.bounds.size.width - self.bounds.size.width*sizeMultiplier, y: self.bounds.size.height))
currentContext?.strokePath()
//bottom left corner
currentContext?.beginPath()
currentContext?.move(to: CGPoint(x: self.bounds.size.width*sizeMultiplier, y: self.bounds.size.height))
currentContext?.addLine(to: CGPoint(x: 0, y: self.bounds.size.height))
currentContext?.addLine(to: CGPoint(x: 0, y: self.bounds.size.height - self.bounds.size.height*sizeMultiplier))
currentContext?.strokePath()
//second part of top left corner
currentContext?.beginPath()
currentContext?.move(to: CGPoint(x: 0, y: self.bounds.size.height*sizeMultiplier))
currentContext?.addLine(to: CGPoint(x: 0, y: 0))
currentContext?.strokePath()
}
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
super.draw(rect)
self.drawCorners()
}
}
EDITED
Example Code of Use
import UIKit
class ViewController: UIViewController {
var cornerViewCode : CornerView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.cornerViewCode = CornerView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
self.view.addSubview(self.cornerViewCode!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
this is how it looks
Hope this helps
Check out this UIView:
class RectangleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
let aPath = UIBezierPath()
UIColor.black.set()
aPath.move(to: CGPoint(x: rect.minX, y: 0.1*rect.maxY))
aPath.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
aPath.addLine(to: CGPoint(x: 20, y: rect.minY))
aPath.stroke()
aPath.move(to: CGPoint(x: rect.maxX - 0.1*rect.maxX, y: rect.minY))
aPath.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
aPath.addLine(to: CGPoint(x: rect.maxX, y: 0.1*rect.maxY))
aPath.stroke()
aPath.move(to: CGPoint(x: rect.maxX, y: rect.maxY - 0.1*rect.maxY))
aPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
aPath.addLine(to: CGPoint(x: rect.maxX - 0.1*rect.maxX, y: rect.maxY))
aPath.stroke()
aPath.move(to: CGPoint(x: rect.minX + 0.1*rect.maxX, y: rect.maxY))
aPath.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
aPath.addLine(to: CGPoint(x: rect.minX, y: rect.maxY - 0.1*rect.maxY))
aPath.stroke()
}
}
If you don't wish to subclass UIView. The same can be achieved using Autolayout.
Swift Version: 3.0
Xcode Version 8.2.1
func setupAutoLayout() {
let cameraViewWidth : Float = Float(UIScreen.main.bounds.size.width * 0.50)
let edgeLength : Float = cameraViewWidth * 0.10
cameraView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
cameraView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
cameraView.widthAnchor.constraint(equalToConstant: CGFloat(cameraViewWidth)).isActive = true
cameraView.heightAnchor.constraint(equalToConstant: CGFloat(cameraViewWidth)).isActive = true
//Top Left
topLeftEdgeView.leadingAnchor.constraint(equalTo: cameraView.leadingAnchor).isActive = true
topLeftEdgeView.topAnchor.constraint(equalTo: cameraView.topAnchor).isActive = true
topLeftEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
topLeftEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
topLeftSideEdgeView.leadingAnchor.constraint(equalTo: cameraView.leadingAnchor).isActive = true
topLeftSideEdgeView.topAnchor.constraint(equalTo: cameraView.topAnchor).isActive = true
topLeftSideEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
topLeftSideEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
//Top Right
topRightEdgeView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor).isActive = true
topRightEdgeView.topAnchor.constraint(equalTo: cameraView.topAnchor).isActive = true
topRightEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
topRightEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
topRightSideEdgeView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor).isActive = true
topRightSideEdgeView.topAnchor.constraint(equalTo: cameraView.topAnchor).isActive = true
topRightSideEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
topRightSideEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
//Bottom Left
bottomLeftEdgeView.leadingAnchor.constraint(equalTo: cameraView.leadingAnchor).isActive = true
bottomLeftEdgeView.bottomAnchor.constraint(equalTo: cameraView.bottomAnchor, constant : -CGFloat(1.0)).isActive = true
bottomLeftEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
bottomLeftEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
bottomLeftSideEdgeView.leadingAnchor.constraint(equalTo: cameraView.leadingAnchor).isActive = true
bottomLeftSideEdgeView.topAnchor.constraint(equalTo: cameraView.bottomAnchor, constant : -CGFloat(edgeLength + 1.0)).isActive = true
bottomLeftSideEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
bottomLeftSideEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
//Bottom Right
bottomRightEdgeView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor).isActive = true
bottomRightEdgeView.bottomAnchor.constraint(equalTo: cameraView.bottomAnchor, constant : -CGFloat(1.0)).isActive = true
bottomRightEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
bottomRightEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
bottomRightSideEdgeView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor).isActive = true
bottomRightSideEdgeView.topAnchor.constraint(equalTo: cameraView.bottomAnchor, constant : -CGFloat(edgeLength + 1.0)).isActive = true
bottomRightSideEdgeView.widthAnchor.constraint(equalToConstant: CGFloat(1.0)).isActive = true
bottomRightSideEdgeView.heightAnchor.constraint(equalToConstant: CGFloat(edgeLength)).isActive = true
}
PS: Where cameraView, topLeftEdgeView, topLeftSideEdgeView... etc all are UIViews.
Here our edgeLength is dependent on cameraView width(currently 10%).
At 0.50 this will draw complete border around cameraView.
Don't Forget to add translatesAutoresizingMaskIntoConstraints = false for all views involved!!
I was able to achieve the same via BeizerPath and CAShapeLayer. Hence sharing the same.
Code created on Xcode 9.3 using Swift 4.0. Tested on iOS 10.0 and iOS 11.3
func createCorners() -> Void {
//Calculate the length of corner to be shown
let cornerLengthToShow = self.bounds.size.height * 0.10
print(cornerLengthToShow)
// Create Paths Using BeizerPath for all four corners
let topLeftCorner = UIBezierPath()
topLeftCorner.move(to: CGPoint(x: self.bounds.minX, y: self.bounds.minY + cornerLengthToShow))
topLeftCorner.addLine(to: CGPoint(x: self.bounds.minX, y: self.bounds.minY))
topLeftCorner.addLine(to: CGPoint(x: self.bounds.minX + cornerLengthToShow, y: self.bounds.minY))
let topRightCorner = UIBezierPath()
topRightCorner.move(to: CGPoint(x: self.bounds.maxX - cornerLengthToShow, y: self.bounds.minY))
topRightCorner.addLine(to: CGPoint(x: self.bounds.maxX, y: self.bounds.minY))
topRightCorner.addLine(to: CGPoint(x: self.bounds.maxX, y: self.bounds.minY + cornerLengthToShow))
let bottomRightCorner = UIBezierPath()
bottomRightCorner.move(to: CGPoint(x: self.bounds.maxX, y: self.bounds.maxY - cornerLengthToShow))
bottomRightCorner.addLine(to: CGPoint(x: self.bounds.maxX, y: self.bounds.maxY))
bottomRightCorner.addLine(to: CGPoint(x: self.bounds.maxX - cornerLengthToShow, y: self.bounds.maxY ))
let bottomLeftCorner = UIBezierPath()
bottomLeftCorner.move(to: CGPoint(x: self.bounds.minX, y: self.bounds.maxY - cornerLengthToShow))
bottomLeftCorner.addLine(to: CGPoint(x: self.bounds.minX, y: self.bounds.maxY))
bottomLeftCorner.addLine(to: CGPoint(x: self.bounds.minX + cornerLengthToShow, y: self.bounds.maxY))
let combinedPath = CGMutablePath()
combinedPath.addPath(topLeftCorner.cgPath)
combinedPath.addPath(topRightCorner.cgPath)
combinedPath.addPath(bottomRightCorner.cgPath)
combinedPath.addPath(bottomLeftCorner.cgPath)
let shapeLayer = CAShapeLayer()
shapeLayer.path = combinedPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3
layer.addSublayer(shapeLayer)
}

Resources