how to update view of bezierpath graphic with timer

I have timer, simple label with seconds. That label is automatically changing with Timer, it is simple and ok.

I have arc/circle implemented with radius strictly connected with seconds. My goal is to actualize this graphic, doing something like an analog clock.

Seoconds label are dynamically changing, but I cannot make arc changing. Is it normally possible? How to make that circle to increment arc (to actualize graphic in viewcontroller?)

Thank You!


// ViewController.swift

import UIKit

class ViewController: UIViewController {

let circle = Circle()

let button = Button()

let timeKeeper = TimeKeeper()

override func viewDidLoad() {

super.viewDidLoad()

timeKeeper.runTime()


//label clock setup

TimeLabel.sharedTime.font = UIFont.systemFont(ofSize: 13)

view.addSubview(TimeLabel.sharedTime)

TimeLabel.sharedTime.translatesAutoresizingMaskIntoConstraints = false

//circle clock setup

view.addSubview(circle)

circle.translatesAutoresizingMaskIntoConstraints = false

}

}


// TimeKeeper.swift

import UIKit

struct TimeLabel {

static var sharedTime = UILabel()

static var sharedCountTime = Timer()

static var sharedTotalSeconds = 0

}

class TimeKeeper {

func runTime() {

TimeLabel.sharedCountTime = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(increaseTime), userInfo: nil, repeats: true)

}

@objc func increaseTime() {

TimeLabel.sharedTotalSeconds += 1

TimeLabel.sharedTime.text = String(TimeLabel.sharedTotalSeconds)

}

}


// Circle.swift

import UIKit

class Circle: UIView {

override init(frame: CGRect) {

super.init(frame: frame)

self.backgroundColor = UIColor.clear

}

required init?(coder aDecoder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

func setCircle() {

let center = CGPoint(x: frame.size.width/2, y: frame.size.height/2)

let circle = UIBezierPath(arcCenter: center, radius: frame.size.width * 0.4, startAngle: 0, endAngle: .pi / 60 * CGFloat(TimeLabel.sharedTotalSeconds), clockwise: true)

circle.stroke()

}

override func draw(_ rect: CGRect) {

setCircle()

}

}

Answered by Claude31 in 342642022

Please format code for readability.


//  ViewController.swift
import UIKit
class ViewController: UIViewController {
    let circle = Circle()
    let button = Button()
    let timeKeeper = TimeKeeper()
    override func viewDidLoad() {
        super.viewDidLoad()
        timeKeeper.runTime()

//label clock setup
        TimeLabel.sharedTime.font = UIFont.systemFont(ofSize: 13)
        view.addSubview(TimeLabel.sharedTime)
        TimeLabel.sharedTime.translatesAutoresizingMaskIntoConstraints = false
    
//circle clock setup
        view.addSubview(circle)
        circle.translatesAutoresizingMaskIntoConstraints = false
        }
}

//  TimeKeeper.swift
import UIKit
struct TimeLabel {
    static var sharedTime = UILabel()
    static var sharedCountTime = Timer()
    static var sharedTotalSeconds = 0
}
class TimeKeeper {
    func runTime() {
        TimeLabel.sharedCountTime = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(increaseTime), userInfo: nil, repeats: true)
    }
     @objc func increaseTime() {
        TimeLabel.sharedTotalSeconds += 1
        TimeLabel.sharedTime.text = String(TimeLabel.sharedTotalSeconds)
    }
}

//  Circle.swift
import UIKit
class Circle: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.clear
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func setCircle() {
        let center = CGPoint(x: frame.size.width/2, y: frame.size.height/2)
        let circle = UIBezierPath(arcCenter: center, radius: frame.size.width * 0.4, startAngle: 0, endAngle: .pi / 60 * CGFloat(TimeLabel.sharedTotalSeconds), clockwise: true)
        circle.stroke()
    }
    override func draw(_ rect: CGRect) {
        setCircle()
    }
}

May be I missed something, but I do not see the equivalent of runTime() for circle, to call for drawing.


Why not calling a redraw for circle at line 33. Of course setCircle should use sharedTotalSeconds

Accepted Answer

Please format code for readability.


//  ViewController.swift
import UIKit
class ViewController: UIViewController {
    let circle = Circle()
    let button = Button()
    let timeKeeper = TimeKeeper()
    override func viewDidLoad() {
        super.viewDidLoad()
        timeKeeper.runTime()

//label clock setup
        TimeLabel.sharedTime.font = UIFont.systemFont(ofSize: 13)
        view.addSubview(TimeLabel.sharedTime)
        TimeLabel.sharedTime.translatesAutoresizingMaskIntoConstraints = false
    
//circle clock setup
        view.addSubview(circle)
        circle.translatesAutoresizingMaskIntoConstraints = false
        }
}

//  TimeKeeper.swift
import UIKit
struct TimeLabel {
    static var sharedTime = UILabel()
    static var sharedCountTime = Timer()
    static var sharedTotalSeconds = 0
}
class TimeKeeper {
    func runTime() {
        TimeLabel.sharedCountTime = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(increaseTime), userInfo: nil, repeats: true)
    }
     @objc func increaseTime() {
        TimeLabel.sharedTotalSeconds += 1
        TimeLabel.sharedTime.text = String(TimeLabel.sharedTotalSeconds)
    }
}

//  Circle.swift
import UIKit
class Circle: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.clear
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func setCircle() {
        let center = CGPoint(x: frame.size.width/2, y: frame.size.height/2)
        let circle = UIBezierPath(arcCenter: center, radius: frame.size.width * 0.4, startAngle: 0, endAngle: .pi / 60 * CGFloat(TimeLabel.sharedTotalSeconds), clockwise: true)
        circle.stroke()
    }
    override func draw(_ rect: CGRect) {
        setCircle()
    }
}

May be I missed something, but I do not see the equivalent of runTime() for circle, to call for drawing.


Why not calling a redraw for circle at line 33. Of course setCircle should use sharedTotalSeconds

I did almost everything ok, but almost.

Now I set "TimeLabel.circle.setNeedsDisplay()", as I was trying earlier, but I did not set circle into TimeLabel struct. Thank You for help.

how to update view of bezierpath graphic with timer
 
 
Q