I have amended some code I pulled from https://github.com/kaandedeoglu/KDCircularProgress
I have a small UIView which represents the circle progress timer. I have referenced it and also instantiated it to KDCircularProgress class.
I have managed to implement methods to start and reset the timer progress circular bar.
But I am having trouble restarting the circular progress bar when I pause the animation.
My code preamble:
import UIKit
class SomeTableViewController: UITableViewController {
//Circular progress vars
var currentCount = 1.0
let maxCount = 60.0
//Reference selected UIView & instantiate
#IBOutlet weak var circularProgressView: KDCircularProgress!
Start animation - 60 second animation:
if currentCount != maxCount {
currentCount += 1
circularProgressView.animateToAngle(360, duration: 60, completion: nil)
}
To stop and reset the animation:
currentCount = 0
circularProgressView.animateFromAngle(circularProgressView.angle, toAngle: 0, duration: 0.5, completion: nil)
To pause the animation:
circularProgressView.pauseAnimation()
How would I set up a method to restart the animation after the paused state?
Many thanks in advance for any clarification. It's my first animation, I have tried to resolve the matter myself, but cannot seem to find any syntax applicable to my particular case.
UPDATED SOLUTION:
Thanks to #Duncan C for putting me on the right path.
I solved my problem as follows ...
Since I initiated the counter's progress using currentCount += 1 I thought I would try to pause the counter with:
if currentCount != maxCount {
currentCount -= 1
circularProgressView.animateToAngle(360, duration: 60, completion: nil)
}
which I thought would have a 'net' effect on the counter (netting off counter +=1 and counter -=1) to effectively stop the counter. In theory this should being the counter's progress to zero, but it continued to count down.
So I reverted back to circularProgressView.pauseAnimation() to pause the circular counter animation.
To restart the animation after being paused, I had to amend the duration to represent the updated duration - i.e. the time at which the animation was paused.
I used a bit of a trick here and included a NSTimer - which I happened to have in my code anyway.
To restart the animation at the time of pause:
if currentCount != maxCount {
currentCount += 1
circularProgressView.animateToAngle(360, duration: NSTimeInterval(swiftCounter), completion: nil)
}
I couldn't figure out how to update my duration for the animated circular bar, but did know how to update time passed using an NSTimer - timer with the same duration and countdown speed. So I tagged the reference to the updated timer's value. Issue resolved ;)
My Code:
import UIKit
class SomeFunkyTableViewController: UITableViewController {
//Circular progress variables
var currentCount = 0.0
let maxCount = 60.0
#IBOutlet weak var circularProgressView: KDCircularProgress!
//Timer countdown vars
var swiftTimer = NSTimer()
var swiftCounter = 60
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var timerView: UIView!
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var startView: UIView!
override func viewDidLoad() {
circularProgressView.angle = 0
timerLabel.text = String(swiftCounter)
super.viewDidLoad()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func startButton(sender: AnyObject) {
pauseBtn.alpha = 1.0
playBtn.alpha = 1.0
stopBtn.alpha = 1.0
circularProgressView.hidden = false
if currentCount != maxCount {
currentCount += 1
circularProgressView.animateToAngle(360, duration: 60, completion: nil)
}
startView.hidden = true
timerView.hidden = false
swiftTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: #selector(SomeFunkyTableViewController.updateCounter), userInfo: nil, repeats: true)
}
#IBOutlet weak var pauseBtn: UIButton!
#IBAction func pauseButton(sender: AnyObject) {
circularProgressView.pauseAnimation()
swiftTimer.invalidate()
pauseBtn.alpha = 0.5
playBtn.alpha = 1.0
stopBtn.alpha = 1.0
}
#IBOutlet weak var playBtn: UIButton!
#IBAction func playButton(sender: AnyObject) {
if currentCount != maxCount {
currentCount += 1
circularProgressView.animateToAngle(360, duration: NSTimeInterval(swiftCounter), completion: nil)
}
if !swiftTimer.valid {
swiftTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: #selector(SomeFunkyTableViewController.updateCounter), userInfo: nil, repeats: true)
}
if swiftCounter == 0 {
swiftTimer.invalidate()
}
pauseBtn.alpha = 1.0
playBtn.alpha = 0.5
stopBtn.alpha = 1.0
}
#IBOutlet weak var stopBtn: UIButton!
#IBAction func stopButton(sender: AnyObject) {
currentCount = 0
circularProgressView.animateFromAngle(circularProgressView.angle, toAngle: 0, duration: 0.5, completion: nil)
circularProgressView.hidden = true
timerView.hidden = true
startView.hidden = false
swiftTimer.invalidate()
swiftCounter = 60
timerLabel.text = String(swiftCounter)
pauseBtn.alpha = 1.0
playBtn.alpha = 1.0
stopBtn.alpha = 0.5
}
func updateCounter() {
swiftCounter -= 1
timerLabel.text = String(swiftCounter)
if swiftCounter == 0 {
swiftTimer.invalidate()
}
}
}
Side Note : I have two overlapping views - StartView and TimerView. One is hidden on view load, hence the hide/unhide references. And I dim buttons on press - play/pause/stop.
Pausing and resuming an animation takes special code. You might not be able to do it without modifying the library you are using.
The trick is to set the speed on the animation on the parent layer that's hosting the animation to 0 and record the time offset of the animation.
Here are a couple of methods (written in Objective-C) from one of my projects that pause and resume an animation:
- (void) pauseLayer: (CALayer *) theLayer
{
CFTimeInterval mediaTime = CACurrentMediaTime();
CFTimeInterval pausedTime = [theLayer convertTime: mediaTime fromLayer: nil];
theLayer.speed = 0.0;
theLayer.timeOffset = pausedTime;
}
//-----------------------------------------------------------------------------
- (void) removePauseForLayer: (CALayer *) theLayer;
{
theLayer.speed = 1.0;
theLayer.timeOffset = 0.0;
theLayer.beginTime = 0.0;
}
//-----------------------------------------------------------------------------
- (void) resumeLayer: (CALayer *) theLayer;
{
CFTimeInterval pausedTime = [theLayer timeOffset];
[self removePauseForLayer: theLayer];
CFTimeInterval mediaTime = CACurrentMediaTime();
CFTimeInterval timeSincePause =
[theLayer convertTime: mediaTime fromLayer: nil] - pausedTime;
theLayer.beginTime = timeSincePause;
}
Note that starting in iOS 10, there is a newer, better way to pause and resume UIView animations: UIViewPropertyAnimator.
I have a sample project (written in Swift) on Github that demonstrates this new UIViewPropertyAnimator class. Here's the link: UIViewPropertyAnimator-test
Below is an extract from the README from that project:
A UIViewPropertyAnimator allows you to easily create UIView-based animations that can be paused, reversed, and scrubbed back and forth.
A UIViewPropertyAnimator object takes a block of animations, very much like the older animate(withDuration:animations:) family of UIView class methods. However, a UIViewPropertyAnimator can be used to run mulitiple animation blocks.
There is built-in support for scrubbing an animation by setting the fractionComplete property on the animator. There is NOT an automatic mechanism to observe the animation progress, however.
You can reverse a UIViewPropertyAnimator animation by setting its isReversed property, but there are some quirks. If you change the isReversed property of a running animator from false to true, the animate reverses, but you can't set the isReversed property from true to false while the animation is running and have it switch direction from reverse to forward "live". You have to first pause the animation, switch the isReversed flag, and then restart the animation. (To use an automotive analogy, you can switch from forward to reverse while moving, but you have to come to a comlete stop before you can switch from reverse back into drive.)
Related
I am playing around to learn and implemented a progressView that is triggered using a function and reset using another one when buttons are clicked (and feed it with value). For some reason, after the first click, it works fine even though the functions are called also on the following clicks and the progressView is reset, its animation is not restarting (checked functions and values are all correct using print()...)
import UIKit
class ViewController: UIViewController {
let eggTimes = ["Soft" : 5, "Medium" : 7, "Hard": 12]
#IBOutlet weak var progressView: UIProgressView!
//reset the progress bar
func resetProgress(){
progressView.setProgress(0, animated: true)
print("function progress view reset fired")
}
//starts the prgress bar
func startProgressView(duration: Double) {
UIView.animate(withDuration: duration) {
print("progress bar has been initiated")
self.progressView.setProgress(1.0, animated: true)
}
}
#IBAction func hardnessSelected(_ sender: UIButton) {
resetProgress()
print("progress bar has been reset")
let hardness = sender.currentTitle!
var timerCounter = eggTimes[hardness]! * 10
let temp1: Double = Double(timerCounter)
print("temp 1 = \(temp1) ")
self.startProgressView(duration: temp1)
print(eggTimes[hardness]!)
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
if timerCounter > 0 {
timerCounter -= 1
// print(timerCounter)
}
else {
print("egg ready")
timer.invalidate()
}
}
}
}
I'm new to UI programming and right now I'm trying to make the screen pulse at a speed based on the number of times the screen has been tapped. My issue is that when a tap is detected and the duration of the animation is shortened, it starts the animation from the beginning creating a white flash as it restarts. How would I go about speeding up the animation from whatever point it is at when a tap is detected.
My Code:
class ViewController: UIViewController {
var tapCount: Int = 0
var pulseSpeed: Double = 3
override func viewDidLoad() {
super.viewDidLoad()
counter.center = CGPoint(x: 185, y: 118)
pulseAnimation(pulseSpeed: pulseSpeed)
}
func pulseAnimation(pulseSpeed: Double) {
UIView.animate(withDuration: pulseSpeed, delay: 0, options: [UIViewAnimationOptions.repeat, UIViewAnimationOptions.autoreverse],
animations: {
self.red.alpha = 0.5
self.red.alpha = 1.0
})
}
#IBOutlet weak var red: UIImageView!
#IBOutlet weak var counter: UILabel!
#IBAction func screenTapButton(_ sender: UIButton) {
tapCount += 1
counter.text = "\(tapCount)"
pulseSpeed = Double(3) / Double(tapCount)
pulseAnimation(pulseSpeed: pulseSpeed)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You need to use Core Animation directly to achieve what you're after instead of relying on the UIView animation which is built in top.
// create animation in viewDidLoad
let pulseAnimation = CABasicAnimation(keyPath: "opacity")
pulseAnimation.fromValue = 0.5
pulseAnimation.toValue = 1.0
pulseAnimation.autoreverses = true
pulseAnimation.duration = 3.0
pulseAnimation.repeatCount = .greatestFiniteMagnitude
// save animation to property on ViewController
self.pulseAnimation = pulseAnimation
// update animation speed in screenTapButton
pulseAnimation.speed += 0.5
You may want to play with the speed numbers a little. The default speed is 1.0 and the animation specifies a duration of 3 seconds, so it should take 6 seconds to go from 0.5 to 1.0 back to 0.5. At a speed of 2.0, the same animation will happen twice as quickly, or 3 seconds for the full cycle.
I hope that helps!
I have a UIScrollView setup but when I change view the music doesn't stop. How do I make it so the music stops when you change view?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear( animated)
meditationState = .on
setTrackForPlayerWith(trackName: "Bigsur")
player.play()
player.numberOfLoops = -1
}
Here is the whole class from viewcontroller.swift. I've added the func scrollViewDidScroll and the self part that you mentioned but it's still not working.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let vc0 = ViewController0(nibName: "ViewController0", bundle: nil)
var frame0 = vc0.view.frame
frame0.origin.x = self.view.frame.size.width
vc0.view.frame = frame0
self.addChildViewController(vc0)
self.scrollView.addSubview(vc0.view)
vc0.didMove(toParentViewController: self)
let vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
var frame1 = vc1.view.frame
frame1.origin.x = self.view.frame.size.width
vc1.view.frame = frame1
self.addChildViewController(vc1)
self.scrollView.addSubview(vc1.view)
vc1.didMove(toParentViewController: self)
let vc2 = ViewController2(nibName: "ViewController2", bundle: nil)
var frame2 = vc2.view.frame
frame2.origin.x = self.view.frame.size.width * 2
vc2.view.frame = frame2
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMove(toParentViewController: self)
let vc3 = ViewController3(nibName: "ViewController3", bundle: nil)
var frame3 = vc3.view.frame
frame3.origin.x = self.view.frame.size.width * 3
vc1.view.frame = frame3
self.addChildViewController(vc3)
self.scrollView.addSubview(vc3.view)
vc3.didMove(toParentViewController: self)
self.scrollView.contentSize = CGSize(width: Double(self.view.frame.size.width * 4), height: Double(self.view.frame.size.height - 66))
self.scrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x > self.view.frame.size.x {
player.stop()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ViewController3 where 'player' is declared:
import UIKit
import AVFoundation
enum MeditationState {
case on
case off
}
class ViewController3: UIViewController {
var player:AVAudioPlayer = AVAudioPlayer()
var player1:AVAudioPlayer = AVAudioPlayer()
var meditationState: MeditationState?
var replicatorLayer = CAReplicatorLayer()
var dot = CALayer()
func updateTimer(){
seconds += 1
timerclock.text = "\(seconds)"
}
// Animation starts running
func animation2() {
// A layer that creates a specified number of copies of its sublayers (the source layer), each copy potentially having geometric, temporal, and color transformations applied to it.
replicatorLayer = CAReplicatorLayer()
// The layer’s bounds rectangle. Animatable.
replicatorLayer.bounds = CGRect(x: 0.0, y: 0.0, width: 300.0, height: 300.0)
// The radius to use when drawing rounded corners for the layer’s background. Animatable.
replicatorLayer.cornerRadius = 10.0
// The background color of the receiver. Animatable.
replicatorLayer.backgroundColor = UIColor(white: 0.0, alpha: 0.0).cgColor
// The layer’s position in its superlayer’s coordinate space. Animatable.
replicatorLayer.position = view.center
// calling this method creates an array for that property and adds the specified layer to it.
view.layer.addSublayer(replicatorLayer)
// connectng the animation to the content
// An object that manages image-based content and allows you to perform animations on that content
dot = CALayer()
// The layer’s bounds rectangle. Animatable.
dot.bounds = CGRect(x: 0.0, y: 0.0, width: 12.0, height: 12.0)
//The layer’s position in its superlayer’s coordinate space. Animatable.
dot.position = CGPoint(x: 150.0, y: 40.0)
//The background color of the receiver. Animatable.
dot.backgroundColor = UIColor(white: 0.2, alpha: 1.0).cgColor
// The color of the layer’s border. Animatable.
dot.borderColor = UIColor(white: 1.0, alpha: 1.0).cgColor
// The width of the layer’s border. Animatable.
dot.borderWidth = 1.0
//The radius to use when drawing rounded corners for the layer’s background. Animatable.
dot.cornerRadius = 5.0
//Appends the layer to the layer’s list of sublayers.
replicatorLayer.addSublayer(dot)
// number of copies of layer is instanceCount
let nrDots: Int = 1000
//The number of copies to create, including the source layers.
replicatorLayer.instanceCount = nrDots
// The basic type for floating-point scalar values in Core Graphics and related frameworks.
let angle = CGFloat(2*M_PI) / CGFloat(nrDots)
// The transform matrix applied to the previous instance to produce the current instance. Animatable.
replicatorLayer.instanceTransform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
// Type used to represent elapsed time in seconds.
let duration: CFTimeInterval = 10.0
// animation capabilities for a layer property.
// An object that provides basic, single-keyframe animation capabilities for a layer property.
let shrink = CABasicAnimation(keyPath: "transform.scale")
// Defines the value the receiver uses to start interpolation.
shrink.fromValue = 1.0
// Defines the value the receiver uses to end interpolation.
shrink.toValue = 0.1
// Specifies the basic duration of the animation, in seconds.
shrink.duration = duration
// Determines the number of times the animation will repeat.
shrink.repeatCount = Float.infinity
// Add the specified animation object to the layer’s render tree.
dot.add(shrink, forKey: "shrink")
// Specifies the delay, in seconds, between replicated copies. Animatable.
replicatorLayer.instanceDelay = duration/Double(nrDots)
// The transform applied to the layer’s contents. Animatable.
dot.transform = CATransform3DMakeScale(0.01, 0.01, 0.01)
}
// connecting the breathe in label
#IBOutlet weak var label: UILabel!
// instant delay
#IBOutlet weak var instantDelay: UIButton!
#IBAction func delayBtn(_ sender: Any) {
dot.removeAnimation(forKey: "shrink")
timer1.invalidate()
seconds = 0
timer2.invalidate()
timerclock.text = "\(seconds)"
time = 0
timerLabel.text = "Breathe in"
timerisOn = false
pauseBtn.isHidden = true
playBtn.isHidden = false
label.isHidden = true
replicatorLayer.isHidden = true
instantDelay.isHidden = true
instantDelay1.isHidden = false
slider.isHidden = false
}
// Delay 1
#IBOutlet weak var instantDelay1: UIButton!
#IBAction func delayBtn1(_ sender: Any) {
instantDelay1.isHidden = true
instantDelay.isHidden = false
label.isHidden = false
slider.isHidden = true
}
//Slider for changing animation speed
#IBOutlet weak var slider: UISlider!
#IBAction func slider(_ sender: Any) {
}
#IBAction func speed(_ sender: UISlider) {
view.layer.speed = sender.value
}
//Sound On button
#IBOutlet weak var soundOn: UIButton!
#IBAction func SoundOn(_ sender: Any) {
meditationState = .on
setTrackForPlayerWith(trackName: "Mute")
player.play()
soundoff.isHidden = false
soundOn.isHidden = true
}
//Sound Off button
#IBOutlet weak var soundoff: UIButton!
#IBAction func SoundOff(_ sender: Any) {
meditationState = .off
setTrackForPlayerWith(trackName: "Bigsur")
player.play()
soundoff.isHidden = true
soundOn.isHidden = false
}
//Timerclock at top of screen label
#IBOutlet weak var timerclock: UILabel!
// creating vars to set things
var animation = CFTimeInterval()
var timer1 = Timer()
var timer2 = Timer()
var time = 0
var seconds = 0
var timerisOn = false
// connecting breathe in label
#IBOutlet var question: UILabel!
var arrayOfStrings: [String] = [""]
// connecting timerclick and starting it
#IBOutlet var timerLabel: UILabel!
// changes the amount of time on the label of different labels
func increaseTimer() {
time += 1
switch time {
case 0 ... 7:
timerLabel.text = "Hold"
case 8 ... 10:
timerLabel.text = "Breathe Out"
case 11 ... 12:
timerLabel.text = "Breathe in"
default:
time = 0
}
}
// connecting the play button and vars
#IBOutlet weak var playBtn: UIButton!
#IBAction func play(sender: AnyObject) {
bell(trackName: "Bell")
player1.play()
timer1 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController3.increaseTimer), userInfo: nil, repeats: true)
pauseBtn.isHidden = false
playBtn.isHidden = true
if timerisOn == false {
timer2 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
timerisOn = true
}
animation2()
}
// pausing the timer with the vars
#IBOutlet weak var pauseBtn: UIButton!
#IBAction func pause(sender: AnyObject) {
dot.removeAnimation(forKey: "shrink")
timer1.invalidate()
seconds = 0
timer2.invalidate()
timerclock.text = "\(seconds)"
time = 0
timerLabel.text = "Breathe in"
timerisOn = false
pauseBtn.isHidden = true
playBtn.isHidden = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear( animated)
meditationState = .on
setTrackForPlayerWith(trackName: "Bigsur")
player.play()
player.numberOfLoops = -1
}
override func viewDidLoad() {
super.viewDidLoad()
time += 1
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
func setTrackForPlayerWith(trackName: String) {
do
{
let audioPath = Bundle.main.path(forResource: trackName, ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
}
catch
{
//ERROR
}
}
func bell(trackName: String) {
do
{
let audioPath = Bundle.main.path(forResource: trackName, ofType: "mp3")
try player1 = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
}
catch
{
//ERROR
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Are you looking for viewWillDisappear(_:)? In that method you can just add player.stop() to stop the music when you change views.
The smartass way to do this is just ....
let's say it's a vertical table.
1. Each table view cell has an audio track associated with it. Think of one particular cell - C - it has an audio track A.
2. As the view scrolls (ie, whenever it is moving), just get the frame of C
3. Just take the height. Then take the height of the screen SH. Then get the distance of C from the center of the screen .. so Abs(SH - H). Then just get that figure as a fraction (zero to one) of the SH. So, Abs(SH - H)/H
(Depending on your situation, it may be better if that is divided by the height of cells, rather than screen height.)
4. Now ... simply set the volume of all the audio track A, to that fraction. And in fact, simply do that for every cell.
As you scroll, the audio will mix between the various tracks.
"Magic" :)
You can observe the changes made in your UIScrollView subclass in various delegate methods.
First of all, lets make sure your scrollView's delegate is assigned to the viewController. To do so, one option is to add following to viewDidLoad()
// `scrollView` should be whatever is your scrollView called in your VC
self.scrollView.delegate = self
Once this is done, lets make your UIViewController subclass conform to UIScrollViewDelegate
class ViewController: UIViewController, UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x > self.view.frame.size.x {
player.stop()
}
}
}
UIScrollViewDelegate has numerous methods to observe changes in your scrollView. scrollViewDidScroll(_:) will be called every time there is an interaction with the scrollView, so as soon as the contentOffset is greater than the width of the view, lets stop the music.
Im new to swift and I'm trying to create an app where a slider moves back and forth form a value of 1- 100 for 60 second by pressing a button.However,I am not sure how to make the slider move smoothly from 1-100.This is my code so far:
#IBOutlet weak var button: UIButton!
#IBOutlet weak var timer: UILabel!
var county = 5
#IBAction func animateSlider(sender: AnyObject) {if (county == 59){
_ = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)}
if (county>0){
Slider.setValue(100, animated: true)
}
The Problem? The UISlider value instantly moves from 1-100.So, how do I slow down this animation to 5 seconds or so?
Im aware of a similar thread but i'm new to Swift and I believe the other thread is written in Objective-C.Thus,I would love if anyone could help me explain in terms of swift.Thanks in advance!
#IBAction func uiSliderShowProgress(sender: UISlider) {
if sender.value == 100 {
var transition: CATransition = CATransition()
transition.startProgress = 0
transition.endProgress = 1.0
transition.duration = 0.8
uiSliderShowProgress.layer.addAnimation(transition, forKey: "transition")
uiSliderShowProgress.value = 0
lblSliderProgress.text = "\(Int(uiSliderShowProgress.value))"
}
else {
var transition: CATransition = CATransition()
transition.startProgress = 0
transition.endProgress = 1.0
transition.duration = 0.8
uiSliderShowProgress.layer.addAnimation(transition, forKey: "transition")
uiSliderShowProgress.value = 100
lblSliderProgress.text = "\(Int(uiSliderShowProgress.value))"
}
}
#IBAction func btnChangeSliderValueClick(sender: UIButton) {
self.uiSliderShowProgress(uiSliderShowProgress)
}
This changes the value of slider from 0-100 and 100-0 on buttons click and makes the thumb of the slider fade in and out, creating an illusion of being delayed!!! Hope this will help you...
I have seen 1 other question like this but I didn't help. In my application, I want a timer to start counting when I press the start button. I also want to move a button from one side of the screen to another (when I click the start button). To add to that, I want another button to move from one side of the screen to another after a 1 second delay of clicking the start button. The problem is that even though I gave a different name for each timer (NS Timer), they are messing around with each other. After clicking the start button, the timer for counting the seconds and the first button who moves work fine, but after 1 second, the first button goes back to the beginning and starts over and the second button starts to move but then it does the same thing as the first button. The timer that counts the seconds still works fine though. Heres the code (BTW I use CADisplayLink to move the buttons):
var displayLink: CADisplayLink?
var displayLink1: CADisplayLink?
#IBOutlet var moving1outlet: UIButton!
#IBOutlet var moving2outlet: UIButton!
#IBOutlet var timerlabel: UILabel!
var timer = NSTimer()
var timer2 = NSTimer()
var startTime = NSTimeInterval()
#IBAction func startbutton(sender: UIButton) {
timer.invalidate()
timer2.invalidate()
//CREATING THE COUNTING TIMER
let aSelector : Selector = "updateTime"
timer2 = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: aSelector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
displayLink?.invalidate()
displayLink1?.invalidate()
//MOVING BUTTON 1
moving1outlet.frame = CGRectMake(120, 400, 100, 100)
displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
//DELAY TO MOVE BUTTON 2
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerAction", userInfo: nil, repeats: false)
}
func timerAction() {
//MOVING BUTTON 2
self.moving2outlet.frame = CGRectMake(120, 400, 100, 100)
self.displayLink1 = CADisplayLink(target: self, selector: "handleDisplayLink1:")
self.displayLink1?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
}
func updateTime() {
//COUNTING TIMER
let currentTime = NSDate.timeIntervalSinceReferenceDate()
//Find the difference between current time and start time.
var elapsedTime: NSTimeInterval = currentTime - startTime
//calculate the minutes in elapsed time.
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (NSTimeInterval(minutes) * 60)
//calculate the seconds in elapsed time.
let seconds = UInt8(elapsedTime)
elapsedTime -= NSTimeInterval(seconds)
//find out the fraction of milliseconds to be displayed.
let fraction = UInt8(elapsedTime * 100)
//add the leading zero for minutes, seconds and millseconds and store them as string constants
let strMinutes = String(format: "%02d", minutes)
let strSeconds = String(format: "%02d", seconds)
let strFraction = String(format: "%02d", fraction)
//concatenate minuets, seconds and milliseconds as assign it to the UILabel
timerlabel.text = "\(strMinutes):\(strSeconds).\(strFraction)"
}
func handleDisplayLink(displayLink: CADisplayLink) {
//POSITIONING BUTTON 1
var buttonFrame = moving1outlet.frame
buttonFrame.origin.y += -2
moving1outlet.frame = buttonFrame
if moving1outlet.frame.origin.y <= 50 {
displayLink.invalidate()
displayLink1?.invalidate()
}
}
func handleDisplayLink1(displayLink1: CADisplayLink) {
//POSITIONING BUTTON 2
var button4Frame = moving2outlet.frame
button4Frame.origin.y += -2
moving2outlet.frame = button4Frame
if moving2outlet.frame.origin.y <= 50 {
displayLink1.invalidate()
}
}
Thank you. Anton
As I mentioned under point #1 in my answer to your other question, you are modifying frame values, but you undoubtedly are using autolayout, and thus anything that triggers the autolayout engine to layout the subviews will move the views back to where they were defined by the constraints. So the problem isn't the timers, but rather the changing of frame values rather than changing constraints.
One solution is to not change frame values, but rather to create #IBOutlet references for the constraints and then in the display link handlers, change the constant of the constraint rather than changing the frame.
For example, if I wanted to move a view a distance of 50 points using a display link over 2 second period, I would do something like the following.
First, add an outlet for the leading constraint:
Then I would write a display link handler like so:
#IBOutlet weak var leftConstraint: NSLayoutConstraint!
var displayLink: CADisplayLink?
var startTime: CFAbsoluteTime?
let duration = 2.0
override func viewDidLoad() {
super.viewDidLoad()
startDisplayLink()
}
func startDisplayLink() {
startTime = CFAbsoluteTimeGetCurrent()
displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}
func stopDisplayLink() {
displayLink?.invalidate()
displayLink = nil
}
func handleDisplayLink(displayLink: CADisplayLink) {
let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
var percentComplete = CGFloat(elapsed / duration)
if percentComplete >= 1.0 {
percentComplete = 1.0
stopDisplayLink()
}
leftConstraint.constant = 100.0 + 50.0 * percentComplete
}
Note, like I suggested before, I don't move a fixed distance per call (because different devices might result in different speeds, the speed might not be constant, etc.), but rather I calculate the "percent complete" based upon time elapsed, and then calculate the coordinate from that.