最近做项目要实现一个列表中完成单选的需求,就是列表中显示的是乘机人信息,每次只允许选择一个人,然后进行后续操作。乘机人信息展示采用的是自定义Cell,最前面是个单选按钮,然后是姓名,证件号等信息。首先定义一个私有属性来存放当前选择的索引:
NSIndexPath* _selectedIndex;
点击整个Cell来实现单选的功能,逻辑比较简单,只需在didSelectRowAtIndexPath方法中调用单选按钮的点击事件,并记录当前Cell的indexPath。如果之前点选的是别的index,还需将之前的点选给置为未选中状态。
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
SelectCell *cell = (SelectCell *)[self.tableView cellForRowAtIndexPath:indexPath];
[cell SelectedButton:cell.selectButton];
if(_selectedIndex) {
if(_selectedIndex == indexPath) {
_selectedIndex = nil;
}else {
SelectCell *lastSelectedCell = (SelectCell *)[self.tableViewcellForRowAtIndexPath:_selectedIndex];
[lastSelectedCellSelectedButton:lastSelectedCell.selectButton];
_selectedIndex = indexPath;
}
} else{
_selectedIndex = indexPath;
}
}
可是若是点击每个Cell的单选按钮,该如何处理呢?
我考虑了以下几种解决方案:
1、将cell的单选按钮的enabled属性置为NO,如此一来,该按钮就不接受点击事件,点击它就相当于点击整个cell,走didSelectRowAtIndexPath的逻辑。不过,按钮(UIButton)的enabled属性被置为NO以后,它的背景图片变暗,这样点选的效果不好。解决变暗的问题,需要设置其Disabled的背景图片和正常状态下一样。如下:
UIImage* image=[button imageForState:UIControlStateNormal];
[button setImage: image forState:UIControlStateDisabled];
由于使用的是共通的自定义Cell,为了不影响其他页面的正常使用,故放弃该方案。
2、将按钮(UIButton)控件置换为图形控件(UIImageView),这样每次点选只需要替换UIImageView的背景图即可,因为也需要变更自定义Cell,故也放弃。
3、使用代理。将对于单选按钮的点击事件,通过代理传递回ViewController类,执行和点击整个Cell时调用的方法didSelectRowAtIndexPath一样的逻辑即可。一样不愿意对共通的自定义Cell进行修改,故放弃。
4、使用KVO的方式。每次点选单选按钮,该按钮的selected状态都会发生变化,通过对其selected属性值变化进行监听,以实现单选逻辑。需要对每个页面中显示出来的cell进行监听,并在该cell不显示时进行监听的移除。代码如下:
- (void)tableView:(UITableView*)tableView willDisplayCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[((SelectCell *)cell).selectButton addObserver:self forKeyPath:@"selected" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)tableView:(UITableView*)tableView didEndDisplayingCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath*)indexPath {
[((SelectCell *)cell).selectButton removeObserver:self forKeyPath:@"selected"];
}
在监听事件中实现单选的逻辑,代码如下:
#pragma mark - observe value for key path
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(__unused id)object
change:(NSDictionary*)change
context:(void*)context
{
if([keyPath isEqualToString:@"selected"]){
UIButton *btn = (UIButton *)object;
NSIndexPath*indexPath = [JLTools indexPathFromTag:btn.tag];
if (_selectedIndex) {
if (_selectedIndex ==indexPath) {
_selectedIndex = nil;
} else {
SelectCell * lastSelectedCell = (SelectCell *)[self.tableView cellForRowAtIndexPath:_selectedIndex];
[lastSelectedCell SelectedButton:lastSelectedCell.selectButton];
_selectedIndex = indexPath;
}
} else {
_selectedIndex =indexPath;
}
}
}
写完测试,功能OK。不过点击页面后退按钮时却发现程序崩溃了?!原因是没有对页面中显示的Cell的监听事件进行移除。所以还需要下dealloc方法中进行移除监听事件。代码如下:
- (void)dealloc {
NSArray*cells = [self.tableView visibleCells];
for (int i= 0; i < cells.count; i++) {
SelectCell *cell= (SelectCell *)cells[i];
[cell.selectButton removeObserver:self forKeyPath:@"selected"];
}
}
貌似使用了比较复杂的方式解决了一个简单的问题,不过还是很开心自己学会了如何对Cell中的内容使用KVO的方式进行监听。