Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I draw a circle in iOS Swift?

Tags:

ios

swift

let block = UIView(frame: CGRectMake(cellWidth-25, cellHeight/2-8, 16, 16)) block.backgroundColor = UIColor(netHex: 0xff3b30) block.layer.cornerRadius = 9 block.clipsToBounds = true 

This is what I have right now, but it's obviously not the right way to do it.

What's the simplest way to do it?

like image 870
TIMEX Avatar asked Apr 13 '15 23:04

TIMEX


1 Answers

WARNING! This is an incorrect solution. layers are added infinitely in the drawRect method (every time the view is drawn). You should NEVER add layers in the drawRect method. Use layoutSubview instead.

You can draw a circle with this (Swift 3.0+):

let circlePath = UIBezierPath(arcCenter: CGPoint(x: 100, y: 100), radius: CGFloat(20), startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)      let shapeLayer = CAShapeLayer() shapeLayer.path = circlePath.cgPath      // Change the fill color shapeLayer.fillColor = UIColor.clear.cgColor // You can change the stroke color shapeLayer.strokeColor = UIColor.red.cgColor // You can change the line width shapeLayer.lineWidth = 3.0      view.layer.addSublayer(shapeLayer) 

With the code you have posted you are cropping the corners of the UIView, not adding a circle to the view.


Here's a full example of using that method:

/// A special UIView displayed as a ring of color class Ring: UIView {     override func drawRect(rect: CGRect) {         drawRingFittingInsideView()     }          internal func drawRingFittingInsideView() -> () {         let halfSize:CGFloat = min( bounds.size.width/2, bounds.size.height/2)         let desiredLineWidth:CGFloat = 1 // your desired value                      let circlePath = UIBezierPath(                 arcCenter: CGPoint(x:halfSize,y:halfSize),                 radius: CGFloat( halfSize - (desiredLineWidth/2) ),                 startAngle: CGFloat(0),                 endAngle:CGFloat(M_PI * 2),                 clockwise: true)               let shapeLayer = CAShapeLayer()          shapeLayer.path = circlePath.CGPath                       shapeLayer.fillColor = UIColor.clearColor().CGColor          shapeLayer.strokeColor = UIColor.redColor().CGColor          shapeLayer.lineWidth = desiredLineWidth               layer.addSublayer(shapeLayer)      } } 

A circle outlined in red and filled with yellow on a yellow background.


Note, however there's an incredibly handy call:

let circlePath = UIBezierPath(ovalInRect: rect)

which does all the work of making the path. (Don't forget to inset it for the line thickness, which is also incredibly easy with CGRectInset.)

internal func drawRingFittingInsideView(rect: CGRect) {     let desiredLineWidth:CGFloat = 4    // Your desired value     let hw:CGFloat = desiredLineWidth/2          let circlePath = UIBezierPath(ovalInRect: CGRectInset(rect,hw,hw))     let shapeLayer = CAShapeLayer()     shapeLayer.path = circlePath.CGPath     shapeLayer.fillColor = UIColor.clearColor().CGColor     shapeLayer.strokeColor = UIColor.redColor().CGColor     shapeLayer.lineWidth = desiredLineWidth     layer.addSublayer(shapeLayer) } 

An ellipses (oval-like) outlined in red and filled with yellow on a yellow background.


In practice these days in Swift, you would certainly use @IBDesignable and @IBInspectable. Using these you can actually see and change the rendering, in Storyboard!

As you can see, it actually adds new features to the Inspector on the Storyboard, which you can change on the Storyboard:

Xcode Storyboard Attributes Inspector with custom fields.

/// A dot with a border, which you can control completely in Storyboard @IBDesignable class Dot: UIView {      @IBInspectable var mainColor: UIColor = UIColor.blueColor() {         didSet {               print("mainColor was set here")         }     }      @IBInspectable var ringColor: UIColor = UIColor.orangeColor() {          didSet {              print("bColor was set here")          }     }      @IBInspectable var ringThickness: CGFloat = 4 {         didSet {              print("ringThickness was set here")         }     }          @IBInspectable var isSelected: Bool = true          override func drawRect(rect: CGRect) {         let dotPath = UIBezierPath(ovalInRect:rect)         let shapeLayer = CAShapeLayer()         shapeLayer.path = dotPath.CGPath         shapeLayer.fillColor = mainColor.CGColor         layer.addSublayer(shapeLayer)                  if (isSelected) {              drawRingFittingInsideView(rect)         }     }          internal func drawRingFittingInsideView(rect: CGRect) {         let hw:CGFloat = ringThickness/2         let circlePath = UIBezierPath(ovalInRect: CGRectInset(rect,hw,hw) )                  let shapeLayer = CAShapeLayer()         shapeLayer.path = circlePath.CGPath         shapeLayer.fillColor = UIColor.clearColor().CGColor         shapeLayer.strokeColor = ringColor.CGColor         shapeLayer.lineWidth = ringThickness         layer.addSublayer(shapeLayer)     } } 

Finally, note that if you have a UIView (which is square, and which you set to say red in Storyboard) and you simply want to turn it in to a red circle, you can just do the following:

// Makes a UIView into a circular dot of color class Dot: UIView {     override func layoutSubviews() {         layer.cornerRadius = bounds.size.width/2     } } 
like image 186
Dario Avatar answered Oct 15 '22 19:10

Dario