I have to UISwitches in my application. At launch, they're both set to off and the second switch is disabled and should only be enabled when the first switch is aswell, meaning:
#IBAction func switchOneToggled(sender: UISwitch) {
if switchOne.on {
switchTwo.enabled = true
}
else {
switchTWo.enabled = false
}
}
My problem is, that when I enable switchOne, switchTwo gets enabled, but I can't toggle switchTwo's on/off state by touching it.
Try resetting the switches. I had a similar problem and just deleted the switch and replaced it, and giving it the same connections that is had before. It must be a bug inside Xcode, since it should be able to turn on and off without any code.
#IBAction func switchOneToggled(sender: UISwitch) {
switchTwo.enabled = switchOne.on
}
do you maybe have userinteractionenabled set to false for switchTwo?
The problem is that your switchOneToggled is called when the Switch is tapped and the state of the switch is changed. You should check the state of Switch 1 and enable your Switch 2 in your viewDidLoad.
Related
Case: I have 2 UISwitches - Switch1 and Switch2.
Switch1 controls the visbility of a UILabel in my scene.
Switch2 once activated turns Switch1 off as well as its visbility.
Problem: After Switch2 is activated Switch1 turns off as well as disappears however my UILabel is still visible in my scene.
switch1.addTarget(self, action: #selector(switch1Action), for: UIControl.Event.valueChanged)
switch2.addTarget(self, action: #selector(switch2Action), for: UIControl.Event.valueChanged)
#objc func switch1Action(switch1: UISwitch) {
if switch1.isOn {
lockedMessage.isHidden = false
}
if !switch1.isOn {
lockedMessage.isHidden = true
}
}
#objc func switch2Action(switch2: UISwitch) {
if switch2.isOn {
switch1.isOn = false
switch1.isHidden = true
}
Many thanks! :)
If I understand your issue correctly, then it seems you want lockedMessage to be hidden if switch2 is on as well. If this is the case – you can change the visibility of the lockedMessage within your function switch2Action.
#objc func switch2Action(switch2: UISwitch) {
if switch2.isOn {
switch1.isOn = false
switch1.isHidden = true
lockedMessage.isHidden = true
}
This is correct and desired behavior. Since you explicitly changed the value, it is up to you to decide how to handle the changed value.
The reason for this is because it is not uncommon to explicitly change the value of control after being notified of its value being changed through user interaction. If the explicit state change caused the event to fire again, you would end up in an infinite loop. "#rmaddy"
Deprecated (since the original question updated):
You (probably accidentally) set a unrelated condition for hiding the label. (What is moveWall ?).
Try this instead:
#objc func switch1Action(switch1: UISwitch) {
lockedMessage.isHidden = !switch1.isOn
,,,
}
Setting switch1.isOn programmatically will not trigger the switch1Action. You need to hide the message label from switch2Action.
For reference see the documentation:
Setting the switch to either position does not result in an action message being sent.
In my swift iOS application, I have a simple UISwitch control. I have connected the value changed outlet to my #IBAction. The code looks like this:
#IBAction func userDidSelectVisibiltySwitch(_ sender: Any) {
if self.visibilitySwitch.isOn {
if badCondition {
self.visibilitySwith.setOn(false, animated: false)
return
}
} else { // Strangely, it executes the else (I think because the compiler is evaluating the isOn condition again when it arrives to the else {}
// work to be done if the user has turned off the switch
}
}
I suspect that in this case, as I am turning the switch off before the else is evaluated, the compiler executes the else {} statement because it evaluates the above isOn expression again. But how is that possible, given that I placed a 'return' instruction ? that is really beyond me. A confirmation of my suspect comes from the fact that if I dispatch_async using GCD the 'self.visibilitySwith.setOn(false, animated: false)' statement, it works properly without executing the else {} statement, because the evaluation of the else takes place before the control is turned off by my statement. My code now looks like this, and it works:
#IBAction func userDidSelectVisibiltySwitch(_ sender: Any) {
if self.visibilitySwitch.isOn {
if badCondition {
DispatchQueue.main.async {
self.visibilitySwith.setOn(false, animated: false)
}
return
}
} else { // In this case it is normal, it does not execute the else {}
// work to be done if the user has turned off the switch
}
}
I think that I am missing something important of swift in this case. Any help is greatly appreciated. I have already provided a solution, but I want to understand the problem. Thanks a lot
Rather than accessing the UISwitch via your sender argument, you go directly to what I assume is the IBOutlet value. Instead of that approach, you can access the sender as outlined below:
#IBAction func userDidSelectVisibiltySwitch(_ sender: UISwitch) {
if sender.isOn && badCondition {
sender.setOn(false, animated: false)
} else { // In this case it is normal, it does not execute the else {}
// work to be done if the user has turned off the switch
}
}
The reason your fix is working is likely because of a slight delay introduced by the dispatch call which allows for the IBOutlet value to update its value.
I have also gone ahead and combined your if statement, as the sample you provide does not require a nested check.
UPDATED BASED ON RMADDY'S COMMENT
This being the solution struck me a bit of code smell, and upon further investigation, I was able to reproduce the scenarios described by OP. This was accomplished by setting the action in Storyboard as seen here:
With that setting, I saw the following:
Original code posted by OP would fail
Adding the DispatchQueue as demonstrated by OP would correct the switch after a brief delay
My posted solution would correctly work
Assuming that this is what the OP has done, then the first correction would be to change the event to Value Changed. Then, as stated by rmaddy in the comment, this would succeed regardless of whether you use the argument or the IBOutlet. Based on the original question, my interpretation was that there was an issue of the outlet value and the switch's state in the interface being out of sync.
I just noticed that setting a UISwitch's isOn in its IBAction causes the IBAction to be called again. So the following code:
class ViewController: UIViewController {
var count = 0
#IBOutlet weak var mySwitch: UISwitch!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
mySwitch.isOn = false
}
#IBAction func buttonTapped(_ sender: UIButton) {
mySwitch.isOn = !mySwitch.isOn
}
#IBAction func switchChanged(_ sender: UISwitch) {
print("\(count) pre: \(mySwitch.isOn)")
mySwitch.isOn = !mySwitch.isOn
print("\(count) post: \(mySwitch.isOn)")
count += 1
}
}
prints the following when the switch is turned on one time:
0 pre: true
0 post: false
1 pre: false
1 post: true
switch is turned off in viewDidLoad
switch is turned on by the user
switch is on now when switchChanged (IBAction) is called
0 pre: true is printed
switch is turned off programmatically in switchChanged
0 post: false is printed
switchChanged is called again by the system
switch is off now in switchChanged, and 1 pre: false is called
switch is turned on programmatically
1 post: true is printed
Why is the IBAction called by the system a second time? How does one get around this, say, for example, when wanting to negate the user's action based upon some internal state? I feel like I am missing something embarrassingly obvious, but I'm pretty sure similar code used to work. Is this an iOS bug? It's being run on an iOS 10.2 iPhone 5s simulator, Xcode Version 8.2.1 (8C1002)
It's interesting to note that when the button tied to buttonTapped is tapped (calling that same method), the switch's IBAction is not called.
Your IBAction is presumably hooked up to valueChanged, which doesn't indicate a particular touch event, just exactly what it says, that the value was changed.
I'd suggest setting a variable called something like var didOverrideSwitchValue = false, set it to true just before setting the new switch value, then when the function is called, check for that variable. If it's set to true, then set it to false and return.
Or, if you wish to negate the new setting only when it's turned on, then you could do if (switch.isOn), and then if so then you can respond to it by turning it off, if required.
I've been battling the same issue and found a workaround...
Check the "selected" property on the sender in your switch handler. I've found that it's true the first time through and false the second time, so you can tell if you're really being called by the user action.
I'm guessing whatever is teeing up the event to fire the second time isn't the switch itself, or maybe this property gets cleared after the first event is handled. Maybe a UIKit guru could chime in.
The UISwitch docs for -setOn:animated: say
Setting the switch to either position does not result in an action message being sent.
Seems clear enough. Feels like an OS bug.
Anyway, this seems to work but it makes me uneasy because I don't fully understand why the problem exists in the first place, nor exactly why this fixes it, and I worry that either could change in a future OS update.
UPDATE
This works fine in my little test app but not in my real app, which has a more complex UI hierarchy with a nav bar, tabs, etc. This just reinforces my uneasiness with this solution.
I have a tableView in my app. and in my app users can like a post (best example is instagram). when I like a post my button state change to active and button's color change. but when I scroll the tableView button state back to normal. I know it happen because cell reload again, but what is the best solution for this problem? am I have to overtime that I press like button app send like request and tableview reloadData? (this is not good for server)
EDIT: I have a flag for button, but when I press like button I have to send request and fetch dataArray again. but how can change button state and send like request but not fetch dataArray. it will happen when user manually refresh data.
thanks.
You can set using if and else condition. see below example
if model.data.isFav == true
{
btnFav.selected = true
}
else
{
btnFav.selected = false
}
If you have any flag for button with Posts in your dataArray from which you are loading cells data then you can use that flag to set the color of button. It will take care at the time of scroll.
Set button type to custom in the navigation inspector, then set the background image for default and selected. then use the code below:
// MARK: Set fav button Action
func setFavButtonAction(_ sender : UIButton) {
print(sender.tag)
if sender.isSelected == true {
sender.isSelected = false
}else{
sender.isSelected = true
}
}
I have an app with some buttons, when those buttons are pressed the image on them should change. I assume that the TouchUpInside runs when you tap and remove the finger while still holding inside the area of the element, however it only works rarely and I'm not sure why.
The reason I use TouchUpInside instead of TouchDown is because I want the user to be able to cancel the action.
I'm sorry if I've misunderstood anything about those events and if this has already been asked. I couldn't find an answer to my problem searching the web.
//The IBAction is set to trigger on TouchUpInside
#IBAction func action11(sender: UIButton) {
setTile(sender)
}
func setTile(sender: UIButton) {
if turn {
print("O's turn")
sender.setImage(xTile, forState: .Normal)
turn = false
}
}
EDIT: Added the necessary code
There are some properties of UIButtons which you can use to achieve what you want.
You can use Default and selected state of uibutton to set two different images.
In XIB select state "Default" and assign default image to that state again select state to "Selected" and assign image which you want after button section.
and add following line in button selection method.
-(IBAction)buttonTapped:(UIButton *)sender{
sender.selected = !sender.selected;
}
Your understanding is correct, you need to use touchUpInside.
I assume you are trying to create a button that has a toggle function. On one touch you want the button to have the value Say "X" and when touched again the button has a value "O".
Take a look at this code below, this should do the job.
class ViewController: UIViewController {
var isButtonPressed = false{
// Adding a Property Observer, that reacts to changes in button state
didSet{
if isButtonPressed{
// Set the Value to X.
}else{
// Set the Value to O.
}
}
}
#IBAction func changeButtonValue(sender: UIButton) {
// Toggle the button value.
isButtonPressed = !isButtonPressed
}
}
If you don't set turn=true after the first time, this code is executed it will be executed only one.
if turn {
print("O's turn")
sender.setImage(xTile, forState: .Normal)
turn = false
}
Check if the button frame is large enough to get finger touch.
Apple says at least 35x35 pixel.