Editing UITableView

UITableView有一个editing属性,当这个属性被设置为true时,table view进入编辑模式。在编辑模式,可以修改行顺序,增加行,删除行,但是不能修改行内容。

UITableView有header view和footer view,他们可以是section范围的,也可以是整个table范围的。

通过XIB为table view添加header view,先在XIB的File's Owner中声明IBOutlet和IBAction。

#import "BKItemsViewController.h"
#import "BKItem.h"
#import "BKItemStore.h"

@interface BKItemsViewController ()

@property (nonatomic, strong) IBOutlet UIView *headerView;

@end

@implementation BKItemsViewController

// .....

- (IBAction)addNewItem:(id)sender{
    
}

- (IBAction)toggleEditingMode:(id)sender{
    
}

@end

headerView属性声明为strong,是因为他是XIB文件中的top-level object,top-level object拥有的对象声明为weak的。

XIB文件不仅可以用来创建view controller的view,还可以布局view对象,然后在运行时加载他们。

XIB files are typically used to create the view for a view controller, but they can also be used any time you want to lay out view objects, archive them, and have them loaded at runtime.

创建XIB

创建一个空的user interface,选择File's Owner,修改其Class为BKItemsViewController


view的大小默认是屏幕大小,不可调整,可以设置其size为none,使其大小可以自由调整。

还是在上图的attributes inspector中,将view的background设置为clear color.

将view和File's Owner关联起来:
将两个button的action连接到两个方法,将File's Owner的headerView属性和view关联起来,这样当加载XIB文件时,headerView就是这个关联的view。


手动加载XIB文件,需要使用NSBundle,当应用启动后,就会有一个对应的NSBundle实例,要获得此实例,调用[NSBundle mainBundle],加载完后,将headerView设置给table view。

#import "BKItemsViewController.h"
#import "BKItem.h"
#import "BKItemStore.h"

@interface BKItemsViewController ()

@property (nonatomic, strong) IBOutlet UIView *headerView;

@end

@implementation BKItemsViewController

// ...

- (void)viewDidLoad{
    [super viewDidLoad];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];
    
    // 设置table view的header view
    UIView *header = self.headerView;
    [self.tableView setTableHeaderView:header];
}

// headerView属性的get方法,当第一次调用此方法时,加载对应的XIB文件
- (UIView *)headerView{
    if(!_headerView){
        // 加载 HeaderView.xib
        [[NSBundle mainBundle] loadNibNamed:@"HeaderView" owner:self options:nil];
    }
    return _headerView;
}

@end

启用table view编辑模式

不仅UITableView有一个editing属性,UITableViewController也有一个editing属性,table view controller会自动设置table view's editing property和自己的editing property保持一致。

要设置table view controller的editing property,需要发送setEditing:animated: message.

- (IBAction)toggleEditingMode:(id)sender{
    if (self.isEditing) {
        [sender setTitle:@"Edit" forState:UIControlStateNormal];
        [self setEditing:NO animated:YES];
    } else {
        [sender setTitle:@"Done" forState:UIControlStateNormal];
        [self setEditing:YES animated:YES];
    }
}

添加行

为table view插入一行

- (IBAction)addNewItem:(id)sender{
    // 为要插入的行创建index path
    NSInteger lastRow = [self.tableView numberOfRowsInSection:0];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
    
    // 插入一行
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
}

此时运行,应用崩溃程序出错,因为table view的行是由其dataSource决定的,这里只为table view插入一行,但是其dataSource中却没有这行的数据,所以出错。

必须保证table view的行数和其dataSource中的数量一致。所以在插入一行之前,先添加数据到dataSource中。

- (IBAction)addNewItem:(id)sender{
    // 为要插入的行创建index path
    //NSInteger lastRow = [self.tableView numberOfRowsInSection:0];
    
    // 新建一条数据
    BKItem *newItem = [[BKItemStore sharedStore] createItem];
    NSInteger lastRow = [[[BKItemStore sharedStore] allItems] indexOfObject:newItem];
    
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
    
    // 插入一行
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
}

在插入一行时,发送了tableView message给BKItemsViewController,tableView属性继承自UITableViewController,指定一个UITableView实例,view属性其实和tableView属性指向同一个实例,但是view属性的类型是UIView。

删除行

当table view要删除一行时,会发送消息给data source,并等待确认后才会删除。

要删除一行,需要两个动作:tabel view删除一行,数据源也删除对应的行数据。

table view的dataSource是BKItemViewController,BKItemStore只是一个存放和操作数据的地方
为BKItemStore添加删除方法:

#import <Foundation/Foundation.h>

@class BKItem;

@interface BKItemStore : NSObject

@property (nonatomic, readonly) NSArray *allItems;

// class method
+ (instancetype)sharedStore;

- (BKItem *)createItem;
- (void)removeItem:(BKItem *)item;

@end

在实现文件中实现

- (void)removeItem:(BKItem *)item{
    //[self.privateItems removeObject:item];
    [self.privateItems removeObjectIdenticalTo:item];
}

removeObject和removeObjectIdenticalTo的区别:

method desc
removeObject: 传递的对象只要和数据中的对象isEqual:就会被删除
removeObjectIdenticalTo: 删除==的对象

在table view的delegate,即BKItemsViewController中实现tableView:commitEditingStyle:forRowAtIndexPath:方法,当要真正删除时发送deleteRowsAtIndexPaths:withRowAnimation:返回给table view。

// 当table view要删除一行时,会发送此消息到其delegate,这个方法是在UITableVieaDataSource protocol中定义的
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
    
    // 当是要执行删除时
    if(editingStyle == UITableViewCellEditingStyleDelete){
        NSArray *items = [[BKItemStore sharedStore] allItems];
        BKItem *item = items[indexPath.row];
        
        // 删除cell对应的item
        [[BKItemStore sharedStore] removeItem:item];
        
        // table view 删除行
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}

移动行

首先为操作数据的类BKItemStore添加一个方法,来修改数据的顺序。
在头文件中声明

- (void)moveItemAtIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex;
- (void)moveItemAtIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex{
    if (fromIndex == toIndex) {
        return;
    }
    BKItem *item = self.privateItems[fromIndex];
    
    [self.privateItems removeObjectAtIndex:fromIndex];
    
    [self.privateItems insertObject:item atIndex:toIndex];
}

当table view要更改行顺序时,需要发送tableView:moveRowAtIndexPath:toIndexPath:给其代理,在BKItemsViewController中实现此方法,当table view的代理实现了此方法,那么table view的编辑模式就会出现 reordering controls(就是列右侧的三个横线)。

// 移动行顺序
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
    [[BKItemStore sharedStore] moveItemAtIndex:sourceIndexPath.row toIndex:destinationIndexPath.row];
}

本文是对《iOS Programming The Big Nerd Ranch Guide 4th Edition》第九章的总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容