1. 程式人生 > >App裡『設定』模組,通用配置

App裡『設定』模組,通用配置

我們知道常見的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];
}

@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


效果展示: