Prevent simultaneously tapping on views - ios

I have many views, and I want the behaviour that when I tap at the first view, other views interaction will be disabled until the task finish.
Now, my code looks like this.
private var lock = false
#IBAction func firstViewTapped(sender: UITapGestureRecognizer) {
if lock{
return
}
lock = true
doSomeTask{ error in
println("finish 1!!")
self.lock = false
}
}
#IBAction func secondViewTapped(sender: UITapGestureRecognizer) {
if lock{
return
}
lock = true
doSomeTask{ error in
println("finish 2!!")
self.lock = false
}
}
I am wondering. Is there another elegant way to do this ?

You can use self.view.userInteractionEnabled = false and self.view.userInteractionEnabled = true to lock and unlock it. Be careful you app may be unresponsive if you don't unlock the view properly.

Related

Struggling with referencing a variable (Swift)

Background:
I'm trying to animate a wave using BAFluidView. My goal is pretty simple: start the animation when a button is tapped, stop it when it is tapped again. The pod (linked above) provides all of the code to manage this.
Let it be known, I'm new to this. Help me learn, please!
My struggle:
I need to create a view for this wave. I put it into a function called "WaveContainer." Here's what that looks like:
func WaveAnimation() {
let wave = BAFluidView(frame: self.view.frame, startElevation: 0.02)!
wave.maxAmplitude = 10
wave.minAmplitude = 8
wave.fillDuration = 50
wave.fill(to: 0.95)
wave.fillAutoReverse = false
wave.fillColor = UIColor.blue
waveView.addSubview(wave)
}
I then called this in the ViewDidAppear function. Of course, it works! I can see the wave, and it's waving. Nice.
Of course, I can't call the wave constant anywhere else, though! If I want to stop / start the wave on a button press, for example?
If I try to move the wave constant out of this function into ViewDidLoad or ViewDidAppear, I can't access the ...self.view.frame, and the wave won't show up on the screen.
My asks:
How should I structure this code so that I can reference the wave constant from several different functions?
How should I reference the view for the wave constant when I'm not within ViewDidLoad or some other view-based function?
Thank you SO MUCH for your help!
My Code:
import UIKit
import BAFluidView
class ViewController: UIViewController {
// OUTLETS:
#IBOutlet weak var waveView: UIView!
// ACTIONS:
#IBAction func WaveButton(_ sender: UIButton) {
// If the user taps this button and the waveHasStarted variable is equal to false, flip it to true.
if waveHasStarted == false {
print("Start the wave with a press.")
startWave = true
waveHasStarted = true
waveHasBeenStopped = false
} else {
print("Stop the wave with a press.")
startWave = false
waveHasStarted = false
waveHasBeenStopped = true
}
}
// VARIABLES:
var waveHasStarted = false
var startWave = false
var waveHasBeenStopped = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {}
// FUNCTIONS:
func WaveAnimation() {
let wave = BAFluidView(frame: self.view.frame, startElevation: 0.02)!
wave.maxAmplitude = 10
wave.minAmplitude = 8
wave.fillDuration = 50
wave.fill(to: 0.95)
wave.fillAutoReverse = false
wave.fillColor = UIColor.blue
// If the variable above has been flipped to "true," start the animation...
if startWave == true {
print("Start that wave animation")
wave.startAnimation()
} else {
// If not, stop it or do nothing.
print("Stop that wave animation")
wave.stopAnimation()
wave.keepStationary()
}
waveView.addSubview(wave)
}
}
Use lazy initialization and get the class level access,
lazy var fluidView: BAFluidView = {
let wave = BAFluidView(frame: self.view.frame, startElevation: 0.02)!
wave.maxAmplitude = 10
wave.minAmplitude = 8
wave.fillDuration = 50
wave.fill(to: 0.95)
wave.fillAutoReverse = false
wave.fillColor = UIColor.blue
return wave
}()
Now you can add, start and stop the animation anywhere as,
waveView.addSubview(fluidView)
fluidView.startAnimation()
fluidView.stopAnimation()

Why are animated calls of UIView.isHidden = false compounded?

As you can see I'm having trouble formulating the question. Let me try to explain:
I'm using a search bar in my swift ios app. In order to get a desired animation effect I put it in a vertical stack view and then animate its isHidden property. This way search bar pushes down other children of the stack view as it animates in or pulls them up as it animates out. So far so good.
I've noticed a behavior that I think is strange. Could be a bug or could be me not understanding how things work. Basically if I call search bar hiding method x times in a row I need to call search bar showing method x times before it would show. I'd expect to have to make just one call to show search bar regardless of how many times I called hiding method. The issue doesn't exist other way around: if I call search bar showing code x times I only need to call hiding method once for it to go away. This doesn't happen if I set isHidden without animating it...
Here's a sample code and a video of the issue. I'd appreciate it if someone would help me understand this behavior.
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar! {
didSet {
searchBar.isHidden = true
}
}
#IBAction func showAction(_ sender: UIButton) {
expandSearch()
}
#IBAction func hideAction(_ sender: UIButton) {
collapseSearch()
}
private func expandSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = false
}
}
private func collapseSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = true
}
searchBar.resignFirstResponder()
}
}
You should not call an asynchronous animation of searchbar x times, instead of I suggest you to keep its state in variable, something like isSearchBarHidden,
and check it before show/hide search bar. You could use just one method with such signature showSearchBar(show: Bool) and setting this variable there.
#IBAction func showAction(_ sender: UIButton) {
showSearchBar(true)
}
#IBAction func hideAction(_ sender: UIButton) {
showSearchBar(false)
}
private
func showSearchBar(_ show: Bool) {
guard isSearchBarHidden != show else {
return
}
UIView.animate(withDuration: 0.3, animations: {
self.searchBar.isHidden = show
}) {
self.isSearchBarHidden = show
if !show && searchBar.isFerstResponder {
searchBar.resignFirstResponder
}
}
}
private
var isSearchBarHidden: Bool = true
Also it is a good practice to check if your textView/textField/searchBar isFirstResponder before call resignFirstResponder.
Hope it will help. Good luck!

Can we make 1 UIButton for 2 action with swift?

i want to make 2 action for a button like that.
selected and deselected action for 1 button.
#IBAction func btntouch(sender: UIButton) {
if firsttouch
{
print bla bla
change button to selected style. maybe background color.
}
else
{
}
}
how can i do that?
In case you need to split two button statuses - like ON and OFF, try this:
var buttonSwitched : Bool = false
#IBAction func btntouch(sender: UIButton) {
//this line toggle your button status variable
//if true, it goes to false, and vice versa
self.buttonSwitched = !self.buttonSwitched
if self.buttonSwitched
{
//your UI styling
}
else
{
//your opposite UI styling
}
}
Create 2 IBActions:
#IBAction func touchDown(_ sender: AnyObject) {
print("down")
}
#IBAction func touchUp(_ sender: AnyObject) {
print("up")
}
When connecting the first one, make sure the event is set to touchDown. For the second one, make sure it is set to touchUpInside
Yes, you can. Depending on your requirements, you could store the current state of the button in the view controller or in the model.
If the visual change caused by the first touch needs to be persisted across opening and closing of your view controller, store the value indicating the change in the model; if you need to reset the visuals when the view controller shows, store the value in the view controller itself:
var firstTouch = true
#IBAction func btntouch(sender: UIButton) {
if firstTouch {
firstTouch = false
...
} else {
...
}
}

How to disable UISlider?

I've seen answers about this but no actual code explaining how to, I'm new to swift so really confused on how to perform this simple task.
I have a UISlider However I want to disable the sliders function when an if statement is true.
I current have a UISlider with an if statement but can't workout how to disable it.
#IBAction func radiusSlider(sender: UISlider) {
if location == false {
//Disable Slider
} else {
radiusData.radiusValue = Double(sender.value)
radiusLabel.text = "Radius: \(sender.value)km"
NSNotificationCenter.defaultCenter().postNotificationName(radiusValue, object: self)
}
}
Or would this be in within the viewDidLoad? If so how?
Set the UISlider's enabled property to false.
#IBAction func radiusSlider(sender: UISlider) {
if location == false {
sender.enabled = false
} else {
radiusData.radiusValue = Double(sender.value)
radiusLabel.text = "Radius: \(sender.value)km"
NSNotificationCenter.defaultCenter().postNotificationName(radiusValue, object: self)
}
}
If you want to enable/disable your slider in other functions of your view controller, you will need to make an IBOutlet property for your slider.

Disable an IBAction

I've created an IBAction from a button on the storyboard. After it is clicked, I want to disable it so its no longer functional. How can I do this?
#IBAction func b1(sender: AnyObject) {
displayLabel(1)
}
Create an outlet for your button and set its enabled property to false in your b1 func. Say your outlet is "button1":
button1.enabled = false
#Mike Taverne is right about disabling button. But from the line
button.enabled = false
will make button dark greyed out. And user clearly see that it is not tappable or clickable.
But if we use this line:
button.userInteractionEnabled = false
Then button UI will not change and user thinks that it is tappable. But related action event will not called.
In both case action will not called but the look of button will matter here.
Code will suppose to be like:
#IBAction func b1(sender: AnyObject) {
var view = UIView()
view = sender as UIView
view.userInteractionEnabled = false
}
You will get the button instance in that function as sender (AnyObject).
You need to convert it to UIButton and set it's userInteractionEnabled or enabled property to false.
#IBAction func b1(sender: AnyObject)
{
displayLabel(1)
var button = sender as UIButton;
button.userInteractionEnabled = false;
}
Alternatively, you can do it using:
#IBAction func b1(sender: UIButton)
{
displayLabel(1)
sender.enabled = false;
}

Resources