Created
November 25, 2017 23:11
-
-
Save paulw11/279bcc3569473a73793da87c163502f3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CircularTimerView.swift | |
// ProgressView | |
// | |
// Created by Paul Wilkinson on 26/11/17. | |
// Copyright © 2017 Paul Wilkinson. All rights reserved. | |
// | |
import UIKit | |
protocol CircularTimerViewDelegate { | |
func ended(timerView: CircularTimerView) | |
} | |
@IBDesignable class CircularTimerView: UIView { | |
/* | |
// Only override draw() if you perform custom drawing. | |
// An empty implementation adversely affects performance during animation. | |
override func draw(_ rect: CGRect) { | |
// Drawing code | |
} | |
*/ | |
@IBInspectable var duration: TimeInterval = 30.0{ | |
didSet { | |
if self.timer != nil { | |
self.startTime = Date() | |
} | |
} | |
} | |
var timeRemaining: TimeInterval { | |
get { | |
return duration - elapsed | |
} | |
} | |
var elapsed: TimeInterval { | |
guard let start = self.startTime, let current = self.currentTime else { | |
return 0 | |
} | |
return current.timeIntervalSince(start) | |
} | |
@IBInspectable var timeLabelString: String = "" { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var progressColour: UIColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1) { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var progressBackgroundColour: UIColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.4) { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var progressBackroundLineWidth: CGFloat = 5.0 { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var innerShadow: UIColor = UIColor.black.withAlphaComponent(0.22) { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var innerShadowOffset: CGSize = CGSize(width: 3.1, height: 3.1) { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var innerShadowBlurRadius: CGFloat = 4 { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
@IBInspectable var progressLineWith: CGFloat = 5 { | |
didSet { | |
setNeedsDisplay() | |
} | |
} | |
public var delegate: CircularTimerViewDelegate? | |
private var startAngle = CGFloat(-90 * Double.pi / 180) | |
private var endAngle = CGFloat(270 * Double.pi / 180) | |
private var timer: Timer? | |
private var startTime: Date? | |
private var currentTime: Date? | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
} | |
required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
} | |
public func start() { | |
self.timer?.invalidate() | |
self.startTime = Date().addingTimeInterval(-self.elapsed) | |
self.currentTime = Date() | |
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true, block: { (timer) in | |
self.currentTime = Date() | |
self.setNeedsDisplay() | |
if self.elapsed >= self.duration { | |
self.timer?.invalidate() | |
self.timer = nil | |
self.delegate?.ended(timerView: self) | |
} | |
}) | |
} | |
public func pause() { | |
self.timer?.invalidate() | |
} | |
public func stop() { | |
self.timer?.invalidate() | |
self.timer = nil | |
self.startTime = nil | |
self.currentTime = nil | |
} | |
override func draw(_ rect: CGRect) { | |
// General Declarations | |
guard let context = UIGraphicsGetCurrentContext() else { | |
return | |
} | |
// Background Drawing | |
let backgroundPath = UIBezierPath(ovalIn: CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: rect.height)) | |
backgroundColor?.setFill() | |
backgroundPath.fill() | |
// ProgressBackground Drawing | |
let progressPadding: CGFloat = 15 | |
let progressBackgroundPath = UIBezierPath(ovalIn: CGRect(x: rect.minX + progressPadding/2, y: rect.minY + progressPadding/2, width: rect.size.width - progressPadding, height: rect.size.height - progressPadding)) | |
progressBackgroundColour.setStroke() | |
progressBackgroundPath.lineWidth = self.progressBackroundLineWidth | |
progressBackgroundPath.stroke() | |
// Progress Drawing | |
let progressRect = CGRect(x: rect.minX + progressPadding/2, y: rect.minY + progressPadding/2, width: rect.size.width - progressPadding, height: rect.size.height - progressPadding) | |
let progressPath = UIBezierPath() | |
// progressPath.addArc(withCenter: CGPoint(x: progressRect.midX, y: progressRect.midY), radius: progressRect.width / 2, startAngle: startAngle, endAngle: (endAngle - startAngle) * CGFloat(1 - elapsed/duration) + startAngle, clockwise: true) | |
let percentComplete = CGFloat(elapsed / duration) | |
let arcAngle = (endAngle - startAngle) * percentComplete | |
progressPath.addArc(withCenter: CGPoint(x: progressRect.midX, y: progressRect.midY), radius: progressRect.width / 2, startAngle: startAngle, endAngle: startAngle+arcAngle, clockwise: true) | |
progressColour.setStroke() | |
progressPath.lineWidth = self.progressLineWith | |
progressPath.lineCapStyle = CGLineCap.round | |
progressPath.stroke() | |
// Text Drawing | |
let textRect = CGRect(x: rect.minX, y: rect.minY, width: rect.size.width, height: rect.size.height) | |
let textContent = NSString(string: timeLabelString) | |
let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle | |
textStyle.alignment = .center | |
let textFontAttributes = [ | |
NSAttributedStringKey.font: UIFont(name: "Helvetica", size: rect.width / 12)!, // 3, HelveticaNeue-Light | |
NSAttributedStringKey.foregroundColor: UIColor.black, // NSAttributedStringKey.foregroundColor: titleColor, | |
NSAttributedStringKey.paragraphStyle: textStyle] | |
let textHeight = textContent.boundingRect(with: CGSize(width: textRect.width, height: textRect.height), options: .usesLineFragmentOrigin, attributes: textFontAttributes, context: nil).height | |
context.saveGState() | |
context.clip(to: textRect) | |
textContent.draw(in: CGRect(x: textRect.minX, y: textRect.minY + (textRect.height - textHeight) / 2, width: textRect.width, height: textHeight), withAttributes: textFontAttributes) | |
context.restoreGState(); | |
} | |
override func prepareForInterfaceBuilder() { | |
self.currentTime = Date() | |
self.startTime = self.currentTime?.addingTimeInterval(-self.duration/3) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment