I want to create this badge or direction-type view in swift. I cannot use image because text can be long in other languages. I want to make it with UIView. I want to achieve this:
I managed to make it with sharp points with this code
class ArrowView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() -> Void {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0)) // Starting point: Top left
path.addLine(to: CGPoint(x: self.frame.width - self.frame.height/2, y: 0)) // Move to the top right before the arrow point
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height/2)) // Move to the tip of the arrow
path.addLine(to: CGPoint(x: self.frame.width - self.frame.height/2, y: self.frame.height)) // Move to the bottom right before the arrow point
path.addLine(to: CGPoint(x: 0, y: self.frame.height)) // Move to the bottom left
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.black.cgColor // Choose the color of the arrow
self.layer.addSublayer(shapeLayer)
}
override func layoutSubviews() {
self.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
self.commonInit()
}
}
How to make line joint rounded like the actual design?
Another option...
Work with CGMutablePath
and take advantage of tangent curves:
@IBDesignable
class ArrowView: UIView {
@IBInspectable var cornerRadius: CGFloat = 6.0 { didSet { setNeedsLayout() } }
@IBInspectable var color: UIColor = .black { didSet { setNeedsLayout() } }
override class var layerClass: AnyClass { CAShapeLayer.self }
var shapeLayer: CAShapeLayer { layer as! CAShapeLayer }
override func layoutSubviews() {
super.layoutSubviews()
// may want to include this to make sure self's background is clear
self.backgroundColor = .clear
// set the shape fill color
shapeLayer.fillColor = color.cgColor
// use rect as reference to bounds
// makes it easy if we want to inset the shape
let rect: CGRect = bounds
let arrowInset: CGFloat = rect.height / 2.0 //- cornerRadius / 2.0
// top-left corner
let pt1: CGPoint = .init(x: rect.minX, y: rect.minY)
// top-right curve
let pt2: CGPoint = .init(x: rect.maxX - arrowInset, y: rect.minY)
// arrow point
let pt3: CGPoint = .init(x: rect.maxX, y: rect.midY)
// bottom-right curve
let pt4: CGPoint = .init(x: rect.maxX - arrowInset, y: rect.maxY)
// bottom-left corner
let pt5: CGPoint = .init(x: rect.minX, y: rect.maxY)
let cPath: CGMutablePath = CGMutablePath()
cPath.move(to: pt1)
cPath.addArc(tangent1End: pt2, tangent2End: pt3, radius: cornerRadius)
cPath.addArc(tangent1End: pt3, tangent2End: pt4, radius: cornerRadius)
cPath.addArc(tangent1End: pt4, tangent2End: pt5, radius: cornerRadius)
cPath.addLine(to: pt5)
cPath.closeSubpath()
shapeLayer.path = cPath
}
}
Note: I used the same @IBInspectable
properties to make it easy for you to compare with Rob's implementation.