一般table view有編輯模式和正常模式,當table view進入編輯模式時,會在row的左邊顯示編輯和重排控件,如圖 42所示的編輯模式時的控件布局;左邊的editing control有表 61的兩種圖標。
表 61 table view編輯控件
圖標 |
描述 |
|
Deletion控件 |
|
Insertion控件 |
若table view在編輯模式時,用戶點擊編輯控件,那么table view會發送一系列消息給data source 和delegate對象。可以通過實現這些方法來修改table view的外觀和行為,或者執行插入和刪除操作。
1 插入與刪除row
UITableView中有兩個編輯row的方法:插入和刪除。在任何時候都可直接調用這兩個方法,無需在tableView進入編輯模式才能調用。並且當調用這兩方法后,tableView會自動重新加載(reload)。
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation |
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation |
參數語義: indexPaths:希望修改的rows路徑,是一個路徑對象的數組; animation:為動畫類型。 |
注意:
這兩個方法的調用需要相應修改tableview中顯示的數據模型,即應保持data source的三個方法(section數、row數和cell對象)返回的數據與修改后的數據一致。如調用方法刪除某一行,那么該行就不能在tableview中再顯示,否則會奔潰出錯。
1.1 進入編輯模式
當tableView進入編輯模式時,即在每個row的左邊會出現插入或刪除控件。其中可以通過UITableView的setEditing:animated:方法來控制其進入和退出編輯模式。
- (void)setEditing:(BOOL)editing animated:(BOOL)animate |
參數語義: editing:控制進出操作,若為YES為進入編輯模式;若為NO為退出編輯模式。 animate:為設置是否進行動畫播放。 |
當調用了setEditing方法之后,tableView會陸續調用data source和delegate的幾個方法,具體執行次序如圖 61所示。
1) tableView對象首先調用data source對象的tableView:canEditRowAtIndexPath:方法(可選);
2) 然后tableView對象首先調用delegate對象的tableView:editingStyleForRowAtIndexPath:方法(必選),該方法返回row所要顯示的控件類型,即刪除或插入控件。
3) 接着,在table view中的row左邊會顯示響應的控件,此時用戶可以進行操作;
4)當用戶對row的編輯控件進行點擊時,會調用data source對象的tableView:commitEditingStyle:forRowAtIndexPath:方法(必選),用戶即可實現該方法,從而實現希望完成的操作,如刪除或添加某一row。
圖 61 Calling sequence for inserting or deleting rows in a table view
1.2 操作示例
1)tableView初始化
首先初始化一個數組來顯示table view中的cell,並在導航欄右邊添加一個進入編輯模式的按鈕,該按鈕為UITableView對象提供的控制項。
2 @property NSMutableArray * array;
3 @end
4 @implementation ViewController
5 - ( void)viewDidLoad {
6 [super viewDidLoad];
7 self.navigationItem.rightBarButtonItem = self.editButtonItem;
8
9 _array = [[NSMutableArray alloc] initWithObjects:
10 [NSMutableArray arrayWithObjects: @" Item1 ", @" Item2 ", nil],
11 [NSMutableArray arrayWithObjects: @" Item1 ", @" Item2 ", @" Item3 ", nil],
12 [NSMutableArray arrayWithObjects: @" Item1 ", @" Item2 ", @" Item3 ", @" Item4 ", nil],
13 [NSMutableArray arrayWithObjects: @" Item1 ", @" Item2 ", @" Item3 ", @" Item4 ", @" Item5 ",nil],
14 nil];
15 }
16 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
17 {
18 return [_array count];
19 }
20 -(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
21 {
22 return [_array[section] count];
23 }
24 -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
25 {
26 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: @" myCell "];
27 return cell;
28 }
2) 進入編輯模式
當用戶點擊導航按鈕時,將會自動調用如下實現的setEditing方法,在該方法中調用tableView對象的setEditing方法進入編輯模式;然后在第二個方法中將每個section中的奇數row顯示插入控件,偶數的row顯示為刪除控件;
2 [super setEditing:editing animated:animated];
3 [self.tableView setEditing:editing animated:YES];
4 }
5 - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
6 if (indexPath.row % 2 == 1) {
7 return UITableViewCellEditingStyleInsert;
8 } else {
9 return UITableViewCellEditingStyleDelete;
10 }
11 }
3) 響應操作
當用戶點擊編輯控件時,執行如下方法,從而在該方法中判斷是什么類型(插入或刪除操作);接着執行對array數組執行不同的操作,最終通過調用tableview的deleteRowsAtIndexPaths方法和insertRowsAtIndexPaths方法來刷新表格的內容。
2
3 if (editingStyle == UITableViewCellEditingStyleDelete) { // 若是刪除操作,則刪除數組內容,並刷新表格
4 [_array[indexPath.section] removeObjectAtIndex:indexPath.row];
5 [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
6 }
7 else // 若是插入操作,則添加一個數組元素,並刷新表格
8 {
9 [_array[indexPath.section] insertObject: @" hlw " atIndex:indexPath.row];
10 [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
11 }
12 }
4) 顯示效果
如影 61所示是表格的顯示效果,和刪除操作視頻。
影 61 刪除操作效果圖(雙擊)
2 批量操作
除了可以對row進行編輯,UITableView還支持對section進行編輯,並且可以一次性進行插入、刪除和重載等多個操作。如下所示是UITableView總共提供了8個方法:
- (void)beginUpdates; - (void)endUpdates; |
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; |
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation (UITableViewRowAnimation)animation; - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; |
其中對row和section的6個操作方法必須處於beginUpdates和endUpdates兩方法之間調用。
如下的示例,當用戶點擊某行時,就添加和刪除幾行:
2 {
3 [_array[ 2] removeObjectAtIndex: 0];
4 [_array[ 1] removeObjectAtIndex: 0];
5 [_array[ 0] insertObject: @" hlw " atIndex: 0];
6 [_array[ 3] insertObject: @" hlw " atIndex: 0];
7 NSArray *deleteIndexPaths = [NSArray arrayWithObjects:
8 [NSIndexPath indexPathForRow: 0 inSection: 2],
9 [NSIndexPath indexPathForRow: 0 inSection: 1],
10 nil];
11 NSArray *insertIndexPaths = [NSArray arrayWithObjects:
12 [NSIndexPath indexPathForRow: 0 inSection: 0],
13 [NSIndexPath indexPathForRow: 0 inSection: 3],
14 nil];
15
16 UITableView *tv = self.tableView;
17 [tv beginUpdates];
18 [tv deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
19 [tv insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
20 [tv endUpdates];
21 }
上述對_array的操作順序可以與調用tv的順序不一樣,其中動畫的展示效果是按tv的調用順序執行。
3 交換操作
UITableView提供了一個方法能夠交互兩row或兩section的順序,從而實現重新排序的效果,這兩個方法能夠在任何情況下使用,並不是必須進入編輯模式才能使用。
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection |
注意: moveRowAtIndexPath方法可以交換任意的兩row;但moveSection只能交換兩個section有相同數量的row。 |
如下所示,當用戶點擊任何一行時,即與下一行交換:
2 {
3 NSIndexPath * to = [NSIndexPath indexPathForRow:indexPath.row+ 1 inSection:indexPath.section];
4 [tableView moveRowAtIndexPath:indexPath toIndexPath:to];
5 }
3.1 進入編輯模式
與刪除和插入row類型,當table view進入編輯模式,即調用了setEditing方法之后,tableView會陸續調用data source和delegate的幾個方法,具體執行次序如圖 62所示。
1) tableView對象首先調用data source對象的tableView:canMoveRowAtIndexPath:方法(必選);如果該方法返回YES,則在cell的右邊會顯示一個可交換的控件。
2) 當在cell中出現交換控件后,用戶即可拖拽交換row。
3) 若當用戶拖拽了row后,那么tableView對象會去調用delegate對象的tableView:targetIndexPathForMoveFromRowAtIndexPath方法(可選)。
4) 接着,table view對象會去調用data source對象tableView:moveRowAtIndexPath:toIndexPath:方法(必選),這個方法會自動交換拖拽的兩行,不需要調用 UITableView的moveRowAtIndexPath方法即可進行交換。

圖 62 Calling sequence for reordering a row in a table view
3.2 操作示例
如在6.1.2小節所示的示例基礎之上進行操作,不允許每個section的第0行進行交換,而其它方法是可以交換的,如下是實現的兩個方法:
2 if (indexPath.row == 0) {
3 return NO;
4 }
5 return YES;
6 }
7 - ( void)tableView:(UITableView *)tableView
8 moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
9 toIndexPath:(NSIndexPath *)toIndexPath
10 {
11
12 }
如影 62所示展示的顯示效果,其與插入和刪除操作不會沖突,都能夠顯示。

影 62 交換row效果圖
4 自定義editingAccessoryView
4.1 修改附加視圖
當進入編輯模式時,cell右邊還有一個附件視圖的位置,默認情況為空,用戶可以使用標准視圖或者自定義視圖內容,只需修改UITableViewCell對象的editingAccessoryView屬性,同時還可以修改刪除按鈕的顯示內容,其由tableView:titleForDeleteConfirmationButtonForRowAtIndexPath方法決定顯示的內容:
-(NSString*)tableView:(UITableView*)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath |
返回參數: 為所要顯示的標題內容。 |
如下所示,當進入編輯模式時,顯示附件視圖並修改刪除按鈕的內容:
2 {
3 UILabel *mainLabel;
4 mainLabel = [[UILabel alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 15.0)];
5 mainLabel.text = _array[indexPath.section][indexPath.row];
6
7 static NSString *CellIdentifier = @" myCell ";
8 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
9
10 [cell setEditingAccessoryView:mainLabel];
11 // cell.editingAccessoryView = mainLabel; // 或者直接賦值
12
13 return cell;
14 }
15 - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
16 {
17 return @" myDelete ";
18 }

圖 63 編輯模式自定義accessoryView效果圖
4.2 修改刪除按鈕
對於那些支持刪除操作的cell,當在編輯模式中點擊左邊的刪除控件,會在右邊出現有一個刪除按鈕;或者在正常模式下,水平滑動也能出現這個刪除按鈕,當點擊這個刪除按鈕后,會調用data source對象的tableView:commitEditingStyle:方法(如6.1.1小節所示)。
UITableView支持修改上述這個刪除按鈕和相應響應方法,即用戶可以實現UITableViewDelegate協議的tableView:editActionsForRowAtIndexPath方法來自定義顯示內容和響應方法,該方法的定義為:
-(NSArray<UITableViewRowAction*>*)tableView:(UITableView*)tableView //UITableViewDelegate協議方法 editActionsForRowAtIndexPath:(NSIndexPath *)indexPath |
+ (instancetype)rowActionWithStyle:(UITableViewRowActionStyle)style //UITableViewRowAction類的便利方法 title:(NSString *)title handler:(void (^)(UITableViewRowAction *action, NSIndexPath *indexPath))handler |
rowActionWithStyle方法參數語義: style:為顯示的類型; title:為顯示的標題; handler:為響應的block。 |
如下所示,創建兩個按鈕來替換系統默認的刪除按鈕,當用戶水平滑動時,出現兩個按鈕,當點擊時執行相應的block。
2 editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
3 {
4 UITableViewRowAction *first = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title: @" first " handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
5 NSLog( @" UITableViewRowAction action first ");
6 }];
7 UITableViewRowAction *second = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title: @" second " handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
8 NSLog( @" UITableViewRowAction action second ");
9 }];
10
11 NSArray * array =[[NSArray alloc] initWithObjects:first, second, nil];
12
13 return array;
14 }

影 63 效果圖
其中上述的itemX為附加視圖的內容,而first和second按鈕為修改的內容。
5 參考文獻
[1] Table View Programming Guide for IOS