我们知道常见的App里,『设置』这一块都长的差不多,都是表格展示,我们是否可以灵活配置cell呢?
我们可以写一个基础的类,来实现常见功能。
1.现在看结构

model说明:我们的表格是分组样式,模型里一个item就对应表格的一个cell, 一个group里有多个item。
2.cell模型代码:
//
// SettingItem.h
// 表格每一个cell对应一个SettingItem模型
#import <Foundation/Foundation.h>
/**
* 定义一个block类型
*/
typedef void (^SettingItemOption)();
@interface SettingItem : NSObject
/**
* cell上的图标
*/
@property(nonatomic,copy)NSString *icon;
/**
* cell标题
*/
@property(nonatomic,copy)NSString *title;
/**
* 子标题
*/
@property(nonatomic,copy)NSString *subtitle;
/**
* 点击那个cell需要做什么事情
*/
@property(nonatomic,copy) SettingItemOption option;
/**
* 返回一个cell(图标和大标题)
*
* @param icon 图标
* @param title 大标题
*/
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title;
/**
* 返回一个cell(只有大标题)
*/
+ (instancetype)itemWithTitle:(NSString *)title;
/**
* 返回一个cell
*
* @param icon 图标
* @param title 大标题
* @param subtitle 子标题
*/
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title subtitle:(NSString *)subtitle;
@end//
// SettingItem.m
#import "SettingItem.h"
@implementation SettingItem
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title subtitle:(NSString *)subtitle
{
SettingItem *item = [[self alloc]init];
item.icon = icon;
item.title = title;
item.subtitle = subtitle;
return item;
}
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title
{
return [self itemWithIcon:icon title:title subtitle:nil];
}
+ (instancetype)itemWithTitle:(NSString *)title
{
return [self itemWithIcon:nil title:title subtitle:nil];
}
@end3.组模型代码:
//
// SettingGroup.h
// 『设置模块』里表格组模型
#import <Foundation/Foundation.h>
@interface SettingGroup : NSObject
/**
* 组头部标题
*/
@property(nonatomic,copy)NSString *header;
/**
* 组尾部标题
*/
@property(nonatomic,copy)NSString *footer;
/**
* 该组所有模型数据(SettingItem对象)
*/
@property(nonatomic,copy)NSArray *items;
@end//
// SettingGroup.m
#import "SettingGroup.h"
@implementation SettingGroup
@end4.其他几个继承自SettingItem的子分类(不同形式的cell)
//
// SettingArrowItem.h
// cell模型(cell右侧展示箭头的)
#import "SettingItem.h"
@interface SettingArrowItem : SettingItem
/**
* 点击这行cell要跳转的控制器
*/
@property(nonatomic,assign) Class destClass;
/**
* 返回一个cell(点击这个cell要跳转)
*
* @param icon 图标
* @param title 标题
* @param subtitle 子标题
* @param destClass 要跳转过去的目标控制器
*/
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title subtitle:(NSString *)subtitle destClass:(Class)destClass;
/**
* 返回一个点击可以跳转的cell
*
* @param icon 图标
* @param title 标题
* @param destClass 要跳转过去的目标控制器
*/
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title destClass:(Class)destClass;
/**
* 返回一个点击可以跳转的cell
*
* @param title 标题
* @param destClass 要跳转过去的目标控制器
*/
+ (instancetype) itemWithTitle:(NSString *)title destClass:(Class)destClass;
@end//
// SettingArrowItem.m
// 高考志愿填报指南
#import "SettingArrowItem.h"
@implementation SettingArrowItem
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title subtitle:(NSString *)subtitle destClass:(__unsafe_unretained Class)destClass
{
SettingArrowItem *item = [self itemWithIcon:icon title:title subtitle:subtitle];
item.destClass = destClass;
return item;
}
+ (instancetype) itemWithIcon:(NSString *)icon title:(NSString *)title destClass:(__unsafe_unretained Class)destClass
{
SettingArrowItem *item = [self itemWithIcon:icon title:title subtitle:nil destClass:destClass];
return item;
}
+ (instancetype) itemWithTitle:(NSString *)title destClass:(Class)destClass
{
return [self itemWithIcon:nil title:title subtitle:nil destClass:destClass];
}
@end//
// SettingSwitchItem.h
// cell模型(cell上要展示Switch开关的)
#import "SettingItem.h"
@interface SettingSwitchItem : SettingItem
@end//
// SettingSwitchItem.m
// 高考志愿填报指南
#import "SettingSwitchItem.h"
@implementation SettingSwitchItem
@end//
// SettingLabelItem.h
// cell模型(右边是一个Label,不是箭头,不是Switch开关)
#import "SettingItem.h"
@interface SettingLabelItem : SettingItem
@end//
// SettingLabelItem.m
#import "SettingLabelItem.h"
@implementation SettingLabelItem
@end5.cell视图
//
// SettingCell.h
// 『设置模块』cell视图
#import <UIKit/UIKit.h>
@class SettingItem;
@interface SettingCell : UITableViewCell
/**
* 这个属性用于接收外界传递给cell的模型数据
*/
@property(nonatomic,strong) SettingItem *item;
/**
* 快速创建一个cell的方法
*/
+(instancetype)cellWithTableView:(UITableView *)tableView;
@end//
// SettingCell.m
#import "SettingCell.h"
#import "SettingItem.h"
#import "SettingArrowItem.h"
#import "SettingSwitchItem.h"
#import "SettingLabelItem.h"
@interface SettingCell()
// UI控件要实时保留,所以用 strong
@property(nonatomic,strong)UIImageView *arrowView;
@property(nonatomic,strong)UISwitch *switchView;
@property(nonatomic,strong)UILabel *labelView;
@end
@implementation SettingCell
/**
* 懒加载创建arrowView控件
*/
- (UIImageView *)arrowView
{
if (_arrowView == nil) {
_arrowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellArrow"]];
}
return _arrowView;
}
/**
* 懒加载创建switchView控件
*/
- (UILabel *)labelView
{
if (_labelView == nil) {
_labelView = [[UILabel alloc]init];
_labelView.bounds = CGRectMake(0, 0, 100, 30);
_labelView.backgroundColor = [UIColor redColor];
}
return _labelView;
}
/**
* 懒加载创建switchView控件
*/
- (UISwitch *)switchView
{
if (_switchView == nil) {
_switchView = [[UISwitch alloc]init];
[_switchView addTarget:self action:@selector(switchStateChange) forControlEvents:UIControlEventValueChanged];
}
return _switchView;
}
/**
* 监听swicth开关状态的改变
*/
- (void)switchStateChange
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:self.switchView.isOn forKey:self.item.title];
[defaults synchronize];
}
/**
* 提供一个创建cell的类方法
*/
+(instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"setting";
SettingCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[SettingCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID];
}
return cell;
}
/**
* 重写item属性,设置cell上的数据
*/
- (void)setItem:(SettingItem *)item
{
_item = item;
//1.设置数据
[self setupData];
//2.设置cell右边的内容(箭头)
[self setupRightContent];
}
/**
* 给cell设置数据
*/
- (void)setupData
{
if (self.item.icon) {
self.imageView.image = [UIImage imageNamed:self.item.icon];
}
self.textLabel.text = self.item.title;
self.detailTextLabel.text = self.item.subtitle;
}
/**
* 给cell设置右边的样式(箭头)
*/
- (void)setupRightContent
{
if ([self.item isKindOfClass:[SettingArrowItem class]]) { //箭头
// self.accessoryView = self.arrowView;
// 如果没有自定义箭头,使用系统默认的
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
self.selectionStyle = UITableViewCellSelectionStyleDefault;
}else if ([self.item isKindOfClass:[SettingSwitchItem class]]){ //开关
self.accessoryView = self.switchView;
//如果是开关该cell不能有点击样式
self.selectionStyle = UITableViewCellSelectionStyleNone;
//设置开关状态
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.switchView.on = [defaults boolForKey:self.item.title];
}else if ([self.item isKindOfClass:[SettingLabelItem class]]){ //标签
self.accessoryView = self.labelView;
self.selectionStyle = UITableViewCellSelectionStyleDefault;
}else{
self.accessoryView = nil;
}
}
@end
6.控制器
//
// BaseSettingViewController.h
// 项目常见"设置模块"基础类
#import <UIKit/UIKit.h>
@interface BaseSettingViewController : UITableViewController
/**
* 数据源
*/
@property(nonatomic,strong)NSMutableArray *dataSources;
@end//
// BaseSettingViewController.m
#import "BaseSettingViewController.h"
#import "SettingGroup.h"
#import "SettingItem.h"
#import "SettingSwitchItem.h"
#import "SettingArrowItem.h"
#import "SettingCell.h"
@interface BaseSettingViewController ()
@end
@implementation BaseSettingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 1.设置表格背景色
self.tableView.backgroundView = nil;
self.tableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];
}
/**
* 懒加载创建数据源数组
*/
- (NSMutableArray *)dataSources
{
if (_dataSources == nil) {
_dataSources = [NSMutableArray array];
}
return _dataSources;
}
/**
* 初始化tablView的样式
*/
- (instancetype)init
{
/*
UITableViewStylePlain, 普通样式
UITableViewStyleGrouped 分组样式
*/
return [super initWithStyle:UITableViewStyleGrouped];
}
/**
* 初始化tableView的样式(也有可能调用这个方法)
*/
- (instancetype)initWithStyle:(UITableViewStyle)style
{
return [super initWithStyle:UITableViewStyleGrouped];
}
#pragma mark - Table view data source
/**
* 有多少组
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.dataSources.count;
}
/**
* 该组里面有多少行
*/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
SettingGroup *group = self.dataSources[section];
return group.items.count;
}
/**
* 返回UITableViewCell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.创建cell
SettingCell *cell = [SettingCell cellWithTableView:tableView];
// 2.给cell传递模型数据
SettingGroup *group = self.dataSources[indexPath.section];
SettingItem *item = group.items[indexPath.row];
cell.item = item;
// 3.返回cell
return cell;
}
/**
* 设置组头部标题
*/
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
SettingGroup *group = self.dataSources[section];
return group.header;
}
/**
* 设置组尾部标题
*/
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
SettingGroup *group = self.dataSources[section];
return group.footer;
}
#pragma mark - 代理方法
/**
* 点击了cell会调用
*/
-(void)tableView:(UITableView *)tableView <span style="font-family: Arial, Helvetica, sans-serif;">didSelectRowAtIndexPath</span>:(NSIndexPath *)indexPath
{
// 0.取消cell选中样式(默认cell选中会变灰)
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// 1.拿到模型
SettingGroup *group = self.dataSources[indexPath.section];
SettingItem *item = group.items[indexPath.row];
if (item.option) { //block有值,点击这个cell有特定的操作执行
item.option();
}
//需要跳转的控制器
if ([item isKindOfClass:[SettingArrowItem class]]) { //箭头
SettingArrowItem *arrowItem = (SettingArrowItem *)item;
if (arrowItem.destClass == nil) return;
UIViewController *vc = [[arrowItem.destClass alloc]init];
vc.title = arrowItem.title;
[self.navigationController pushViewController:vc animated:YES];
}
}
@end
自从,一个基础的可以通用的设置模块就完成了。
那么,在我们的项目里,应该如何使用呢?
1.我们需要创建一个继承自BaseSettingViewController的控制器
//
// SettingViewController.h
#import "BaseSettingViewController.h"
@interface SettingViewController : BaseSettingViewController
@end2.然后在viewDidLoad初始化数据源:
//
// SettingViewController.m
#import "SettingViewController.h"
#import "SettingItem.h"
#import "SettingSwitchItem.h"
#import "SettingArrowItem.h"
#import "SettingGroup.h"
#import "SettingCell.h"
@interface SettingViewController ()
@end
@implementation SettingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 1.设置当前控制器标题
self.title = @"设置";
// 2.添加要数据
[self setupGroup0];
}
/**
* 第0组数据
*/
- (void)setupGroup0
{
// 1.设置cell模型
SettingItem *item0 = [SettingArrowItem itemWithIcon:@"subject-of-attention_icon" title:@"科目"];
SettingItem *item1 = [SettingSwitchItem itemWithIcon:@"subject-of-attention_icon" title:@"摇一摇机选" ];
SettingItem *item2 = [SettingSwitchItem itemWithIcon:@"subject-of-attention_icon" title:@"声音效果" ];
// 2.把cell模型添加到组
SettingGroup *group = [[SettingGroup alloc]init];
group.items = @[item0,item1,item2];
// 3.把组模型添加到数据源
[self.dataSources addObject:group];
}
@enddataSource里装的是组模型数据,组数据里装的是item模型数据。
这里值添加了一组数组,如果要添加第二组数据,照例配置,先把item加到group,再把group加到dataSoures
效果展示:

















