Handling multiple UISwitches with a single IBAction method - ios

I have the following IBAction that is linked to several switch in my application. I would like to figure out which switch is clicked. Each UISwitch has a specific name. I want that name.
- (IBAction)valueChanged:(UISwitch *)theSwitch { //Get name of switch and do something... }

You can either use tags:
When you create the switches you need to set their tags.
- (IBAction)valueChanged:(UISwitch *)theSwitch {
switch(theSwitch.tag){
case 0:
{
//things to be done when the switch with tag 0 changes value
}
break;
case 1:
{
//things to be done when the switch with tag 0 changes value
}
break;
// ...
default:
break;
}
}
Or check if the switch is one of your controller properties
- (IBAction)valueChanged:(UISwitch *)theSwitch {
if(theSwitch == self.switch1){
//things to be done when the switch1 changes value
} else if (theSwitch == self.switch2) {
//things to be done when the switch2 changes value
}// test all the cases you have
}

The IBAction passes a pointer to the switch that performed the action. You can get any property off of it.
To compare switches:
- (void)valueChanged:(UISwitch *)theSwitch {
if ([theSwitch isEqual:self.switch1]) {
NSLog(#"The first switch was toggled!");
}
else if ([theSwitch isEqual:self.switch2]) {
NSLog(#"The second switch was toggled!");
}
else {
NSLog(#"Some other switch was toggled!");
}
}

I don't thank you can get the name of that switch. You could tag each of the switches, and use that tag to determine the name of the switch.

UISwitch doesn't have a name property. But you can subclass it and add a name property to the subclass. Then create switches from the subclass instead of UISwitch and give them a name when you init them.
#class MySwitch : UISwitch
#property (nonatomic, retain) NSString* name;
#end
Then the event handler can access them name string:
- (IBAction)valueChanged:(MySwitch *)theSwitch {
NSLog(#"switch %# value changed", theSwitch.name);
}
But I think the better answer is to use the tag field already there and use integer tags to identify the switches rather than a string. You can create enumeration constants in your code to name the tag values:
enum { SomeSwitch = 1, AnotherSwitch = 2, MainSwitch = 3 } _SwitchTags;
The best answer is the one #Moxy mentioned to compare the switch's pointer to your controller's properties to figure out which switch changed. That's what I do in my code. Tags and names are too error prone in the long run.

Related

how to identify UITextField in a better way

I am using xcode8+swift3.
I have multiple UITextField in my controller view. Each UITextField has a outlet connection in code.
I know I can use “tag” to identify UITextField, but it seems I can only use number as tag (I tried with string value for tag field, my Xcode always get stuck, only number as tag works).
But I don’t want to use magic number in my code like:
If (textField.tag == 0) {
}
I am wondering, is there a better way or more descriptive way in code to identify UITextField?
Tag is the correct tool. Just create an enum for them to track.
enum FieldIdentifier: Int {
case name = 0
case age = 1
}
if let fieldIdentifier = FieldIdentifier(rawValue: textField.tag) {
switch fieldIdentifier {
case .name: ...
case .age: ...
}
}
(Note that Larme's comment about using == is also appropriate, and if you already have outlets is better.)

UISwitch won't let me set default state

I have a custom UICell that contains a UISwitch element. When I render my table, cellForRowAtIndexPath() will check to see if the users preference matches a specific category, if it does they are subscribed, else they are not. I have written some logic to toggle the switch based on this result:
if let userPreferences = user["preferences"] as? Array<AnyObject>
{
if userPreferences[indexPath.row].objectId == game.getGameId() {
prefCell.subscribed?.setOn(false, animated: true)
}
}
I have breakpointed the line where the switch is disabled, and it breaks 9 times out of the current 11 items I retrieve in the userPreferences collection, however none of the switches on my UI are updated. Why is this happening?
EDIT: After some further testing it appears that the if statement is always satisfied, this is extremely strange as in the event that the strings don't match, Swift still claims that they did.
For example if I have a list of the ID's
xyz12345
abc12345
efg12345
And the following loop executed:
if let userPreferences = user["preferences"] as? Array<AnyObject>
{
for preference in userPreferences
{
if preference.objectId == game.getGameId()
{
prefCell.subscribed.on = true;
} else {
prefCell.subscribed.on = false;
}
}
}
The first comparison could be something like preference = xyz12345 and game.getGameId() = abc12345. I'd expect the second case to be invoked as these strings certainly do not match, however the case passes as true and the switch is set, why am I witnessing this behaviour?

Objective-C Class Swapping at runtime

At the moment we have a class structure like...
GenericFruitViewController
- AppleViewController
- PearViewController
- StrawberryViewController
Where the specific view controllers are subclasses and only change a small amount of implementation.
What I'd like to do is be able to swap out the GenericViewController at runtime. I want to change the way the generic view controller works and change some of the methods (this won't affect the subclassed overridden methods).
But I'd like to be able to switch this on/off (A/B testing).
At the moment we have a factory method that does something like...
- (GenericFruitViewController *)fruitControllerWithType:(FruitType)type
{
if (type == Apple) {
return [AppleViewController new];
}
return [GenericFruitViewController new];
}
What I'd like to do (ideally) is something like...
- (GenericFruitViewController *)fruitControllerWithType:(FruitType)type
{
// this is the new bit!
if (switchOnTheTesting) {
// swap GenericFruitViewController for my NewFruitViewController
}
// new bit ends
// existing code not changed
if (type == Apple) {
// this now returns a subclass of NewFruitVC if switched
return [AppleViewController new];
}
// this now returns NewFruitVC if switched
return [GenericFruitViewController new];
}
And by doing this it will then use my new VC whenever it refers to the GenericFruitVC.
Is that even possible?

Swift: UIButton textLabel.text value not useable in switch statement

I am new to Swift and iOS development, so I am trying to build a calculator app for learning purposes. However, I am encountering an error. I have titled all of my buttons with the number they represent, so I am retrieving the title in the buttonPress IBAction via sender.titleLabel.text. Then, I pass that into a switch statement to determine if the button was a number or an operator.
func handleButton (sender:UIButton) {
switch sender.titleLabel.text {
case "1","2","3","4","5","6","7","8","9","0" :
println(sender.titleLabel.text)
default:
break
}
}
The error is that sender.titleLabel.text will not bind to the string values I have entered - nor any string values - even though it is is of type String.
There seems to be a bug in the complier at the moment where Implicitly Unwrapped Optionals cannot be used in switch statements. Instead you can use optional binding to satisfy the compiler. As an extra plus, this will handle the case where titleLabel.text is nil.
func handleButton (sender:UIButton) {
if let text = sender.titleLabel.text {
switch text {
case "1","2","3","4","5","6","7","8","9","0" :
println(sender.titleLabel.text)
default:
break
}
}
else {
// sender.titleLabel.text is nil
}
}

Pass different parameters to an IBAction

My iPhone app has many buttons and I want all the buttons to call the same method, but with different parameters.
For example I want tapping one button to call the method myMethod: with the argument #"foo", and a second button should call the same method but with argument #"bar".
The so called "IBActions" must have one of these signatures:
-(void)action;
-(void)actionWithSender:(id)sender;
-(void)actionWithSender:(id)sender event:(UIEvent*)event;
You cannot add any other parameters. Nevertheless you can use sender (which is button1 or button2 in your case) to get the parameter:
-(void)actionWithSender:(UIButton*)sender {
NSString* parameter;
if (sender.tag == 1) // button1
parameter = #"foo";
else // button2
parameter = #"bar";
...
}
the real reason You cannot add additional parameter is that UIKIT will push params on the stack.
so the only way is to use tags.
A DIRTY way can be to convert a pointer to int and tagging the button with it:
myStruct params;
// fill params:
params.x=....
params.y=....
params.z=....
UIButton * btn = [UIButton......]; // create or use one from XIB
btn.tag = (int)&params;
... in Call back:
-(IBActions) doIt:(id)sender
{
myStruct * paramsPtr = (myStruct*)tag;
int i = paramsPtr->x;
NOTE: params MUST be keep static .. or allocate using malloc (more and more ugly code...).
DO NOT use a local var: it will be allocated on stack so will be removed after exiting from the setup method.
Give your various UIButton instances different tag property values.
In your IBAction method -myMethod:, you might then do something like:
- (void) myMethod:(id)sender {
switch (sender.tag) {
case (firstButtonTag):
doFooStuff;
break;
case (secondButtonTag):
doBarStuff;
break;
// etc.
}
}
The values firstButtonTag and secondButtonTag can be stored in an enum if you want to make this easy to maintain.
You can't pass parameters through an IBAction. What I usually do is give the buttons the unique tag in IB. THe tag is an integer value so I u then use a simple lookup table to convert the tag to some value.
In this case, three buttons but tags 1 to 3:
- (IBAction) buttonPressed: (UIButton*) sender
{
static const NSString* names = { #"Foo", #"Bar", #"Baz" };
id tag = [sender tag];
if (tag >= 1 && tag <= 3) {
NSLog(#"Button pressed is %#", names[tag]);
}
}
(id)Sender is shows that whatever u pass on UIButton click event is directly pass to this method and no matter that what type it is , it take automatically like if you pass button tag then it take button tag as sender.tag etc
As others have mentioned you cannot pass your custom parameter into action method. If you do not like the solution using tags you may also subclass UIButton with your custom class and add your parameter there. (By I wouldn't bother and just use tags)
You don't. The only parameter is the sender object, which you may use to have a different behavior, but what I'd do is define 2 action methods, which simply in turn call the same method with a different parameter, i.e. you'd have:
- (IBAction)button1:(id)sender
{
[self doStuff:kButton1];
}
- (IBAction)button2:(id)sender
{
[self doStuff:kButton2];
}
- (void)doStuff:(ParamType)param;
{
...
}
In defense of that method (no pun intended), I'd add that it makes clearer when you review your UI with Interface Builder to see that different buttons actually have different effects, which is harder to tell if they all call whateverAction:

Resources