I am making a timeline as you can see below. This is just my idea in its infancy. More stuff should be on the timeline like: Time spans (events starting and stopping), Comments (text, usernames maybe images and icons), Arches from one event to another and so on and so forth.
I want to be able to zoom in and out but obviously not like when you zoom in and out of a picture. Arches would change shape depending on zoom level and icons would fade in and out depending on zoom level as well as the vertical lines indicating date and time.
But now with this pretty simple setup it is already taking 10% to 15% cpu time. I fear that adding a whole lot more might really upset the cpu.
Is there a optimization that I should do or another approach altogether that I might use. Or is 10-15% cpu time fine? Maybe it is fine... nope! I changed it to show a wider time span and the cpu shot up to 35%!
So here is the timeline view code:
// Agust Rafnsson
import UIKit
class RulerView: UIView {
// 1minute 12seconds
let LENGTH_IN_SECONDS:TimeInterval = 100000
var position: TimeInterval? {
didSet {
self.setNeedsDisplay()
}
}
let span:TimeInterval = 60
let dateFormatter = DateComponentsFormatter()
var tinyLineHeight = CGFloat(1)
var smallLineHeight = CGFloat(4)
var bigLineHeight = CGFloat(10)
public var contentOffset = CGFloat(0) {
didSet {
self.setNeedsDisplay()
}
}
override open func layoutSubviews() {
super.layoutSubviews()
self.backgroundColor = UIColor.clear
}
override func draw(_ rect: CGRect) {
guard let position = position else {
return
}
let width = self.bounds.size.width
let height = self.bounds.size.height
tinyLineHeight = height/4
smallLineHeight = height/3
bigLineHeight = height/1.5
let dotsPerSecond:CGFloat = width/span
let firstSecond = position - span/2
let lastSecond = position + span/2
if let context = UIGraphicsGetCurrentContext() {
let centerXPath = UIBezierPath()
UIColor.black.set()
centerXPath.move(to: CGPoint(x: width/2 - height/5, y: 0))
centerXPath.addLine(to: CGPoint(x: width/2 + height/5, y: height))
centerXPath.move(to: CGPoint(x: width/2 + height/5, y: 0))
centerXPath.addLine(to: CGPoint(x: width/2 - height/5, y: height))
centerXPath.lineWidth = 0.5
centerXPath.stroke()
for i in Int(firstSecond)...Int(lastSecond) {
let xPosition = (Float64(i)-firstSecond)*dotsPerSecond
let path = UIBezierPath()
path.move(to: CGPoint(x: xPosition , y:0))
if i.isMultiple(of: 15) {
UIColor.red.set()
path.addLine(to: CGPoint(x: xPosition, y: bigLineHeight))
} else if i.isMultiple(of: 5){
UIColor.black.set()
path.addLine(to: CGPoint(x: xPosition, y: smallLineHeight))
} else if dotsPerSecond > 5 {
UIColor.gray.set()
path.addLine(to: CGPoint(x: xPosition, y: tinyLineHeight))
}
path.lineWidth = 0.5
path.stroke()
if i.isMultiple(of: 15){
let attrs = [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue", size: smallLineHeight)!,
NSAttributedString.Key.foregroundColor: UIColor.black,
NSAttributedString.Key.backgroundColor: UIColor.white.withAlphaComponent(0.75)]
if let string = dateFormatter.string(from: TimeInterval(i)) {
let stringSize = string.size(withAttributes: attrs)
string.draw(at: CGPoint(x: xPosition - stringSize.width/2, y: height/2), withAttributes: attrs)
}
}
}
}
}
}
And here is the viewcontroller for demonstration purposes:
import UIKit
class ViewController: UIViewController {
let ruler: RulerView = RulerView()
lazy var timer = {Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] timer in
guard let self = self else { return }
self.ruler.position! = self.ruler.position! + 0.05
print(self.ruler.position!)
}}()
override func viewDidLoad() {
print(timer.self)
ruler.position = TimeInterval.random(in: 0...ruler.LENGTH_IN_SECONDS)
super.viewDidLoad()
ruler.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(ruler)
ruler.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
ruler.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
ruler.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
ruler.heightAnchor.constraint(equalToConstant: 40).isActive = true
// Do any additional setup after loading the view.
}
}
