最近在寫一個輕量級的網絡游戲,遇到了技能優先順序手動排序的需求,我就想到了iOS自帶的tableView編輯功能,對其進行了初步探索,最后做出的效果如下圖所示:
點擊左邊可以刪除,拖住右邊可以手動排序,要實現這個功能,分以下步驟。
①用plist存儲這些數據,可以看到數據分兩個職業,每個職業4個技能,因此建立如下的plist結構:
②因為每個職業除了技能還有名稱這個屬性,因此應該用職業模型保存一個職業的所有數據,再用一個數組保存所有職業模型,職業模型的定義如下:
#import <Foundation/Foundation.h>@interface Vocation : NSObject@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSMutableArray *skills;+ (instancetype)vocationWithDict:(NSDictionary *)dict;@end
需要注意的是這里沒有利用系統實現KVC,因為如果采用系統自帶的,在把plist中的NSArray傳給NSMutableArray時,因為NSMutableArray沒有初始化 ,所以就變成了不可變的數組,這樣為后面的順序調整帶來了致命的問題,因此我們手動實現KVC,用NSArray初始化一個NSMutableArray。#import "Vocation.h"@implementation Vocation+ (instancetype)vocationWithDict:(NSDictionary *)dict{Vocation *vc = [[Vocation alloc] init];vc.title = dict[@"title"];vc.skills = [NSMutableArray arrayWithArray:dict[@"skills"]];return vc;}@end
③使用一個TableViewController,并且實現下面的代碼:
#import "TableViewController.h"
#import "Vocation.h"@interface TableViewController ()@property (nonatomic, strong) NSArray *vocations;@end@implementation TableViewController- (void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];self.editing = YES;}- (BOOL)prefersStatusBarHidden{return YES;}- (NSArray *)vocations{if (_vocations == nil) {NSString *path = [[NSBundle mainBundle] pathForResource:@"skillList.plist" ofType:nil];NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];NSMutableArray *vocations = [NSMutableArray array];for (NSDictionary *dict in dictArray) {Vocation *vc = [Vocation vocationWithDict:dict];[vocations addObject:vc];}_vocations = vocations;}return _vocations;}- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{return self.vocations.count;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{Vocation *vc = self.vocations[section];return vc.skills.count;}- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{Vocation *vc = self.vocations[section];return vc.title;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{static NSString *ID = @"vocation";UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];if (cell == nil) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];}// 在這里設置cell數據Vocation *vc = self.vocations[indexPath.section];cell.textLabel.text = vc.skills[indexPath.row];return cell;}- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{if (sourceIndexPath.section != destinationIndexPath.section) {[self.tableView reloadData];return;}Vocation *vc = self.vocations[sourceIndexPath.section];[vc.skills exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row];}- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{if (editingStyle == UITableViewCellEditingStyleDelete) {Vocation *vc = self.vocations[indexPath.section];[vc.skills removeObjectAtIndex:indexPath.row];}[self.tableView reloadData];}- (IBAction)editClick:(id)sender {UIBarButtonItem *btn = sender;if ([btn.title isEqualToString:@"調整"]) {btn.title = @"確定";self.editing = YES;}else{btn.title = @"調整";self.editing = NO;[self.tableView reloadData];}}@end
在這其中,editClick:對應了NavigationBar上的按鈕,用于切換編輯和非編輯狀態。 通過tableViewController的editing方法控制是否進入編輯狀態。
要實現拖動排序,需要實現下面的方法,否則不能拖動,在這個方法中可以獲取到起始和終止位置。
需要注意的是移動只是單純的視覺效果,實際的數據源變化需要自己調整,否則在重新加載數據后又會回到原來的順序,可通過數組的exchangeObjectAtIndexPath::方法調整。
為了避免組間移動,這里進行了判斷,發現非法移動直接重置數據。
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{if (sourceIndexPath.section != destinationIndexPath.section) {[self.tableView reloadData];return;}Vocation *vc = self.vocations[sourceIndexPath.section];[vc.skills exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row];}