Search code examples
iosswiftxcodecore-graphicsuibezierpath

Swift Add square dashes to Circle


I have a custom view class which draws a circle as it is seen below:

import UIKit

@IBDesignable
class RotaryCircleSlider:UIView
{
    @IBInspectable var mainColor: UIColor = UIColor.blue
        {
        didSet { print("mainColor was set here") }
    }
    @IBInspectable var ringColor: UIColor = UIColor.orange
        {
        didSet { print("bColor was set here") }
    }
    @IBInspectable var ringThickness: CGFloat = 4
        {
        didSet { print("ringThickness was set here") }
    }

    @IBInspectable var isSelected: Bool = true

    override func draw(_ rect: CGRect)
    {
        let dotPath = UIBezierPath(ovalIn:rect)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = dotPath.cgPath
        shapeLayer.fillColor = mainColor.cgColor
        layer.addSublayer(shapeLayer)

        if (isSelected) { drawRingFittingInsideView(rect: rect) }
    }

    internal func drawRingFittingInsideView(rect: CGRect)->()
    {
        let hw:CGFloat = ringThickness/2
        let circlePath = UIBezierPath(ovalIn: rect.insetBy(dx: hw,dy: hw) )
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = ringColor.cgColor
        shapeLayer.lineWidth = ringThickness
        layer.addSublayer(shapeLayer)
    }
}

However I want the circular view to be divided like a clock.

Although, I tried linecap and lineDashPattern I could not get it.


Solution

  • Here is the code that will create your desired view... you can alter them according to your design

    import Foundation
    import UIKit
    
    @IBDesignable
    class RotaryCircleSlider:UIView
    {
        @IBInspectable var mainColor: UIColor = #colorLiteral(red: 0.1999788582, green: 0.2000134587, blue: 0.1999712586, alpha: 1)
            {
            didSet { print("mainColor was set here") }
        }
        @IBInspectable var ringColor: UIColor = #colorLiteral(red: 0.7466413379, green: 0.027390128, blue: 0.2014107406, alpha: 1)
            {
            didSet { print("bColor was set here") }
        }
        @IBInspectable var ringThickness: CGFloat = 14
            {
            didSet { print("ringThickness was set here") }
        }
    
        @IBInspectable var isSelected: Bool = true
    
        override func draw(_ rect: CGRect)
        {
            let dotPath = UIBezierPath(ovalIn:rect)
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = dotPath.cgPath
            shapeLayer.fillColor = mainColor.cgColor
            layer.addSublayer(shapeLayer)
            let ringView = RingProgressHud(frame: rect)
    
             drawRingFittingInsideView(rect: rect)
              addSubview(ringView)
    
            let stack = UIStackView(frame: CGRect(x: 0, y: 0, width: rect.width * 0.4, height: rect.width * 0.4))
            stack.alignment = .center
            stack.axis = .vertical
            stack.center = CGPoint(x: bounds.midX, y: bounds.midY)
            stack.distribution = .fillProportionally
            addSubview(stack)
    
    
            let label = UILabel()
            label.text = "172"
            label.font = .systemFont(ofSize: 30, weight: .heavy)
            label.sizeToFit()
            label.textColor = .white
    
            let move = UILabel()
                   move.text = "Total Move"
                   move.font = .systemFont(ofSize: 15, weight: .regular)
                   move.sizeToFit()
                   move.textColor = #colorLiteral(red: 0.5293634534, green: 0.5294427276, blue: 0.529346168, alpha: 1)
    
            let ccw = UILabel()
                          ccw.text = "CCW"
                          ccw.font = .systemFont(ofSize: 18, weight: .semibold)
                          ccw.sizeToFit()
            ccw.textColor = .white
    
            let direction = UILabel()
                          direction.text = "Active Direction"
                          direction.font = .systemFont(ofSize: 14, weight: .regular)
                          direction.sizeToFit()
                          direction.textColor = #colorLiteral(red: 0.5293634534, green: 0.5294427276, blue: 0.529346168, alpha: 1)
    
    
            stack.addArrangedSubview(label)
            stack.addArrangedSubview(move)
             stack.addArrangedSubview(ccw)
            stack.addArrangedSubview(direction)
    
        }
    
        internal func drawRingFittingInsideView(rect: CGRect)->()
        {
            let hw:CGFloat = ringThickness/2
            let circlePath = UIBezierPath(ovalIn: rect.insetBy(dx: hw,dy: hw) )
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = circlePath.cgPath
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = ringColor.cgColor
            shapeLayer.lineWidth = ringThickness
            layer.addSublayer(shapeLayer)
        }
    }
    

    and other class creating clock shape bars is here

    import UIKit
    @IBDesignable
    class RingProgressHud : UIView {
        let numberOfTeeth = UInt(60) // Number of teetch to render
        let teethSize = CGSize(width:2, height:15) // The size of each individual tooth
        let shapeLayer = CAShapeLayer() // The teeth shape layer
    
        private func getPathMask(size:CGSize, teethCount:UInt, teethSize:CGSize, radius:CGFloat) -> CGPath? {
    
            let halfHeight = size.height*0.5
                let halfWidth = size.width*0.5
            let deltaAngle = CGFloat(2*Double.pi)/CGFloat(teethCount); // The change in angle between paths
    
            // Create the template path of a single shape.
            let p = CGPath(rect: CGRect(x: -teethSize.width*0.5, y: radius, width: teethSize.width, height: teethSize.height), transform: nil)
            let returnPath = CGMutablePath()
    
            for i in 0..<teethCount { // Copy, translate and rotate shapes around
                let translate = CGAffineTransform(translationX: halfWidth, y: halfHeight)
                let rotate = translate.rotated(by: deltaAngle*CGFloat(i))
                returnPath.addPath(p, transform: rotate)
    
            }
    
            return returnPath.copy()
        }
    
        private func commonSetup() {
    
            shapeLayer.path = getPathMask(size: frame.size, teethCount: numberOfTeeth, teethSize: teethSize, radius: ((frame.width*0.4)-teethSize.height))
            shapeLayer.fillColor = #colorLiteral(red: 0.3411435485, green: 0.3411974311, blue: 0.341131866, alpha: 1)
            layer.addSublayer(shapeLayer)
        }
    
        override init(frame: CGRect) {
    
            super.init(frame: frame)
            commonSetup()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonSetup()
        }
    }
    

    enter image description here

    i also added them on git ... you can download them from there as well

    Gist