139 lines
4.2 KiB
Swift
139 lines
4.2 KiB
Swift
//
|
|
// TimableProgressView.swift
|
|
// TGUIKit
|
|
//
|
|
// Created by keepcoder on 13/07/2017.
|
|
// Copyright © 2017 Telegram. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import SwiftSignalKit
|
|
|
|
public final class TimableProgressTheme {
|
|
let backgroundColor: NSColor
|
|
let foregroundColor: NSColor
|
|
let animated: Bool
|
|
let outer: CGFloat
|
|
let seconds: TimeInterval
|
|
let border: Bool
|
|
let borderWidth: CGFloat
|
|
let start:Double
|
|
public init(backgroundColor: NSColor = presentation.colors.blackTransparent, foregroundColor: NSColor = .white, animated: Bool = true, outer: CGFloat = 5, seconds: TimeInterval = 10, start: Double = 100, border: Bool = true, borderWidth: CGFloat = 3) {
|
|
self.backgroundColor = backgroundColor
|
|
self.foregroundColor = foregroundColor
|
|
self.animated = animated
|
|
self.outer = outer
|
|
self.seconds = seconds
|
|
self.border = border
|
|
self.borderWidth = borderWidth
|
|
self.start = start
|
|
}
|
|
}
|
|
|
|
public class TimableProgressView: View {
|
|
|
|
public var theme: TimableProgressTheme = TimableProgressTheme() {
|
|
didSet {
|
|
self._progress = theme.start
|
|
progress = Int32(round(theme.start))
|
|
stopAnimation()
|
|
needsDisplay = true
|
|
}
|
|
}
|
|
|
|
public var progress: Int32 = 100 {
|
|
didSet {
|
|
assert(progress <= 100 && progress >= 0)
|
|
}
|
|
}
|
|
private var _progress: Double = 100
|
|
|
|
private var timer: SwiftSignalKit.Timer?
|
|
|
|
public func startAnimation() {
|
|
timer?.invalidate()
|
|
let duration = self.theme.seconds
|
|
let fps: TimeInterval = 60
|
|
let difference = Double(progress) - _progress
|
|
let tick: Double = Double(difference / (fps * duration))
|
|
timer = SwiftSignalKit.Timer(timeout: 1 / fps, repeat: true, completion: { [weak self] in
|
|
if let strongSelf = self {
|
|
strongSelf._progress += tick
|
|
strongSelf.needsDisplay = true
|
|
if Int32(floor(strongSelf._progress)) == strongSelf.progress || strongSelf._progress < 0 || strongSelf._progress > 100 {
|
|
strongSelf.stopAnimation()
|
|
}
|
|
}
|
|
}, queue: Queue.mainQueue())
|
|
|
|
timer?.start()
|
|
}
|
|
public func stopAnimation() {
|
|
timer?.invalidate()
|
|
timer = nil
|
|
_progress = Double(progress)
|
|
needsDisplay = true
|
|
}
|
|
|
|
deinit {
|
|
timer?.invalidate()
|
|
}
|
|
|
|
|
|
private func radians(_ degrees: CGFloat) -> CGFloat {
|
|
return ((degrees) / 180 * .pi)
|
|
}
|
|
|
|
public override func draw(_ layer: CALayer, in ctx: CGContext) {
|
|
|
|
ctx.round(frame.size, floorToScreenPixels(backingScaleFactor, frame.width/2))
|
|
|
|
ctx.setFillColor(theme.backgroundColor.cgColor)
|
|
ctx.fill(bounds)
|
|
|
|
let center = NSMakePoint(frame.width/2, frame.height/2)
|
|
|
|
let startAngle: CGFloat = 90
|
|
|
|
ctx.translateBy(x: frame.width / 2.0, y: frame.height / 2.0)
|
|
ctx.scaleBy(x: 1.0, y: -1.0)
|
|
ctx.translateBy(x: -frame.width / 2.0, y: -frame.height / 2.0)
|
|
|
|
let radius: CGFloat = frame.width/2 - theme.outer
|
|
let angle = CGFloat(_progress / 100 * 360)
|
|
|
|
if theme.border {
|
|
ctx.setStrokeColor(theme.foregroundColor.cgColor)
|
|
ctx.setLineWidth(theme.borderWidth)
|
|
ctx.strokeEllipse(in: NSMakeRect(1, 1, frame.width - 2, frame.height - 2))
|
|
}
|
|
|
|
|
|
ctx.move(to: center)
|
|
|
|
ctx.setFillColor(theme.foregroundColor.cgColor)
|
|
ctx.addArc(center: center, radius: radius, startAngle: radians(startAngle), endAngle: radians(startAngle + angle), clockwise: false)
|
|
ctx.closePath()
|
|
ctx.clip()
|
|
ctx.fill(bounds)
|
|
|
|
|
|
// ctx.move(to: center)
|
|
|
|
}
|
|
|
|
public init(theme: TimableProgressTheme = TimableProgressTheme(), size: NSSize = NSMakeSize(40, 40)) {
|
|
self.theme = theme
|
|
super.init(frame: CGRect(origin: .zero, size: size))
|
|
}
|
|
|
|
required public init(frame frameRect: NSRect) {
|
|
super.init(frame: frameRect)
|
|
}
|
|
|
|
required public init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
}
|