首页 > 代码库 > iOS_12_tableViewCell的删除更新_红楼梦

iOS_12_tableViewCell的删除更新_红楼梦

最终效果图:



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处理点击事件