开发者

Handling multiple UISwitch controls in a table view without using tag property

开发者 https://www.devze.com 2022-12-25 14:19 出处:网络
I have a table view controller with multiple UISwitch controls in them. I set the delegate to the table view controller with the same action for all switches. I need to be able to determine wh开发者_如

I have a table view controller with multiple UISwitch controls in them. I set the delegate to the table view controller with the same action for all switches. I need to be able to determine wh开发者_如何学Pythonat switch was changed, so I create an array of strings that contains the name of each switch. The indexes in the array will be put in the tag property of each UISwitch.

However, I'm ready using the tag property for something else, namely to find the right control in the cell in cellForRowAtIndexPath with viewWithTag! (There are several things I need to set within each cell.)

So, am I thinking along the right lines here? I feel I'm rather limited in how I find out exactly which UISwitch changed its value, so I can do something useful with it.


I fixed this by subclassing UISwitch like so:

@interface NamedUISwitch : UISwitch {
NSString *name;

}

It seems elegant (no index arrays required) and the tag property is free to do whatever it wants.

I read that you have to be careful with subclassing in Objective-C, though...


I have written a UISwitch subclass with a block based hander for value change control events which can help when trying to track which switch's value has changed. Ideally, we could do something similar with composition rather than subclassing, but this works well for my needs.

https://gist.github.com/3958325

You can use it like this:

ZUISwitch *mySwitch = [ZUISwitch alloc] init];

[mySwitch onValueChange:^(UISwitch *uiSwitch) {
        if (uiSwitch.on) {
            // do something
        } else {
            // do something else
        }
    }];

You can also use it from a XIB file, by dragging a switch onto your view, and then changing its class to ZUISwitch


You are close in your approach. What I have done in similar situations is create separate UITableViewCell subclasses, set the tag of the UISwitch to be the index.row of the index path, and only use that UITableViewCell subclass in a specific section of the table view. This allows you to use the tag of the cell to uniquely determine what cell has the event without maintaining a separate index list (as it sounds like you are doing).

Because the cell type is unique, you can than easily access the other elements of the cell by creating methods/properties on the UITableViewCell Subclass.

For example:

@interface TableViewToggleCell : UITableViewCell {
    IBOutlet UILabel *toggleNameLabel;
    IBOutlet UILabel *detailedTextLabel;
    IBOutlet UISwitch *toggle;
    NSNumber *value;
    id owner;
}

@property (nonatomic, retain) UILabel *toggleNameLabel;
@property (nonatomic, retain) UILabel *detailedTextLabel;
@property (nonatomic, retain) UISwitch *toggle;
@property (nonatomic, retain) id owner;

-(void) setLable:(NSString*)aString;
-(void) setValue:(NSNumber*)aNum;
-(NSNumber*)value;
-(void) setTagOnToggle:(NSInteger)aTag;

-(IBAction)toggleValue:(id)sender;

@end

In:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // ... prior iniitalization code for creating cell is assumed
toggleCell.owner = self;
[toggleCell setLable:@"some string value"];
[toggleCell setTagOnToggle:indexPath.row];
toggleCell.owner = self;
return toggleCell;
   //...  handle cell set up for other cell types as needed
}

Owner is the delegate for the cell and can then be used to initiate actions in your controller. Make sure you connect your UISwitch to the toggleValue Action, so that you can initiate actions in the delegate when the UISwitch changes state:

-(IBAction)toggleValue:(id)sender;
{
    BOOL oldValue = [value boolValue];
    [value release];
    value = [[NSNumber numberWithBool:!oldValue] retain];
    [owner performSelector:@selector(someAction:) withObject:toggle];
}

By passing the UISwitch with the method call, you can then access the index path for the cell. You could also bypass the use of the tag property by explicitly having an ivar to store the NSIndexPath of the cell and then passing the whole cell with the method call.


I realize I'm about three years late to the party but I've developed a solution without subclassing that I think is preferable (and simpler). I'm working with the exact same scenario as Thaurin's described scenario.

- (void)toggleSwitch:(id) sender
{
    // declare the switch by its type based on the sender element
    UISwitch *switchIsPressed = (UISwitch *)sender;
    // get the indexPath of the cell containing the switch
    NSIndexPath *indexPath = [self indexPathForCellContainingView:switchIsPressed];
    // look up the value of the item that is referenced by the switch - this
    // is from my datasource for the table view
    NSString *elementId = [dataSourceArray objectAtIndex:indexPath.row];
}

Then you want to declare the method shown above, indexPathForCellContainingView. This is a seemingly needless method because it would appear at first glance that all you have to do is identify the switch's superview but there is a difference between the superviews of ios7 and earlier versions, so this handles all:

- (NSIndexPath *)indexPathForCellContainingView:(UIView *)view {
    while (view != nil) {
        if ([view isKindOfClass:[UITableViewCell class]]) {
            return [self.myTableView indexPathForCell:(UITableViewCell *)view];
        } else {
            view = [view superview];
        }
    }   
    return nil;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消