I have 2 buttons each below each other and depending on a function it shows what button is enabled and what button is disabled.
#IBOutlet weak var startBtn: workoutButton!
#IBOutlet weak var restBtn: workoutButton!
#IBAction func startBtnPressed(_ sender: AnyObject) {
startBtn.isHidden = true
startBtn.isEnabled = false
perform(#selector(workoutStartVC.revealRestModeBtn), with: 1, afterDelay: 10)
timeLeft = 0
myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(workoutStartVC.timerRunning), userInfo: nil, repeats: true)
}
#IBAction func restBtnPressed(_ sender: AnyObject) {
print("rest mode button is pressed and i am showing a overlay right now with data count down")
}
When a click the restBtn it still executes the code in the startBtnPressed. How is this possible? Because when I click startBtnPressed 1 time it should disable the button and hide it. It hides it but I am still able to execute the function. So the timer goes twice as fast.
Thanks for the help!
Kevin.
Open your storyboard, select resetButton and make sure there is only one action attached in "Sent Events" section. Right now you will see both IBActions attached to it.
It should be like this:
You probably have something like this:
Delete the IBAction Connection and reconnect them.
This problem happens when both buttons are hooked to both methods.
This usually happens, if you have copied and pasted a button the storyboard.
Related
I am trying to learn how to use "ON" and "OFF" buttons and the basics of swift in general. Everything is going well but when I try to create an infinite loop of vibration using the "ON" UIButton, the loop keeps reiterating and I can't press the "OFF" button to stop it.
I tried looking up ways to stop it but none of them mention how to apply the code. I am still new and learning how to use swift. I read about "UIViewAnimationOptionAllowUserInteraction" but I don't know how to put it into my code.
#IBOutlet weak var label: UILabel!
#IBAction func onSwitch(_ sender: UIButton) {
label.text = "ON"
vibrate()
}
#IBAction func offSwitch(_ sender: UIButton) {
label.text = "OFF"
vibrate()
}
func vibrate() {
while label.text == "ON" {
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
}
}
Your code is continuously re-running tasks in while loop. This is happening on a main thread, so your application is unable to catch off button tap, because every tick is consumed to re-run the AudioServicesPlayAlertSound().
The While Loop is wrong choice here !
You only vibrate is once by replacing the While With if condition to test your code correctly
OR
you can use Timer to vibrate every 3-5 Seconds
let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.vibrate), userInfo: nil, repeats: true)
// must be internal or public.
#objc func vibrate() {
// Something cool
}
I have a set of buttons I'd like to have crossfade through a different highlighted image than normal when touched. The technique below works fine, but only after the second touch event on the same button. The first touch produces the standard "faded dark grey" on the whole button; from then on future touches it crossfades correctly through the pictures on highlight.
I've only tested this in the simulator, is that he issue? If not, how do I produce the correct highlighted state on every touch? Thanks!
#IBOutlet weak var someButton: UIButton!
#IBAction func someButton(_ sender: UIButton) {
UIView.transition(with: sender, duration: 0.3, options: .transitionCrossDissolve, animations: {
sender.setImage(UIImage(named: "My Image.png"), for: UIControl.State.highlighted)
}, completion: nil)
//...other functions
}
Set the button's highlighted image initially at the time of setting up UIButton properties or in viewDidLoad() by writing the code below.
yourButton.setImage(UIImage(named: "My Image.png"), for: UIControl.State.highlighted)
Reason for not working your code
The button action #IBAction func someButton(_ sender: UIButton) always call only on release of the user's finger so by this time UIButton doesn't has the image for the highlighted state for the first time. Later on, the image is set through animation so it is animating perfectly.
Edit
You can make transition of UIButton with firebase call or some other event action by creating a separate function like shown below. Use the UIButton's outlet to access the button.
#IBOutlet weak var someButton: UIButton!
func buttonTransition() {
UIView.transition(with: sender, duration: 0.3, options: .transitionCrossDissolve, animations: {
someButton.setImage(UIImage(named: "My Image.png"), for: UIControl.State.highlighted)
}, completion: nil)
}
So, you can call buttonTransition() function from wherever you want.
It is working on the second click because you have written the transition code inside the button click action. Just write that code in the viewDidLoad or viewWillApear and it will work before the first click and will set the transition on the button. Write something like this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
UIView.transition(with: button, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.button.setImage(UIImage(named: "image.png"), for: UIControl.State.highlighted)
}, completion: nil)
}
here button is the outlet variable of the button created in storyboard.
I have a weird bug where a buttons touchUpInside does not work as it should. I have two buttons using the same code
#IBOutlet weak var previousIBO: UIButton!
#IBOutlet weak var nextIBO: UIButton!
#IBAction func buttonDown(sender: AnyObject) {
nextSingleFire()
timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector:#selector(nextFire), userInfo: nil, repeats: true)
}
#IBAction func buttonUp(sender: AnyObject) {
timer.invalidate()
}
nextIBO.addTarget(self, action:#selector(buttonDown(sender:)), for: .touchDown)
nextIBO.addTarget(self, action:#selector(buttonUp(sender:)), for: [.touchUpInside, .touchUpOutside, .touchDragOutside])
previousIBO.addTarget(self, action:#selector(buttonDown(sender:)), for: .touchDown)
previousIBO.addTarget(self, action:#selector(buttonUp(sender:)), for: [.touchUpInside, .touchUpOutside])
However the previous button only works when I drag after tapping. as opposed to the next button that works simply by tapping. Why am I getting this weird behavior in this one button?
I would like to add.touchDragOutside to my previous button, but I can't because then the button does not work
If I were you, I'd start by removing the #IBAction on your functions.
As far as I can tell, they are useless, since you set the actions via "addTarget". And there's a chance your weird behaviour is caused by a wrong connection between your buttons and functions (select your buttons, and open the "Connections inspector" to check if everything is fine).
One other possibility is that your buttons are overlapping, or some view is blocking your button.
Also, Timers can be tricky, take a look at this post for starting and stoping them: swift invalidate timer doesn't work
I got a button with a LongPressureGesture, and i would like to have a small ProgressView on top of this button as visual feedback for the user that the longPressureGesture is recognized.
I'm stuck on how to detect the beginning of the longPressure and the duration of the longPressure to be able to set the setProgress() on my ProgressView.
EDIT: So i inspired myself from the answers, thank you. Here is what i made. Feel free to comment the following code, maybe there is a better solution.
private var lpProgress:Float = 0
private var startTouch: NSTimer!
#IBAction func pauseJogButtonTouchDown(sender: AnyObject) {
self.startTouch = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "updateLPGestureProgressView", userInfo: nil, repeats: true)
}
func updateLPGestureProgressView() {
self.lpProgress += 0.1
self.lpGestureProgressView.setProgress(self.lpProgress, animated: true)
if self.lpProgress >= 1 {
self.startTouch.invalidate()
self.pauseBarButton.hidden = true
self.lpGestureProgressView.setProgress(0.0, animated: false)
self.toolbarHomeMadeView.hidden = false
self.switchToState(.Paused)
}
}
#IBAction func pauseJogButtonTouchUpInside(sender: AnyObject) {
self.lpProgress = 0
self.startTouch.invalidate()
self.lpGestureProgressView.setProgress(0.0, animated: false)
}
You do not need the LongPressureGesture in this case.
Use "Touch Down" IBAction of UIButton to start NSTimer, and "Touch Up Inside" to stop timer and check if the delay was right.
ProgressView you can fill by timer progress.
Set up an NSTimer on touchesBegan.
At the same time start your animation to animate the view.
When touchesEnded is triggered then stop the animation if the NSTimer has not triggered yet and cancel the timer.
When the timer finishes run your desired action.
Long Press isn't really designed for this sort of thing.
I made a practice project in Swift to learn how NSTimer works. There is one button to start the timer and one button to invalidate it. It works fine when I tap each button once. However, when I tap the start timer button multiple times, I am no longer able to invalidate it.
Here is my code:
class ViewController: UIViewController {
var counter = 0
var timer = NSTimer()
#IBOutlet weak var label: UILabel!
#IBAction func startTimerButtonTapped(sender: UIButton) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}
#IBAction func cancelTimerButtonTapped(sender: UIButton) {
timer.invalidate()
}
func update() {
++counter
label.text = "\(counter)"
}
}
I have seen these questions but I wasn't able to glean an answer to my question from them (many are old Obj-C pre-ARC days and others are different issues):
NSTimer() - timer.invalidate not working on a simple stopwatch?
Using an NSTimer in Swift
NSTimer doesn't stop
Unable to invalidate (Stop) NSTimer
NSTimer doesn't stop with invalidate
Can't invalidate, stop countdown NSTimer - Objective C
IOS: stop a NSTimer
You can add timer.invalidate() before starting a new timer in startTimerButtonTapped if you want to reset the timer each time the "start" button is tapped:
#IBAction func startTimerButtonTapped(sender: UIButton) {
timer.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}
I was going to update with an explanation but #jcaron already did it in the comment, so I'm just quoting his text, no need to change it:
Every time you tap on the "Start Timer" button, you create a new timer, while leaving the previous one running, but with no reference to it (since you've overwritten timer with the new timer you just created). You need to invalidate the previous one before you create the new one.
I would like to suggest you to set timer to nil when press on cancel button.
And don't forget to set counter =0
When invalidating the Timer.