Skip to content

Commit

Permalink
Merge pull request #26 from Coeur/kirowCompatibilityLayer
Browse files Browse the repository at this point in the history
Kirow's Reworked module + Cœur's compatibility layer
  • Loading branch information
EyreFree authored Jun 20, 2019
2 parents f52a423 + 4856e3a commit 5a53b63
Show file tree
Hide file tree
Showing 20 changed files with 1,341 additions and 734 deletions.
165 changes: 165 additions & 0 deletions EFCountingLabel/Classes/EFCount.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation
import QuartzCore

public protocol EFTiming {
func update(_ time: CGFloat) -> CGFloat
}

public protocol EFCount {
func countFrom(_ startValue: CGFloat, to endValue: CGFloat, withDuration duration: TimeInterval)
func countFromCurrentValueTo(_ endValue: CGFloat, withDuration duration: TimeInterval)
func stopCountAtCurrentValue()
}

extension EFCount {
public func countFromZeroTo(_ endValue: CGFloat, withDuration duration: TimeInterval) {
countFrom(0, to: endValue, withDuration: duration)
}

public func countFrom(_ startValue: CGFloat, to endValue: CGFloat) {
countFrom(startValue, to: endValue, withDuration: 0)
}

public func countFromCurrentValueTo(_ endValue: CGFloat) {
countFromCurrentValueTo(endValue, withDuration: 0)
}

public func countFromZeroTo(_ endValue: CGFloat) {
countFromZeroTo(endValue, withDuration: 0)
}
}

public class EFCounter {
public var timingFunction: EFTiming = EFTimingFunction.linear

public var updateBlock: ((CGFloat) -> Void)?
public var completionBlock: (() -> Void)?

public private(set) var fromValue: CGFloat = 0
public private(set) var toValue: CGFloat = 1
private var currentDuration: TimeInterval = 0
public private(set) var totalDuration: TimeInterval = 1
private var lastUpdate: TimeInterval = 0

private var timer: CADisplayLink?

public var isCounting: Bool {
return timer != nil
}

public var progress: CGFloat {
guard totalDuration != 0 else { return 1 }
return CGFloat(currentDuration / totalDuration)
}

public var currentValue: CGFloat {
if currentDuration == 0 {
return 0
} else if currentDuration >= totalDuration {
return toValue
}
return fromValue + timingFunction.update(progress) * (toValue - fromValue)
}

public init() {

}

// CADisplayLink callback
@objc public func updateValue(_ timer: Timer) {
let now = CACurrentMediaTime()
currentDuration += now - lastUpdate
lastUpdate = now

if currentDuration >= totalDuration {
invalidate()
currentDuration = totalDuration
}

updateBlock?(currentValue)

if currentDuration == totalDuration {
runCompletionBlock()
}
}

private func runCompletionBlock() {
if let tryCompletionBlock = completionBlock {
completionBlock = nil
tryCompletionBlock()
}
}

//set init values
public func reset() {
invalidate()
fromValue = 0
toValue = 1
currentDuration = 0
lastUpdate = 0
totalDuration = 1
}

public func invalidate() {
timer?.invalidate()
timer = nil
}
}

extension EFCounter: EFCount {
public func countFromCurrentValueTo(_ endValue: CGFloat, withDuration duration: TimeInterval) {
countFrom(currentValue, to: endValue, withDuration: duration)
}

public func countFrom(_ startValue: CGFloat, to endValue: CGFloat, withDuration duration: TimeInterval) {
fromValue = startValue
toValue = endValue

// remove any (possible) old timers
invalidate()

if duration == 0.0 {
// No animation
updateBlock?(endValue)
runCompletionBlock()
return
}

currentDuration = 0
totalDuration = duration
lastUpdate = CACurrentMediaTime()

let timer = CADisplayLink(target: self, selector: #selector(updateValue(_:)))
if #available(iOS 10.0, *) {
timer.preferredFramesPerSecond = 30
} else {
timer.frameInterval = 2
}
timer.add(to: .main, forMode: .default)
timer.add(to: .main, forMode: .tracking)
self.timer = timer
}

public func stopCountAtCurrentValue() {
invalidate()
updateBlock?(currentValue)
}
}
154 changes: 154 additions & 0 deletions EFCountingLabel/Classes/EFCountAdapter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation
import UIKit

public protocol EFCountAdapter: class, EFCount {
var counter: EFCounter { get }
}

extension EFCountAdapter {
public func setUpdateBlock(_ update: ((_ value: CGFloat, _ sender: Self) -> Void)?) {
if let update = update {
counter.updateBlock = { [unowned self] value in
update(value, self)
}
} else {
counter.updateBlock = nil
}
}

public func setCompletionBlock(_ completion: ((_ sender: Self) -> Void)?) {
if let completion = completion {
counter.completionBlock = { [unowned self] in
completion(self)
}
} else {
counter.completionBlock = nil
}
}

public func countFrom(_ startValue: CGFloat, to endValue: CGFloat, withDuration duration: TimeInterval) {
counter.countFrom(startValue, to: endValue, withDuration: duration)
}

public func countFromCurrentValueTo(_ endValue: CGFloat, withDuration duration: TimeInterval) {
countFrom(counter.currentValue, to: endValue, withDuration: duration)
}

public func stopCountAtCurrentValue() {
counter.stopCountAtCurrentValue()
}
}

open class EFCountingButton: UIButton, EFCountAdapter {
public private(set) var counter = EFCounter()

open var formatBlock: ((CGFloat) -> String)? {
set {
if let formatBlock = newValue {
setUpdateBlock { value, button in button.setTitle(formatBlock(value), for: .normal) }
} else {
setUpdateBlock(nil)
}
}
@available(*, unavailable)
get {
return nil
}
}
open var attributedFormatBlock: ((CGFloat) -> NSAttributedString)? {
set {
if let attributedFormatBlock = newValue {
setUpdateBlock { value, button in button.setAttributedTitle(attributedFormatBlock(value), for: .normal) }
} else {
setUpdateBlock(nil)
}
}
@available(*, unavailable)
get {
return nil
}
}
open var completionBlock: (() -> Void)? {
set {
if let completionBlock = newValue {
setCompletionBlock { _ in completionBlock() }
} else {
setCompletionBlock(nil)
}
}
@available(*, unavailable)
get {
return nil
}
}

deinit {
counter.invalidate()
}
}

open class EFCountingLabel: UILabel, EFCountAdapter {
public private(set) var counter = EFCounter()

open var formatBlock: ((CGFloat) -> String)? {
set {
if let formatBlock = newValue {
setUpdateBlock { value, label in label.text = formatBlock(value) }
} else {
setUpdateBlock(nil)
}
}
@available(*, unavailable)
get {
return nil
}
}
open var attributedFormatBlock: ((CGFloat) -> NSAttributedString)? {
set {
if let attributedFormatBlock = newValue {
setUpdateBlock { value, label in label.attributedText = attributedFormatBlock(value) }
} else {
setUpdateBlock(nil)
}
}
@available(*, unavailable)
get {
return nil
}
}
open var completionBlock: (() -> Void)? {
set {
if let completionBlock = newValue {
setCompletionBlock { _ in completionBlock() }
} else {
setCompletionBlock(nil)
}
}
@available(*, unavailable)
get {
return nil
}
}

deinit {
counter.invalidate()
}
}
68 changes: 0 additions & 68 deletions EFCountingLabel/Classes/EFCountingButton.swift

This file was deleted.

Loading

0 comments on commit 5a53b63

Please sign in to comment.