最终效果图:
Girl.h
// // Girl.h // 12_tableView的增删改 // // Created by beyond on 14-7-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import <Foundation/Foundation.h> @interface Girl : NSObject // UI控件用weak,字符串用copy,其他对象用strong // 头像图片名 @property(nonatomic,copy)NSString *headImgName; // 姓名 @property(nonatomic,copy)NSString *name; // 判词 @property(nonatomic,copy)NSString *verdict; // 提供一个类方法,即构造函数,返回封装好数据的对象(返回id亦可) + (Girl *)girlNamed:(NSString *)name headImgName:(NSString*)headImgName verdict:(NSString *)verdict; // 类方法,字典 转 对象 类似javaBean一次性填充 + (Girl *)girlWithDict:(NSDictionary *)dict; // 对象方法,设置对象的属性后,返回对象 - (Girl *)initWithDict:(NSDictionary *)dict; @end
Girl.m
// // Girl.m // 12_tableView的增删改 // // Created by beyond on 14-7-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "Girl.h" @implementation Girl // 提供一个类方法,即构造函数,返回封装好数据的对象(返回id亦可) +(Girl *)girlNamed:(NSString *)name headImgName:(NSString *)headImgName verdict:(NSString *)verdict { Girl *girl = [[Girl alloc]init]; girl.name = name; girl.headImgName = headImgName; girl.verdict = verdict; return girl; } // 类方法,字典 转 对象 类似javaBean一次性填充 + (Girl *)girlWithDict:(NSDictionary *)dict { // 只是调用对象的initWithDict方法,之所以用self是为了对子类进行兼容 return [[self alloc]initWithDict:dict]; } // 对象方法,设置对象的属性后,返回对象 - (Girl *)initWithDict:(NSDictionary *)dict { // 先调用父类NSObject的init方法 if (self = [super init]) { // 设置对象自己的属性 self.name = dict[@"name"] ; self.headImgName = dict[@"headImg"] ; self.verdict = dict[@"verdict"]; } // 返回填充好的对象 return self; } @end
BeyondViewController.h
// // BeyondViewController.h // 12_tableView的增删改 // // Created by beyond on 14-7-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import <UIKit/UIKit.h> @interface BeyondViewController : UIViewController // 标题 @property (weak, nonatomic) IBOutlet UILabel *titleStatus; // tableView @property (weak, nonatomic) IBOutlet UITableView *tableView; // 清空按钮 @property (weak, nonatomic) IBOutlet UIBarButtonItem *trashBtn; // 全选 反选按钮 @property (weak, nonatomic) IBOutlet UIBarButtonItem *checkAllBtn; // 清空 - (IBAction)trashBtnClick:(UIBarButtonItem *)sender; // 全选 or 反选 - (IBAction)checkAll:(UIBarButtonItem *)sender; @end
BeyondViewController.m
// // BeyondViewController.m // 12_tableView的增删改 // // Created by beyond on 14-7-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "BeyondViewController.h" #import "Girl.h" @interface BeyondViewController ()<UITableViewDataSource,UITableViewDelegate> { // 从plist文件中加载的所有girls,返回字典数组 NSArray *_arrayWithDict; // 所有的对象数组 NSMutableArray *_girls; // 被勾选的行的对应该的模型数组 // 不用数组也行,只要在模型中增加一个属性:记录是否被选中 NSMutableArray *_checkedGirls; } @end @implementation BeyondViewController - (void)viewDidLoad { [super viewDidLoad]; // 所有的对象数组 _girls = [NSMutableArray array]; // 被勾选的行的数组 _checkedGirls = [NSMutableArray array]; // 调用自定义方法,加载plist文件 [self loadPlist]; } // 自定义方法,加载plist文件 - (void)loadPlist { // sg_bundle模板代码,1,获得.app主要的包;2,返回主要的包中某个文件的fullPath全路径 NSBundle *mainBundle = [NSBundle mainBundle]; NSString *fullPath = [mainBundle pathForResource:@"girls.plist" ofType:nil]; // 从plist文件中根据全路径,返回字典数组 _arrayWithDict = [NSArray arrayWithContentsOfFile:fullPath]; // 再调用自定义方法,将字典数组,转换成对象数组 [self dictArrayToModel]; } // 自定义方法,将字典数组,转换成对象数组 - (void)dictArrayToModel { // 字典数组 _arrayWithDict // 方式1:for in,这种情况下,控制器知道的东西太多了,如果模型增加属性,还要改控制器中的代码 /* for (NSDictionary *dict in _arrayWithDict) { Girl *girl = [Girl girlNamed:dict[@"name"] headImgName:dict[@"headImg"] verdict:dict[@"verdict"]]; [_girls addObject:girl]; } */ // 方式2:类方法返回对象,参数只要一个字典数组即可 for (NSDictionary *dict in _arrayWithDict) { // 参数只要字典,这样一来,控制器就不用知道太多东西了 // Girl *girl = [[Girl alloc]initWithDict:dict]; Girl *girl = [Girl girlWithDict:dict]; [_girls addObject:girl]; } } // 数据源方法,默认是单组,共有多少行 (每次刷新数据都会调用此行) - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // 返回数组中对应的字典的长度 return _girls.count; } // 数据源方法,每一组的每一行应该显示怎么的界面(含封装的数据),重点!!!必须实现否则,Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:' - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"Beyond"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; if (cell == nil) { // 如果池中没取到,则重新生成一个cell /* cell的4种样式: 1,default 左图右文字 2,subtitle 左图 上文字大 下文字小 3,value 1 左图 左文字大 右文字小 3,value 2 恶心 左文字小 右文字大 */ cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; } // 设置cell中独一无二的内容 Girl *girl = _girls[indexPath.row]; cell.textLabel.text = girl.name; cell.imageView.image = [UIImage imageNamed:girl.headImgName]; cell.detailTextLabel.text = girl.verdict; // 判断,如果模型存在于checkedArray中,则标记为checked if ([_checkedGirls containsObject:girl]) { cell.accessoryType = UITableViewCellAccessoryCheckmark; } else { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } // 返回cell return cell; } // 代理方法,每一行的高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 83; } // 代理方法,点击行,新版本 MVC - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 取消tableView点击后背景高亮的蓝色 [tableView deselectRowAtIndexPath:indexPath animated:YES]; // 获得被点击的行的对应的数据模型 Girl *girl = [_girls objectAtIndex:indexPath.row]; // 判断,若没被勾选过,则勾选,否则取消勾选 // 方式2:只修改模型,不动cell,让tableView reload数据即可,符合MVC~ if ([_checkedGirls containsObject:girl]) { // 取消勾选,从勾选数组中移除,然后再次reloadData [_checkedGirls removeObject:girl]; } else { // 加入到选中数组中,然后再次reloadData! [_checkedGirls addObject:girl]; } // 再次reloadData [_tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; // 最后调用自定义方法,检查trash按钮的可用性,以及标题的变化 [self statusCheck]; } // 代理方法,点击行----旧版本 - (void)oldVersionTableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 取消tableView点击后背景高亮的蓝色 [tableView deselectRowAtIndexPath:indexPath animated:YES]; // 获得被点击的行 UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; // 获得被点击的行的对应的数据模型 Girl *girl = [_girls objectAtIndex:indexPath.row]; // 判断,若没被勾选过,则勾选,否则取消勾选 // 方式1:手动设置cell的样式,但是,这不符合MVC思想~ /* if (cell.accessoryType != UITableViewCellAccessoryCheckmark) { // 勾选上,同时要加入到数组中,记住! cell.accessoryType = UITableViewCellAccessoryCheckmark; [_checkedGirls addObject:girl]; } else { // 取消勾选,同时要移除 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; [_checkedGirls removeObject:girl]; } */ // 方式2:只修改模型,不动cell,让tableView reload数据即可,符合MVC~ if (cell.accessoryType != UITableViewCellAccessoryCheckmark) { // 加入到选中数组中,然后再次reloadData! [_checkedGirls addObject:girl]; [_tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else { // 取消勾选,从勾选数组中移除,然后再次reloadData [_checkedGirls removeObject:girl]; [_tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } // 最后调用自定义方法,检查trash按钮的可用性,以及标题的变化 [self statusCheck]; } // 当点击了toolBar中的trash 按钮时调用 - (IBAction)trashBtnClick:(UIBarButtonItem *)sender { // 可变数组,成员是所有的勾选的行组成的indexPath NSMutableArray *indexPaths = [NSMutableArray array]; // 遍历checkedGirls,得到勾选的行号们,并封装成一个个indexPath,然后添加到indexPaths数组,目的是后面tableView删除行方法中用到 for (Girl *girl in _checkedGirls) { // 勾选的行的行号 int row = [_girls indexOfObject:girl]; // 封装成IndexPath NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0]; // 添加到IndexPaths数组 [indexPaths addObject:indexPath]; } // 先修改模型(从所有的对象数组中删除 勾选的对象们,并且清空勾选的对象数组),最后再deleteRowsAtIndexPaths(注意reload前提是数据源个数不能增加或减少) [_girls removeObjectsInArray:_checkedGirls]; [_checkedGirls removeAllObjects]; // deleteRows删除行之后,剩余的行数,必须与数据源的行数相等,意思就是:数据源中也要删除同样多的行的数据,才可以调用deleteRows方法 [_tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationRight]; // 最后调用自定义方法,检查trash按钮的可用性,以及标题的变化 [self statusCheck]; } // 最后调用自定义方法,检查trash按钮的可用性,以及标题的变化 - (void)statusCheck { // 如果表格没有数据了,则直接禁用掉全选按钮 if (_girls.count == 0) { _checkAllBtn.enabled = NO; } // 设置显示checked的行数 if (_checkedGirls.count != 0) { // 如果没有被选中的行,则禁用 删除按钮 _trashBtn.enabled = YES; // 显示数字(默认bar button item中的文本是不可更改的,所以改成label标签) NSString *titleStatus = [NSString stringWithFormat:@"红楼梦(%d)",_checkedGirls.count]; _titleStatus.text = titleStatus; } else { _trashBtn.enabled = NO; _titleStatus.text = @"红楼梦"; } } // toolBar最右边的 全选 or 反选按钮 - (IBAction)checkAll:(UIBarButtonItem *)sender { if (_girls.count == _checkedGirls.count) { // 取消全选 先修改模型,再reload [_checkedGirls removeAllObjects]; [_tableView reloadData]; } else { // 全选 先修改模型,再reload // 必须先清空checked数组,再全部添加 [_checkedGirls removeAllObjects]; [_checkedGirls addObjectsFromArray:_girls]; [_tableView reloadData]; } // 调用自定义方法 修改检测状态 [self statusCheck]; } @end
属性列表文件girls.plist
main.storyboard
因为bar button item的文字不可更改, 遂换成label,
label不接收点击事件,所以可以向后传递给button处理点击事件
iOS_12_tableViewCell的删除更新_红楼梦
时间: 2024-11-03 05:27:25