diff --git a/EFCountingLabel/Classes/EFCount.swift b/EFCountingLabel/Classes/EFCount.swift index 3e3e166..2a5f8b2 100644 --- a/EFCountingLabel/Classes/EFCount.swift +++ b/EFCountingLabel/Classes/EFCount.swift @@ -47,7 +47,16 @@ extension EFCount { } public class EFCounter { + public var timingFunction: EFTiming = EFTimingFunction.linear + //works same way as CADisplayLink.frameInterval + //0 - max frame rate + public var refreshRateInterval: Int = 2 { + didSet { + guard let timer = timer else { return } + apply(interval: refreshRateInterval, to: timer) + } + } public var updateBlock: ((CGFloat) -> Void)? public var completionBlock: (() -> Void)? @@ -107,6 +116,16 @@ public class EFCounter { } } + private func apply(interval: Int, to displayLink: CADisplayLink) { + if #available(iOS 10.3, *) { + displayLink.preferredFramesPerSecond = interval > 0 ? max(1, UIScreen.main.maximumFramesPerSecond / interval) : 0 + } else if #available(iOS 10.0, *) { + displayLink.preferredFramesPerSecond = interval > 0 ? max(1, 60 / interval) : 0 + } else { + displayLink.frameInterval = interval == 0 ? 1 : interval + } + } + //set init values public func reset() { invalidate() @@ -147,11 +166,7 @@ extension EFCounter: EFCount { lastUpdate = CACurrentMediaTime() let timer = CADisplayLink(target: self, selector: #selector(updateValue(_:))) - if #available(iOS 10.0, *) { - timer.preferredFramesPerSecond = 30 - } else { - timer.frameInterval = 2 - } + apply(interval: refreshRateInterval, to: timer) timer.add(to: .main, forMode: .default) timer.add(to: .main, forMode: .tracking) self.timer = timer diff --git a/Example/EFCountingLabel.xcodeproj/project.pbxproj b/Example/EFCountingLabel.xcodeproj/project.pbxproj index f33227a..6b1c30b 100644 --- a/Example/EFCountingLabel.xcodeproj/project.pbxproj +++ b/Example/EFCountingLabel.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 29AF0091229EE2CE006A18B4 /* FrameRateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AF0090229EE2CE006A18B4 /* FrameRateViewController.swift */; }; 5EEE4459477CD89613CC9254 /* Pods_EFCountingLabel_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA8BF1F2807CCE416BEF5C0C /* Pods_EFCountingLabel_Tests.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; @@ -15,6 +16,7 @@ 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 615470077217336459F28548 /* StoryboardButtonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615478D55F97750BE62D6350 /* StoryboardButtonViewController.swift */; }; 61547605835C3408C93468DF /* CustomTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615478D4657C10BDDB2930B2 /* CustomTableViewController.swift */; }; + 6154767B05F9D92C4A3CCD3D /* DynamicFrameRateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6154777F75D51F92326A369C /* DynamicFrameRateViewController.swift */; }; 61547AB8DD5A704F70185C87 /* ListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6154772B1F9DBE58D87528C6 /* ListTableViewController.swift */; }; 61547FDE291FDA37914D8D74 /* StoryboardLabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615477C97BA7D1BE03393744 /* StoryboardLabelViewController.swift */; }; 88B4CB65271A07819AE9D976 /* Pods_EFCountingLabel_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF702AD45972585A86A336D5 /* Pods_EFCountingLabel_Example.framework */; }; @@ -33,6 +35,7 @@ /* Begin PBXFileReference section */ 0D6321623D5D5D7DD361D3FA /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 276DE02E85C0E7D6ED37EDD4 /* Pods-EFCountingLabel_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EFCountingLabel_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-EFCountingLabel_Example/Pods-EFCountingLabel_Example.debug.xcconfig"; sourceTree = ""; }; + 29AF0090229EE2CE006A18B4 /* FrameRateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameRateViewController.swift; sourceTree = ""; }; 378DEADB22321D1400F0F63D /* README_CN.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README_CN.md; path = ../README_CN.md; sourceTree = ""; }; 378DEADC22321D1400F0F63D /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; 47A0BF93F64203C763BE2E42 /* Pods-EFCountingLabel_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EFCountingLabel_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-EFCountingLabel_Tests/Pods-EFCountingLabel_Tests.release.xcconfig"; sourceTree = ""; }; @@ -46,6 +49,7 @@ 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 6154772B1F9DBE58D87528C6 /* ListTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTableViewController.swift; sourceTree = ""; }; + 6154777F75D51F92326A369C /* DynamicFrameRateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicFrameRateViewController.swift; sourceTree = ""; }; 615477C97BA7D1BE03393744 /* StoryboardLabelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardLabelViewController.swift; sourceTree = ""; }; 615478D4657C10BDDB2930B2 /* CustomTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTableViewController.swift; sourceTree = ""; }; 615478D55F97750BE62D6350 /* StoryboardButtonViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardButtonViewController.swift; sourceTree = ""; }; @@ -156,6 +160,8 @@ 615478D55F97750BE62D6350 /* StoryboardButtonViewController.swift */, 615477C97BA7D1BE03393744 /* StoryboardLabelViewController.swift */, 615478D4657C10BDDB2930B2 /* CustomTableViewController.swift */, + 29AF0090229EE2CE006A18B4 /* FrameRateViewController.swift */, + 6154777F75D51F92326A369C /* DynamicFrameRateViewController.swift */, ); path = ViewControllers; sourceTree = ""; @@ -345,9 +351,11 @@ files = ( 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 61547AB8DD5A704F70185C87 /* ListTableViewController.swift in Sources */, + 29AF0091229EE2CE006A18B4 /* FrameRateViewController.swift in Sources */, 615470077217336459F28548 /* StoryboardButtonViewController.swift in Sources */, 61547FDE291FDA37914D8D74 /* StoryboardLabelViewController.swift in Sources */, 61547605835C3408C93468DF /* CustomTableViewController.swift in Sources */, + 6154767B05F9D92C4A3CCD3D /* DynamicFrameRateViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/EFCountingLabel/Base.lproj/Main.storyboard b/Example/EFCountingLabel/Base.lproj/Main.storyboard index 42bd8fe..2ce5e87 100644 --- a/Example/EFCountingLabel/Base.lproj/Main.storyboard +++ b/Example/EFCountingLabel/Base.lproj/Main.storyboard @@ -61,7 +61,85 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -131,7 +209,7 @@ - + @@ -164,9 +242,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -189,7 +307,7 @@ - + @@ -206,7 +324,7 @@ - + @@ -227,7 +345,7 @@ - + @@ -329,7 +447,110 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/EFCountingLabel/ViewControllers/DynamicFrameRateViewController.swift b/Example/EFCountingLabel/ViewControllers/DynamicFrameRateViewController.swift new file mode 100644 index 0000000..4536603 --- /dev/null +++ b/Example/EFCountingLabel/ViewControllers/DynamicFrameRateViewController.swift @@ -0,0 +1,56 @@ +// +// Created by Kirow Onet on 2019-05-29. +// Copyright (c) 2019 CocoaPods. All rights reserved. +// + +import Foundation +import UIKit +import EFCountingLabel + +class DynamicFrameRateViewController: UIViewController { + @IBOutlet weak var countingLabel: EFCountingLabel! + @IBOutlet weak var frameIntervalSlider: UISlider! + @IBOutlet weak var expectedFrameRate: UILabel! + @IBOutlet weak var actualFrameRate: UILabel! + + var eventCount = 0 + + var timer: Timer? + + override func viewDidLoad() { + super.viewDidLoad() + countingLabel.setUpdateBlock { [unowned self] value, label in + self.eventCount += 1 + label.text = "\(Int(value.truncatingRemainder(dividingBy: 60)))" + } + frameIntervalSlider.value = Float(countingLabel.counter.refreshRateInterval) + update(interval: countingLabel.counter.refreshRateInterval) + countingLabel.countFrom(0, to: 3_600_000, withDuration: 3600) + } + + @IBAction func sliderDidChangeValue(_ sender: UISlider) { + update(interval: Int(sender.value)) + + } + + private func update(interval: Int) { + eventCount = 0 + countingLabel.counter.refreshRateInterval = interval + timer?.invalidate() + timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refreshActualFramerate), + userInfo: nil, repeats: true) + expectedFrameRate.text = interval > 0 ? "\(60 / interval)" : "max" + actualFrameRate.text = "0" + } + + @objc func refreshActualFramerate() { + actualFrameRate.text = "\(eventCount)" + eventCount = 0 + } + + deinit { + timer?.invalidate() + timer = nil + } + +} \ No newline at end of file diff --git a/Example/EFCountingLabel/ViewControllers/FrameRateViewController.swift b/Example/EFCountingLabel/ViewControllers/FrameRateViewController.swift new file mode 100644 index 0000000..9c7330d --- /dev/null +++ b/Example/EFCountingLabel/ViewControllers/FrameRateViewController.swift @@ -0,0 +1,133 @@ +// +// FrameRateViewController.swift +// EFCountingLabel_Example +// +// Created by Kirow Onet on 5/29/19. +// Copyright © 2019 CocoaPods. All rights reserved. +// + +import Foundation +import EFCountingLabel + +class FrameRateViewController: UIViewController { + + @IBOutlet weak var rate5: EFCountingLabel! //5fps + @IBOutlet weak var rate6: EFCountingLabel! //6fps + @IBOutlet weak var rate8: EFCountingLabel! //7.5fps -> 8fps + @IBOutlet weak var rate10: EFCountingLabel! //10fps + @IBOutlet weak var rate12: EFCountingLabel! //12fps + @IBOutlet weak var rate15: EFCountingLabel! //15fps + @IBOutlet weak var rate30: EFCountingLabel! //30fps + @IBOutlet weak var rate60: EFCountingLabel! //60fps + + var rate5Count = 0 + var rate6Count = 0 + var rate8Count = 0 + var rate10Count = 0 + var rate12Count = 0 + var rate15Count = 0 + var rate30Count = 0 + var rate60Count = 0 + + var cycle = 0 + + override func viewDidLoad() { + super.viewDidLoad() + + rate5.counter.refreshRateInterval = Int(60.0 / 5.0) + rate6.counter.refreshRateInterval = Int(60.0 / 6.0) + rate8.counter.refreshRateInterval = Int(60.0 / 7.5) //this one works unexpected + rate10.counter.refreshRateInterval = Int(60.0 / 10.0) + rate12.counter.refreshRateInterval = Int(60.0 / 12.0) + rate15.counter.refreshRateInterval = Int(60.0 / 15.0) + rate30.counter.refreshRateInterval = Int(60.0 / 30.0) + rate60.counter.refreshRateInterval = Int(60.0 / 60.0) + + rate5.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate5Count += 1 + }) + rate6.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate6Count += 1 + }) + rate8.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate8Count += 1 + }) + rate10.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate10Count += 1 + }) + rate12.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate12Count += 1 + }) + rate15.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate15Count += 1 + }) + rate30.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate30Count += 1 + }) + rate60.setUpdateBlock({ [weak self] value, label in + label.text = "\(Int(value))" + self?.rate60Count += 1 + }) + + refresh() + } + + @IBAction func refresh() { + rate5Count = 0 + rate6Count = 0 + rate8Count = 0 + rate10Count = 0 + rate12Count = 0 + rate15Count = 0 + rate30Count = 0 + rate60Count = 0 + + cycle += 1 + + //expectation for 60fps screen + rate5.setCompletionBlock({ [unowned self] _ in + print("rate5 - \(self.rate5Count). (expected \(5 * self.cycle)+1)") + }) + rate6.setCompletionBlock({ [unowned self] _ in + print("rate6 - \(self.rate6Count). (expected \(6 * self.cycle)+1)") + }) + rate8.setCompletionBlock({ [unowned self] _ in + print("rate8 - \(self.rate8Count). (expected \(Int(7.5) * self.cycle)+1)") + }) + rate10.setCompletionBlock({ [unowned self] _ in + print("rate10 - \(self.rate10Count). (expected \(10 * self.cycle)+1)") + }) + rate12.setCompletionBlock({ [unowned self] _ in + print("rate12 - \(self.rate12Count). (expected \(12 * self.cycle)+1)") + }) + rate15.setCompletionBlock({ [unowned self] _ in + print("rate15 - \(self.rate15Count). (expected \(15 * self.cycle)+1)") + }) + rate30.setCompletionBlock({ [unowned self] _ in + print("rate30 - \(self.rate30Count). (expected \(30 * self.cycle)+1)") + }) + rate60.setCompletionBlock({ [unowned self] _ in + print("rate60 - \(self.rate60Count). (expected \(60 * self.cycle)+1)") + }) + + let duration = TimeInterval(cycle) + + print("running animation for \(cycle) second\(cycle > 1 ? "s" : "")") + + rate5.countFrom(0, to: 100, withDuration: duration) + rate6.countFrom(0, to: 100, withDuration: duration) + rate8.countFrom(0, to: 100, withDuration: duration) + rate10.countFrom(0, to: 100, withDuration: duration) + rate12.countFrom(0, to: 100, withDuration: duration) + rate15.countFrom(0, to: 100, withDuration: duration) + rate30.countFrom(0, to: 100, withDuration: duration) + rate60.countFrom(0, to: 100, withDuration: duration) + } +} diff --git a/Example/EFCountingLabel/ViewControllers/StoryboardButtonViewController.swift b/Example/EFCountingLabel/ViewControllers/StoryboardButtonViewController.swift index c4e8ce6..d890923 100644 --- a/Example/EFCountingLabel/ViewControllers/StoryboardButtonViewController.swift +++ b/Example/EFCountingLabel/ViewControllers/StoryboardButtonViewController.swift @@ -63,8 +63,6 @@ class StoryboardButtonViewController: UIViewController { } else { sender.contentHorizontalAlignment = .left sender.countFromCurrentValueTo(1000000, withDuration: 20) - } - } }