Arel Arel - 1 year ago 68
iOS Question

iOS - Draw a donut in an view controller

This is really simple, but I can't figure out what I'm doing wrong.

I have a view controller with one view in the center. I want to draw something like the circle below:

enter image description here

The main problem I'm having, is I can't get anytihng to show up on the view. I'm just trying to draw a line right now, but am obviously missing something key. Here is my code:

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *centerView;


@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[[UIColor brownColor] set];
CGContextRef currentContext =UIGraphicsGetCurrentContext();
CGContextMoveToPoint(currentContext,50.0f, 10.0f);
CGContextAddLineToPoint(currentContext,100.0f, 200.0f);


Answer Source

You can do it with CAShapeLayer. Here is a playground that illustrates how to do it. As an added bonus, I threw in the animation.

    import UIKit
    import PlaygroundSupport

    class AnimatedRingView: UIView {
        private static let animationDuration = CFTimeInterval(2)
        private let π = CGFloat.pi
        private let startAngle = 1.5 * CGFloat.pi
        private let strokeWidth = CGFloat(8)
        var proportion = CGFloat(0.5) {
            didSet {

        private lazy var circleLayer: CAShapeLayer = {
            let circleLayer = CAShapeLayer()
            circleLayer.strokeColor = UIColor.gray.cgColor
            circleLayer.fillColor = UIColor.clear.cgColor
            circleLayer.lineWidth = self.strokeWidth
            return circleLayer

        private lazy var ringlayer: CAShapeLayer = {
            let ringlayer = CAShapeLayer()
            ringlayer.fillColor = UIColor.clear.cgColor
            ringlayer.strokeColor = self.tintColor.cgColor
            ringlayer.lineCap = kCALineCapRound
            ringlayer.lineWidth = self.strokeWidth
            return ringlayer

        override func layoutSubviews() {
            let radius = (min(frame.size.width, frame.size.height) - strokeWidth - 2)/2
            let circlePath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: startAngle + 2 * π, clockwise: true)
            circleLayer.path = circlePath.cgPath
            ringlayer.path = circlePath.cgPath
            ringlayer.strokeEnd = proportion


        func animateRing(From startProportion: CGFloat, To endProportion: CGFloat, Duration duration: CFTimeInterval = animationDuration) {
            let animation = CABasicAnimation(keyPath: "strokeEnd")
            animation.duration = duration
            animation.fromValue = startProportion
            animation.toValue = endProportion
            animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            ringlayer.strokeEnd = endProportion
            ringlayer.strokeStart = startProportion
            ringlayer.add(animation, forKey: "animateRing")


    let v = AnimatedRingView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    PlaygroundPage.current.liveView = v
    v.animateRing(From: 0, To: 0.5)
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download