Reputation: 6581
I have this code to draw a rectangle which is rounded rect only on one side.
override func draw(_ rect: CGRect) {
// Drawing code
guard let context = UIGraphicsGetCurrentContext() else { return }
let lineWidth = CGFloat(4)
let pathRect = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
let path = UIBezierPath(roundedRect: pathRect.inset(by: UIEdgeInsets(top: lineWidth, left: lineWidth, bottom: lineWidth, right: 0)), byRoundingCorners: [.topLeft, .bottomLeft], cornerRadii: CGSize(width: 7, height: 7))
context.setFillColor(UIColor.black.cgColor)
path.fill()
context.setLineWidth(lineWidth)
}
I want to stroke it with red color on all but the right edge (no stroke on the right edge). How do I do it?
Upvotes: 2
Views: 1044
Reputation: 438222
You’ll have to create your own path.
A couple of observations:
Don’t use the rect
parameter. The rect
is what is being asked to being drawn at this point in time, which may not be the entire view. Use bounds
when figuring out what the overall path should be.
I might inset the path so that the stroke stays within the bounds of the view.
You can make this @IBDesignable
if you want to also be able to see it rendered in IB.
You don’t really need UIGraphicsGetCurrentContext()
. The UIKit
methods fill()
, stroke()
, setFill()
, and setStroke()
methods automatically use the current context.
Thus:
@IBDesignable
class OpenRightView: UIView {
@IBInspectable var lineWidth: CGFloat = 4 { didSet { setNeedsDisplay() } }
@IBInspectable var radius: CGFloat = 7 { didSet { setNeedsDisplay() } }
@IBInspectable var fillColor: UIColor = .black { didSet { setNeedsDisplay() } }
@IBInspectable var strokeColor: UIColor = .red { didSet { setNeedsDisplay() } }
override func draw(_ rect: CGRect) {
let pathRect = bounds.inset(by: .init(top: lineWidth / 2, left: lineWidth / 2, bottom: lineWidth / 2, right: 0))
let path = UIBezierPath()
path.lineWidth = lineWidth
path.move(to: CGPoint(x: pathRect.maxX, y: pathRect.minY))
path.addLine(to: CGPoint(x: pathRect.minX + radius, y: pathRect.minY))
path.addQuadCurve(to: CGPoint(x: pathRect.minX, y: pathRect.minY + radius), controlPoint: pathRect.origin)
path.addLine(to: CGPoint(x: pathRect.minX, y: pathRect.maxY - radius))
path.addQuadCurve(to: CGPoint(x: pathRect.minX + radius, y: pathRect.maxY), controlPoint: CGPoint(x: pathRect.minX, y: pathRect.maxY))
path.addLine(to: CGPoint(x: pathRect.maxX, y: pathRect.maxY))
fillColor.setFill()
path.fill()
strokeColor.setStroke()
path.stroke()
}
}
That yields:
Theoretically, it might be more efficient to use CAShapeLayer
and let Apple take care of the draw(_:)
for us. E.g., they may have optimized the rendering to handle partial view updates, etc.
That might look like the following:
@IBDesignable
class OpenRightView: UIView {
@IBInspectable var lineWidth: CGFloat = 4 { didSet { updatePath() } }
@IBInspectable var radius: CGFloat = 7 { didSet { updatePath() } }
@IBInspectable var fillColor: UIColor = .black { didSet { shapeLayer.fillColor = fillColor.cgColor } }
@IBInspectable var strokeColor: UIColor = .red { didSet { shapeLayer.strokeColor = strokeColor.cgColor } }
lazy var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = fillColor.cgColor
shapeLayer.strokeColor = strokeColor.cgColor
shapeLayer.lineWidth = lineWidth
return shapeLayer
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
updatePath()
}
}
private extension OpenRightView {
func configure() {
layer.addSublayer(shapeLayer)
}
func updatePath() {
let pathRect = bounds.inset(by: .init(top: lineWidth / 2, left: lineWidth / 2, bottom: lineWidth / 2, right: 0))
let path = UIBezierPath()
path.move(to: CGPoint(x: pathRect.maxX, y: pathRect.minY))
path.addLine(to: CGPoint(x: pathRect.minX + radius, y: pathRect.minY))
path.addQuadCurve(to: CGPoint(x: pathRect.minX, y: pathRect.minY + radius), controlPoint: pathRect.origin)
path.addLine(to: CGPoint(x: pathRect.minX, y: pathRect.maxY - radius))
path.addQuadCurve(to: CGPoint(x: pathRect.minX + radius, y: pathRect.maxY), controlPoint: CGPoint(x: pathRect.minX, y: pathRect.maxY))
path.addLine(to: CGPoint(x: pathRect.maxX, y: pathRect.maxY))
shapeLayer.path = path.cgPath
shapeLayer.lineWidth = lineWidth
}
}
Upvotes: 2