UIBezierPath Star with rounded corners
Here is a custom UIView
that computes a path for a 5-point star with a cornerRadius
:
class RoundedStar : UIView {
var cornerRadius: CGFloat = 10 { didSet { setNeedsDisplay() } }
var rotation: CGFloat = 54 { didSet { setNeedsDisplay() } }
var fillColor = UIColor.red { didSet { setNeedsDisplay() } }
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
let r = rect.width / 2
let rc = cornerRadius
let rn = r * 0.95 - rc
var cangle = rotation
for i in 1 ... 5 {
// compute center point of tip arc
let cc = CGPoint(x: center.x + rn * cos(cangle * .pi / 180), y: center.y + rn * sin(cangle * .pi / 180))
// compute tangent point along tip arc
let p = CGPoint(x: cc.x + rc * cos((cangle - 72) * .pi / 180), y: cc.y + rc * sin((cangle - 72) * .pi / 180))
if i == 1 {
path.move(to: p)
} else {
path.addLine(to: p)
}
// add 144 degree arc to draw the corner
path.addArc(withCenter: cc, radius: rc, startAngle: (cangle - 72) * .pi / 180, endAngle: (cangle + 72) * .pi / 180, clockwise: true)
cangle += 144
}
path.close()
fillColor.setFill()
path.fill()
}
}
Example run in the Playground:
Simulator Demo
Code:
class ViewController: UIViewController {
@IBOutlet weak var rstar: RoundedStar!
@IBOutlet weak var cornerRadiusLabel: UILabel!
@IBOutlet weak var rotationlabel: UILabel!
@IBAction func cornerRadiusChanged(_ sender: UISlider) {
rstar.cornerRadius = CGFloat(sender.value)
cornerRadiusLabel.text = "Corner Radius: \(Int(sender.value))"
}
@IBAction func rotationChanged(_ sender: UISlider) {
rstar.rotation = CGFloat(sender.value)
rotationlabel.text = "Rotation: \(Int(sender.value))"
}
}
Drawing Rounded Corners Using UIBezierPath
Don't use a shape layer. Use a layer (or a view). Draw the UIBezierPath's path into it and stroke it, and then erase the bottom line by drawing it and stroking it with a .clear
blend mode.
Result:
Code (modify as desired; I use here a clear UIView that draws the shape as its draw
code):
let p = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 4.0, height: 4.0))
UIColor.white.setStroke()
p.stroke()
let p2 = UIBezierPath()
p2.move(to: CGPoint(x:0, y:self.bounds.height))
p2.addLine(to: CGPoint(x:self.bounds.width, y:self.bounds.height))
p2.lineWidth = 2
p2.stroke(with: .clear, alpha: 1)
EDIT Another way would have been to clip out the bottom line area before drawing the rounded rect:
let p1 = UIBezierPath(rect: CGRect(origin:.zero,
size:CGSize(width:self.bounds.width, height:self.bounds.height-2)))
p1.addClip()
let p = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 4.0, height: 4.0))
UIColor.white.setStroke()
p.stroke()
Round top corners of a UIView and add border
The mask layer doesn't get drawn, just used to compute the mask. Try:
-(void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius
{
CGRect bounds = self.bounds;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
CAShapeLayer* frameLayer = [CAShapeLayer layer];
frameLayer.frame = bounds;
frameLayer.path = maskPath.CGPath;
frameLayer.strokeColor = [UIColor redColor].CGColor;
frameLayer.fillColor = nil;
[self.layer addSublayer:frameLayer];
}
-(void)roundTopCornersRadius:(CGFloat)radius
{
[self roundCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) radius:radius];
}
-(void)roundBottomCornersRadius:(CGFloat)radius
{
[self roundCorners:(UIRectCornerBottomLeft|UIRectCornerBottomRight) radius:radius];
}
The frame you're currently seeing drawn is the UITextField's normal frame, so set the frame style to none. You'll also have to adjust the insets to make up for the fact that with the frame style set to none there's normally no inset.
UIBezierPath - Add rounded corner
Include the following.
circle.lineCap = kCALineJoinRound;
For Swift 3.0 and above
circle.lineCapStyle = .Round;
UIView with round corner and white border
Using the same layer object:
view.layer.borderWidth = 1;
view.layer.borderColor = [[UIColor whiteColor] CGColor];
Rounding and adding a border to a UIImage - Not UIImageView
Use this Extension
extension UIImage {
var rounded: UIImage? {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
defer { UIGraphicsEndImageContext() }
let bezierPath = UIBezierPath(roundedRect: rect, cornerRadius: 40)
bezierPath.addClip()
bezierPath.lineWidth = 10
draw(in: rect)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
context.setStrokeColor(UIColor.red.cgColor)
bezierPath.lineWidth = 10
bezierPath.stroke()
return UIGraphicsGetImageFromCurrentImageContext() }
}
How to add rounded corner to a UIBezierPath custom rectangle?
Instead of starting the code with a straight line :
path.moveToPoint(CGPoint(x: 300, y: 0))
I instead start with an arc (upper right):
path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2) , clockwise: true) //1st rounded corner
and by doing this, I have four rounded corners and I just need to add a straight line at the end of the code right before:
path.closePath()
Here is the code and a screenshot:
let path = UIBezierPath()
path.addArcWithCenter(CGPoint(x: 300-10, y: 50), radius: 10 , startAngle: 0 , endAngle: CGFloat(M_PI/2) , clockwise: true) //1st rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 50), radius:10, startAngle: CGFloat(2 * M_PI / 3), endAngle:CGFloat(M_PI) , clockwise: true)// 2rd rounded corner
path.addArcWithCenter(CGPoint(x: 200, y: 10), radius:10, startAngle: CGFloat(M_PI), endAngle:CGFloat(3 * M_PI / 2), clockwise: true)// 3rd rounded corner
// little triangle
path.addLineToPoint(CGPoint(x:240 , y:0))
path.addLineToPoint(CGPoint(x: 245, y: -10))
path.addLineToPoint(CGPoint(x:250, y: 0))
path.addArcWithCenter(CGPoint(x: 290, y: 10), radius: 10, startAngle: CGFloat(3 * M_PI / 2), endAngle: CGFloat(2 * M_PI ), clockwise: true)
path.addLineToPoint(CGPoint(x:300 , y:50))
path.closePath()
Related Topics
Nsdate() or Date() Shows the Wrong Time
How to Check If a String Contains Another String in Swift
Structure VS Class in Swift Language
Swift Generic Coercion Misunderstanding
Getting a "This Application Is Modifying the Autolayout Engine from a Background Thread" Error
How to Use Swift 4 Codable in Core Data
How to Run a Terminal Command in a Swift Script? (E.G. Xcodebuild)
Nsobject Subclass in Swift: Hash VS Hashvalue, Isequal VS ==
Alamofire Get API Request Not Working as Expected
How to Resolve "Ambiguous Use Of" Compile Error With Swift #Selector Syntax
Swift - How to Convert String to Double
How to Create Generic Protocols in Swift
Extend Array Types Using Where Clause in Swift
Unwrapping Multiple Optionals in If Statement
Convert a Two Byte Uint8 Array to a Uint16 in Swift
How to Detect a Tap Gesture Location in Swiftui
Calculate Age from Birth Date Using Nsdatecomponents in Swift