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》第九章的總結。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容