Im trying to create a custom shaped ImageView that looks like this.
My initial though was to round corners using the following extensions like so:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
extension UIBezierPath {
static func rectWithRoundCorners(
size: CGSize,
topLeft: CGFloat = 0,
bottomLeft: CGFloat = 0,
bottomRight: CGFloat = 0,
topRight: CGFloat = 0)
-> UIBezierPath
{
let path = UIBezierPath()
path.move(to: CGPoint(x: size.width - topRight, y: 0))
path.addLine(to: CGPoint(x: topLeft, y: 0))
path.addQuadCurve(to: CGPoint(x: 0, y: topLeft), controlPoint: .zero)
path.addLine(to: CGPoint(x: 0, y: size.height - bottomLeft))
path.addQuadCurve(to: CGPoint(x: bottomLeft, y: size.height), controlPoint: CGPoint(x: 0, y: size.height))
path.addLine(to: CGPoint(x: size.width - bottomRight, y: size.height))
path.addQuadCurve(
to: CGPoint(x: size.width, y: size.height - bottomRight),
controlPoint: CGPoint(x: size.width, y: size.height))
path.addLine(to: CGPoint(x: size.width, y: topRight))
path.addQuadCurve(to: CGPoint(x: size.width - topRight, y: 0), controlPoint: CGPoint(x: size.width, y: 0))
return path
}
}
extension UIView {
func roundCorners(topLeft: CGFloat = 0, bottomLeft: CGFloat = 0, bottomRight: CGFloat = 0, topRight: CGFloat = 0) {
let path = UIBezierPath.rectWithRoundCorners(
size: bounds.size,
topLeft: topLeft,
bottomLeft: bottomLeft,
bottomRight: bottomRight,
topRight: topRight)
let shape = CAShapeLayer()
shape.path = path.cgPath
layer.mask = shape
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 114, height: 114)
label.backgroundColor = .red
label.text = "Hello World!"
label.textColor = .black
label.roundCorners(topLeft: 100, bottomLeft: 50, bottomRight: 0, topRight: 50)
view.addSubview(label)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
but I get the following result:
Is there a better approach using rounded rect and UIBezierPath? I have no experience using UIBezierPath so not sure how it will do round corners and straight edges...
CodePudding user response:
Well, your shape only has two straight edges, so there should be, at most, two addLine
calls.
There might be more mathematically rigorous solutions, but I use tools like Adobe Photoshop or Illustrator to approximate what Bézier curves I need.
The overall shape (bounded by 117×103 rectangle) can be reasonably well approximated with one big cubic Bézier (shown below), one addLine
for the bottom edge, one quad Bézier (or arc) for the rounded, lower-right corner, and then just close
the path.
To get those control points, I used a pen tool in Adobe Illustrator to create a cubic bezier of the appropriate shape, and I then exported the path as a SVG, and deciphered the file to came up with the appropriate coordinates for the various control points:
let rect = CGRect(origin: .zero, size: CGSize(width: 117, height: 103))
let radius: CGFloat = 10
let path = UIBezierPath()
path.move(to: CGPoint(x: rect.maxX, y: 27.62))
path.addCurve(to: CGPoint(x: 11.77, y: rect.maxY),
controlPoint1: CGPoint(x: 77, y: -46.21),
controlPoint2: CGPoint(x: -36.11, y: 44.91))
path.addLine(to: CGPoint(x: rect.maxX - radius, y: rect.maxY))
path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.maxY - radius),
controlPoint: CGPoint(x: rect.maxX, y: rect.maxY))
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.path = path.cgPath
someView.layer.addSublayer(shapeLayer)
Tweak the coordinates as you see fit, but hopefully this illustrates (no pun intended) how to approximate the Bézier curves in a drawing and translate that to Swift code.
This is the resulting image: