UISwitch in a UITableView cell

62,340

Solution 1

Setting it as the accessoryView is usually the way to go. You can set it up in tableView:cellForRowAtIndexPath: You may want to use target/action to do something when the switch is flipped. Like so:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    switch( [indexPath row] ) {
        case MY_SWITCH_CELL: {
            UITableViewCell *aCell = [tableView dequeueReusableCellWithIdentifier:@"SwitchCell"];
            if( aCell == nil ) {
                aCell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"SwitchCell"] autorelease];
                aCell.textLabel.text = @"I Have A Switch";
                aCell.selectionStyle = UITableViewCellSelectionStyleNone;
                UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
                aCell.accessoryView = switchView;
                [switchView setOn:NO animated:NO];
                [switchView addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
                [switchView release];
            }
            return aCell;
        }
        break;
    }
    return nil;
}

- (void)switchChanged:(id)sender {
    UISwitch *switchControl = sender;
    NSLog( @"The switch is %@", switchControl.on ? @"ON" : @"OFF" );
}

Solution 2

You can add a UISwitch or any other control to the cell's accessoryView. That way it will appear on the right-hand side of the cell which is probably what you want.

Solution 3

if (indexPath.row == 0) {//If you want UISwitch on particular row
    UISwitch *theSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
    [cell addSubview:theSwitch];
    cell.accessoryView = theSwitch;
}

Solution 4

You could prepare the cell in Interfacebuilder, link it to an IBOutlet of your Viewcontroller and return it when the tableview is asking for the proper row.

Instead, you could create a separate xib for the cell (again with IB) and load it using UINib upon the cells creation.

Finally, you could create the switch programmatically and add it to your cells contentview or accessoryview.

Which one suits you best largely depends on what you like to do. If your tableviews content is fixed (for a settings page etc.) the first two might work well, if the content is dynamic I'd prefer the programmatic solution. Please be more specific in what you would like to do, this would make answering your question easier.

Solution 5

This is a more complete solution where turning off and on happens on the view layer (UITableViewCell) and it forwards the events to the tableView delegate through didSelect and didDeselect:

class CustomCell: UITableViewCell {
    private lazy var switchControl: UISwitch = {
        let s = UISwitch()
        s.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged)
        return s
    }()

    override func awakeFromNib() {
        self.accessoryView = switchControl
        self.selectionStyle = .none // to show the selection style only on the UISwitch
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        (self.accessoryView as? UISwitch)?.isOn = selected
    }

    @objc private func switchValueDidChange(_ sender: UISwitch) { // needed to treat switch changes as if the cell was selected/unselected
        guard let tv = self.superview as? UITableView, let ip = tv.indexPath(for: self) else {
            fatalError("Unable to cast self.superview as UITableView or get indexPath")
        }
        setSelected(sender.isOn, animated: true)
        if sender.isOn {
            tv.delegate?.tableView?(tv, didSelectRowAt: ip)
        } else {
            tv.delegate?.tableView?(tv, didDeselectRowAt: ip)
        }
    }
}

And on your delegate


func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
    return false // to disable interaction since it happens on the switch
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // to make sure it is rendered correctly when dequeuing:
    // stuff
    if isSelected { // stored value to know if the switch is on or off
        tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
    } else {
        tableView.deselectRow(at: indexPath, animated: true)
    }
    // more stuff
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // do your thing when selecting
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    // do your thing when deselecting
}
Share:
62,340

Related videos on Youtube

testing
Author by

testing

Updated on July 05, 2022

Comments

  • testing
    testing almost 2 years

    How can I embed a UISwitch on a UITableView cell? Examples can be seen in the settings menu.

    My current solution:

    UISwitch *mySwitch = [[[UISwitch alloc] init] autorelease];
    cell.accessoryView = mySwitch;
    
    • MobileMon
      MobileMon over 10 years
      What's wrong with the current way you are doing it?
  • testing
    testing over 13 years
    Why do you use initWithFrame? Why do you use addSubview? switch can't be used as a variable name.
  • testing
    testing over 13 years
    I'd prefer the programmatic solution (despite it's for a settings page), but I'm also interested how the first two options work. Perhaps you could explain them a little bit in more detail.
  • testing
    testing over 13 years
    Instead of MY_SWITCH_CELL should be the corresponding cell number I think. Nice all around solution!
  • k-thorat
    k-thorat over 13 years
    Sorry for the switch name. I had some code.. I just changed variable name in it.
  • Jesse
    Jesse almost 12 years
    how do you write aCell.accessoryView = switchView; with bracket notation?
  • zpasternack
    zpasternack almost 12 years
    @Jesse 'aCell.accessoryView = switchView;' is exactly equivalent to '[aCell setAccessoryView:switchView];'. Do you have some reason to avoid the dot-notation?
  • Jesse
    Jesse almost 12 years
    @Z - Thank you! I don't know why it was giving me such a hard time. I don't have any real reason other than some mild OCD. When I'm coding in objective-c I like to stick to bracket-notation as opposed to switching back and forth. I try to save the dot-notation for when I'm doing web development.
  • Nitin Alabur
    Nitin Alabur almost 12 years
    Thanks so much for this answer! Adding the switch as a subview messes up the voice over commands. Setting it as accessory view works perfect with voiceover!
  • Kenan Karakecili
    Kenan Karakecili almost 10 years
    It worked for me. Effective solution with less code.
  • doxsi
    doxsi over 9 years
    How know the index of the switch selected?
  • johnnieb
    johnnieb over 8 years
    I was able to get to this work by only setting the cell’s accessoryView property. I don’t think adding the switch as a subview is necessary.
  • Nazmul Hasan
    Nazmul Hasan about 7 years
    @doxsi switchView.tag = indexPath.row for detect which row switch Changed for swift
  • Dani
    Dani over 5 years
    Won't initializing the switch in a local scope kill it and nullify the added target function call?
  • Adam Ware
    Adam Ware almost 3 years
    This code works noting that you cant use the variable name "switch" because that's reserved for a Switch statement. So using anything else would be fine "aSwitch" etc.