最近在写一个轻量级的网络游戏,遇到了技能优先顺序手动排序的需求,我就想到了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];}