34

I have a simple problem. I created a custom UITableViewCell that includes a UISwitch in Interface Builder.

Question 1: Easier, better way to do it ? Question 2: When used in an UITableViewController, what would be the best way to get the data ? At some point I either need to go through the table and check every cell about the status OR send some feedback when the status of the switches changes directly ?

Thanks for the help.

eemceebee
  • 2,656
  • 9
  • 39
  • 49

5 Answers5

86

You could just add the UISwitch in your accessory view. That is the easiest way to do it, not to mention that it looks 'elegant'.

Then, in your tableview controller code, you could just call a selector every time the switch is toggled, or even toggle the switch itself by getting the switch's current status in your controller.

Let know if you'd like a code sample.

---sample code---

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    //add a switch
    UISwitch *switchview = [[UISwitch alloc] initWithFrame:CGRectZero];
    cell.accessoryView = switchview;
    [switchview release];
}

cell.textLabel.text = [NSString stringWithFormat:@"%d", indexPath.row];

return cell;
}

Then, you could have a method that updates a switch based on changes in your model. You could use anything you want - delegates, notifications, KVO, etc.

Example:

- (void)updateSwitchAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UISwitch *switchView = (UISwitch *)cell.accessoryView;

if ([switchView isOn]) {
    [switchView setOn:NO animated:YES];
} else {
    [switchView setOn:YES animated:YES];
}

 }
Saurabh G
  • 1,895
  • 14
  • 15
  • I am wondering if you would have example regarding KVO used for UISwitch. I try to implement like `UISwitch *switchview = [[UISwitch alloc] initWithFrame:CGRectZero]; cell.accessoryView = switchview; [switchview addObserver:self forKeyPath:@"on" options:NSKeyValueChangeSetting context:NULL];` but my function `- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ NSLog(@"observeValueForKeyPath"); } ` did not get called when Switch value changed – user454083 Feb 20 '12 at 10:24
  • if you would have example code to get the IndexPath in the call function? I also tried to implemented like **[(UISwitch*)cell.accessoryView addTarget:self action:@selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventValueChanged];** But I got an **2012-02-20 11:39:32.962 phoneApp[1935:f803] -[UISwitch row]: unrecognized selector sent to instance 0x6b56060 2012-02-20 11:39:32.963 phoneApp[1935:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UISwitch row]: unrecognized selector sent to instance 0x6b56060'** – user454083 Feb 20 '12 at 10:42
  • In this case I made call back function like - (void)updateSwitchAtIndexPath:(NSIndexPath *)indexPath – user454083 Feb 20 '12 at 10:44
  • 2
    Just a word of caution about the example code. Unless every cell in your table is intended to have a switch, your code will not work properly when cells are reused (since the cell identifier is always the same @"Cell"). If your UITableView has other types of cells (without UISwitch) then dequeReusableCellWithIdentifier could return a cell without a UISwitch, in which case the cell will never get a UISwitch because you only add one when creating a new cell (i.e. when cell == null). For indexpaths that require UISwitch you should use a unique cellIdentifier like @"SwitchCell". – kb1ooo Apr 04 '14 at 15:00
  • Wrong! ```tableView:cellForRowAtIndexPath:``` is called when table view creates/reuses cells. So this method may return both existing and new object (which is not used of course). And it doesn't support a table which has 2 or more various cell types – Vyachaslav Gerchicov Mar 29 '17 at 09:42
8
Switch.tag = indexPath.row;
[Switch addTarget:self action:@selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventTouchUpInside];



- (void)updateSwitchAtIndexPath:(UISwitch *)aswitch{
    FFLog(@"%i",aswitch.tag);
}
PaulStock
  • 11,053
  • 9
  • 49
  • 52
wang.qingyi
  • 81
  • 1
  • 1
4

Thanks. I don't have your indexPath, but you can identify a cell or object with a tag like This:

if (indexPath.row == 0) {
        cell.textLabel.text = @"Twitter";


        UISwitch *switchview = [[UISwitch alloc] initWithFrame:CGRectZero];
        switchview.tag = 111;
        //switchview.selected = 0;
        cell.accessoryView = switchview;

        [switchview addTarget:self action:@selector(updateSwitchAtIndexPath) forControlEvents:UIControlEventTouchUpInside];

        //cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;
        //[switchview release];

    }


- (void)updateSwitchAtIndexPath {

for (UITableViewCell * cell in tblView.subviews) {

   for (UISwitch *swch in cell.subviews) {


         UISwitch *switchView = (UISwitch *)cell.accessoryView;

        if (switchView.tag == 111) {

            if ([switchView isOn]) {
                 NSLog(@")>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ON");
            }else{
                NSLog(@")>>>>>>>>>>>>>>>>>>>>>>>>>>>>> OFF");
            }

        } 

          if (switchView.tag == 222) {
            if ([switchView isOn]) {
                NSLog(@")>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ON");
            }else{
                NSLog(@")>>>>>>>>>>>>>>>>>>>>>>>>>>>>> OFF");
            }

        }
    }
}

}

Rembrandt Q. Einstein
  • 1,101
  • 10
  • 23
kalpesh jetani
  • 1,775
  • 19
  • 33
  • 1
    while Configuring UITableViewCell we Can Do like ..this With Tag ** `[switchview addTarget:self action:@selector(updateSwitchAtIndexPath) forControlEvents:UIControlEventTouchUpInside];`** – kalpesh jetani Sep 05 '11 at 16:52
3

Two options:

1) initialize your cell with a pointer to your data model. Have the cell implement the delegate callback for the switch. When the switch changes, use the pointer to your data model to reflect the new setting. Alternatively, initialize the cell with a pointer to your view-controller. Have the cell implement the switch delegate method, then call back into your view controller with the appropriate cell context info.

2) have your view-controller implement the switch delegate callback. When the switch changes, determine which switch/cell it was (may have to set the switch's "tag" property to be a row number, or something) and handle the update in the context of your view controller.

TomSwift
  • 39,369
  • 12
  • 121
  • 149
1

To get the value of the UISwitch in Swift 3 and Swift 4 you can add a target to it:

func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {

    ...

    cell.uiSwitch.tag = 100
    cell.uiSwitch.addTarget(self, action: #selector(ViewController.switchAtValueChanged(switchFromTableViewCell:)), for: UIControlEvents.valueChanged)

    ...
}

And you can get the value like this:

@objc func switchAtValueChanged(uiSwitch: UISwitch) {

    if uiSwitch.tag == 100 {
        let value = uiSwitch.isOn
    }
} 
pableiros
  • 14,932
  • 12
  • 99
  • 105