我们知道常见的App里,『设置』这一块都长的差不多,都是表格展示,我们是否可以灵活配置cell呢?

我们可以写一个基础的类,来实现常见功能。

1.现在看结构

CellRangeAddress在哪里_CellRangeAddress在哪里

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];
}

@end

3.组模型代码:

//
//  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

@end

4.其他几个继承自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

@end

5.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

@end

2.然后在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];
}

@end

dataSource里装的是组模型数据,组数据里装的是item模型数据。

这里值添加了一组数组,如果要添加第二组数据,照例配置,先把item加到group,再把group加到dataSoures

效果展示:

CellRangeAddress在哪里_CellRangeAddress在哪里_02