Reputation: 13033
I want to create a UIView that has rounded borders which I have specified. For example, I want to create a UIView that has 3 borders: left, top and right, and the topright and topleft borders should be rounded.
This let's me create resizable border views (without rounded corners):
open class ResizableViewBorder: UIView {
open override func layoutSubviews() {
super.layoutSubviews()
setNeedsDisplay()
}
open override func draw(_ rect: CGRect) {
let edges = ... get some UIRectEdges here
let lineWidth = 1
if edges.contains(.top) || edges.contains(.all) {
addBezierPath(paths: [
CGPoint(x: 0, y: 0 + lineWidth / 2),
CGPoint(x: self.bounds.width, y: 0 + lineWidth / 2)
])
}
if edges.contains(.bottom) || edges.contains(.all) {
addBezierPath(paths: [
CGPoint(x: 0, y: self.bounds.height - lineWidth / 2),
CGPoint(x: self.bounds.width, y: self.bounds.height - lineWidth / 2)
])
}
if (edges.contains(.left) || edges.contains(.all) || edges.contains(.right)) && CurrentDevice.isRightToLeftLanguage{
addBezierPath(paths: [
CGPoint(x: 0 + lineWidth / 2, y: 0),
CGPoint(x: 0 + lineWidth / 2, y: self.bounds.height)
])
}
if (edges.contains(.right) || edges.contains(.all) || edges.contains(.left)) && CurrentDevice.isRightToLeftLanguage{
addBezierPath(paths: [
CGPoint(x: self.bounds.width - lineWidth / 2, y: 0),
CGPoint(x: self.bounds.width - lineWidth / 2, y: self.bounds.height)
])
}
}
private func addBezierPath(paths: [CGPoint]) {
let lineWidth = 1
let borderColor = UIColor.black
let path = UIBezierPath()
path.lineWidth = lineWidth
borderColor.setStroke()
UIColor.blue.setFill()
var didAddedFirstLine = false
for singlePath in paths {
if !didAddedFirstLine {
didAddedFirstLine = true
path.move(to: singlePath)
} else {
path.addLine(to: singlePath)
}
}
path.stroke()
}
}
However, I can not find a nice robust way to add a corner radius to a specified corner. I have a hacky way to do it with a curve:
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: fakeCornerRadius, y: 0))
path.addLine(to: CGPoint(x: frame.width - fakeCornerRadius, y: 0))
path.addQuadCurve(to: CGPoint(x: frame.width, y: fakeCornerRadius), controlPoint: CGPoint(x: frame.width, y: 0))
path.addLine(to: CGPoint(x: frame.width, y: frame.height - fakeCornerRadius))
path.stroke()
Which gives me this:
Why is that line of the quad curve so fat? I prefer using UIBezierPaths over CALayers because CALayers have a huge performance impact...
Upvotes: 0
Views: 116
Reputation: 534950
What's wrong with your curve-drawing code is not that the curve is fat but that the straight lines are thin. They are thin because they are smack dab on the edge of the view. So your line width is 2 points, but one of those points is outside the view. And points are not pixels, so what pixels are there left to fill in? Only the ones inside the view. So the straight lines have an apparent visible line width of 1, and only the curve has a visible line width 2.
Another problem is that you probably are looking at this app running in the simulator on your computer. But there is a mismatch between the pixels of the simulator and the pixels of your computer monitor. That causes numerous drawing artifacts. The way to examine the drawing accurately down to the pixel level is to use the simulator application's screen shot image facility and look at the resulting image file, full-size, in Preview or similar. Or run on a device and take the screen shot image there.
To demonstrate this, I modified your code to operate in an inset version of the original rect
(which, by the way, should be your view's bounds, not its frame):
let fakeCornerRadius : CGFloat = 20
let rect2 = self.bounds.insetBy(dx: 2, dy: 2)
let path = UIBezierPath()
path.lineWidth = 2
path.move(
to: CGPoint(x: rect2.minX + fakeCornerRadius, y: rect2.minY))
path.addLine(
to: CGPoint(x: rect2.maxX - fakeCornerRadius, y: rect2.minY))
path.addQuadCurve(
to: CGPoint(x: rect2.maxX, y: rect2.minY + fakeCornerRadius),
controlPoint: CGPoint(x: rect2.maxX, y: rect2.minY))
path.addLine(
to: CGPoint(x: rect2.maxX, y: rect2.maxY - fakeCornerRadius))
path.stroke()
Taking a screen shot from within the Simulator application, I got this:
As you can see, this lacks the artifacts of your screen shot.
Upvotes: 2