代码改变世界

iOS 网易彩票-4设置模块一

  jiangys  阅读(1219)  评论(0编辑  收藏  举报

概述

基本上,每一款APP都有相应的设置模块。怎么设置才能更灵活和通用呢,这也是大家一直思考的。下面说说在网易彩票中,设置模块的设置思想。

基本上有三种方案:

  1. static cell(呆板,完全没有动态)
  2. 使用代码,条件判断逐个编写(麻烦,代码冗长)
  3. 使用plist加载(能够动态配置跳转控制器,不能配置请求代码;由于使用字符串配置跳转控制器名,容易出现运行时错误)
  4. 使用模型封装每个cell的数据(item),使用Class作为跳转控制器属性(这样就能经过编译检测)

最终我们使用的是第4种方式来实现,效果:

实现

思路:

1.创建cell的数据模型group(头部标题、尾部标题及items)

2.创建cell的数据模型item(图标、标题、block、跳转到控制器)

3.自定义cell,根据不同的item模型创建不同的cell外观(箭头、开头、文本等)

4.自定一个基类控制器,实现

  • 加载解析group和item数据
  • 根据group数据创建table的样式(组底部、组尾部)
  • 加载自定义的cell
  • 如果item类是跳转类型的,需要配置跳转目标控制器
  • 如果item类是代码执行型的,需要配置要执行的block代码

5.设置界面实现基类控制器,只需要简单配置要显示的item

创建cell的数据模型group

MJSettingGroup.h

复制代码
#import <Foundation/Foundation.h>

@interface MJSettingGroup : NSObject

/** 头部标题*/
@property (nonatomic, copy) NSString *header;
/** 尾部标题*/
@property (nonatomic, copy) NSString *footer;
/** 存放这组行的所有的模型*/
@property (nonatomic, copy) NSArray *items;

@end
复制代码

创建cell的数据模型item

    

单元Cell中有好几种类型:

  • 开头
  • 箭头
  • 文本

因此,为了在界面上更容易区别,我们分别为这几个类型定义一个模型。当前,这几种类型的中,左边显示图标和不显示图标都是一样的,因此,定义一个单元Cell模型基类,其它都继续于这个基类。

(基类)MJSettingItem.h

复制代码
#import <Foundation/Foundation.h>

typedef void (^MJSettingItemOption)();

@interface MJSettingItem : NSObject

/**    图标    */
@property (nonatomic, copy) NSString *icon;
/**    标题    */
@property (nonatomic, copy) NSString *title;
/**    点击cell要做的事情    */
@property (nonatomic, copy) MJSettingItemOption option;

+(instancetype)itemWithIcon:(NSString *)icon title:(NSString *)title;
+(instancetype)itemWithTitle:(NSString *)title;
@end
复制代码

(基类)MJSettingItem.m

复制代码
#import "MJSettingItem.h"

@implementation MJSettingItem

+(instancetype)itemWithTitle:(NSString *)title
{
    return [self itemWithIcon:nil title:title];
}

+(instancetype)itemWithIcon:(NSString *)icon title:(NSString *)title
{
    MJSettingItem *item = [[self alloc] init];
    item.icon = icon;
    item.title = title;
    return item;
}

@end
复制代码

MJSettingLabelItem(文本)、MJSettingSwitchItem(开关)、MJSettingArrowItem(箭头)都分别继承MJMJSettingItem

MJSettingArrowItem.h

复制代码
#import "MJSettingItem.h"

@interface MJSettingArrowItem : MJSettingItem
/**
 *  点击这行cell需要跳转的控制器
 */
@property (nonatomic, assign) Class destVcClass;

+ (instancetype)itemWithIcon:(NSString *)icon title:(NSString *)title destVcClass:(Class)destVcClass;
+ (instancetype)itemWithTitle:(NSString *)title destVcClass:(Class)destVcClass;

@end
复制代码

MJSettingArrowItem.m

复制代码
#import "MJSettingArrowItem.h"

@implementation MJSettingArrowItem

+ (instancetype)itemWithIcon:(NSString *)icon title:(NSString *)title destVcClass:(Class)destVcClass
{
    MJSettingArrowItem *item = [self itemWithIcon:icon title:title];
    item.destVcClass = destVcClass;
    return item;
}

+ (instancetype)itemWithTitle:(NSString *)title destVcClass:(Class)destVcClass
{
    return [self itemWithIcon:nil title:title destVcClass:destVcClass];
}

@end
复制代码

自定义cell

需要根据模型来给每个cell设置数据和类型,因而,通过自定义cell来实现。分别指定显示的数据和右边显示的内容类型。

MJSettingCell.h

复制代码
#import <UIKit/UIKit.h>

@class MJSettingItem;

@interface MJSettingCell : UITableViewCell
/**
 *  每个cell需要显示的数据
 */
@property (nonatomic, strong) MJSettingItem *item;

+ (instancetype)cellWithTableView:(UITableView *)tableView;

@end
复制代码

MJSettingCell.m

复制代码
//
//  MJSettingCell.m
//  Lottery
//
//  Created by apple on 15/9/6.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

#import "MJSettingCell.h"
#import "MJSettingItem.h"
#import "MJSettingSwitchItem.h"
#import "MJSettingArrowItem.h"
#import "MJSettingLabelItem.h"

@interface MJSettingCell()
/** 箭头*/
@property (nonatomic, strong) UIImageView *arrowView;
/** 开关*/
@property (nonatomic, strong) UISwitch *switchView;
/** 标签*/
@property (nonatomic, strong) UILabel *labelView;
@end

@implementation MJSettingCell

//懒加载,所有的箭头都只加载一次
- (UIImageView *)arrowView
{
    if (_arrowView == nil) {
        _arrowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellArrow"]];
    }
    return _arrowView;
}

//懒加载,所有的开关都只加载一次
- (UISwitch *)switchView
{
    if (_switchView == nil) {
        _switchView = [[UISwitch alloc] init];
    }
    return _switchView;
}

//懒加载,所有的文本都只加载一次
- (UILabel *)labelView
{
    if (_labelView == nil) {
        _labelView = [[UILabel alloc] init];
        _labelView.bounds = CGRectMake(0, 0, 100, 30);
        _labelView.backgroundColor = [UIColor redColor];
    }
    return _labelView;
}

+ (instancetype)cellWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"setting";
    MJSettingCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[MJSettingCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }
    return cell;
}

/**
 *  重写定义的属性item的Set方法,只有赋值就设定数据
 */
- (void)setItem:(MJSettingItem *)item
{
    _item = item;
    
    // 1.设置数据
    [self setupData];
    
    // 2.设置右边的内容
    [self setupRightContent];
}

/**
 *  设置右边的内容
 */
- (void)setupRightContent
{
    if ([self.item isKindOfClass:[MJSettingArrowItem class]]) { // 箭头
        self.accessoryView = self.arrowView;
    } else if ([self.item isKindOfClass:[MJSettingSwitchItem class]]) { // 开关
        self.accessoryView = self.switchView;
        self.selectionStyle = UITableViewCellSelectionStyleNone;
    } else if ([self.item isKindOfClass:[MJSettingLabelItem class]]) { // 标签
        self.accessoryView = self.labelView;
    } else {
        self.accessoryView = nil;
    }
}

/**
 *  设置数据
 */
- (void)setupData
{
    if (self.item.icon) {
        self.imageView.image = [UIImage imageNamed:self.item.icon];
    }
    self.textLabel.text = self.item.title;
}

@end
复制代码

自定一个基类控制器,实现

  • 加载解析group和item数据
  • 根据group数据创建table的样式(组底部、组尾部)
  • 根据不同的item模型创建不同的cell外观(箭头、开头、文本等)
  • 如果item类是跳转类型的,需要配置跳转目标控制器
  • 如果item类是代码执行型的,需要配置要执行的block代码

MJBaseSettingViewController.h

#import <UIKit/UIKit.h>

@interface MJBaseSettingViewController : UITableViewController
@property (nonatomic, strong) NSMutableArray *data;
@end

 MJBaseSettingViewController.m

复制代码
//
//  MJBaseSettingViewController.m
//  Lottery
//
//  Created by apple on 15/9/6.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

#import "MJBaseSettingViewController.h"
#import "MJSettingArrowItem.h"
#import "MJSettingSwitchItem.h"
#import "MJSettingGroup.h"
#import "MJSettingCell.h"

@interface MJBaseSettingViewController ()
@end

@implementation MJBaseSettingViewController

- (id)init
{
    return [super initWithStyle:UITableViewStyleGrouped];
}

- (id)initWithStyle:(UITableViewStyle)style
{
    return [super initWithStyle:UITableViewStyleGrouped];
}

- (NSArray *)data
{
    if (_data == nil) {
        _data = [NSMutableArray array];
    }
    return _data;
}

#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.data.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    MJSettingGroup *group = self.data[section];
    return group.items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.创建cell
    MJSettingCell *cell = [MJSettingCell cellWithTableView:tableView];
    
    // 2.给cell传递模型数据
    MJSettingGroup *group = self.data[indexPath.section];
    cell.item = group.items[indexPath.row];
    
    // 3.返回cell
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.取消选中这行
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    // 2.模型数据
    MJSettingGroup *group = self.data[indexPath.section];
    MJSettingItem *item = group.items[indexPath.row];
    
    if (item.option) { // block有值(点击这个cell,.有特定的操作需要执行)
        item.option();
    } else if ([item isKindOfClass:[MJSettingArrowItem class]]) { // 箭头
        MJSettingArrowItem *arrowItem = (MJSettingArrowItem *)item;
        
        // 如果没有需要跳转的控制器
        if (arrowItem.destVcClass == nil) return;
        
        UIViewController *vc = [[arrowItem.destVcClass alloc] init];
        vc.title = arrowItem.title;
        [self.navigationController pushViewController:vc  animated:YES];
    }
}

/**
 *  头部标题
 */
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    MJSettingGroup *group = self.data[section];
    return group.header;
}

/**
 *  底部标题
 */
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
    MJSettingGroup *group = self.data[section];
    return group.footer;
}
@end
复制代码

设置界面实现

实现基类控制器,只需要简单配置要显示的cell的文字,图片,push的控制器或者要执行的block代码。

MJSettingViewController.m

复制代码
//
//  MJSettingViewController.m
//  Lottery
//
//  Created by apple on 15/9/6.
//  Copyright (c) 2015年 weconex. All rights reserved.
//

#import "MJSettingViewController.h"
#import "MJSettingArrowItem.h"
#import "MJSettingSwitchItem.h"
#import "MJSettingGroup.h"
#import "MJTest1ViewController.h"

@interface MJSettingViewController ()
@end

@implementation MJSettingViewController
/**
 *  第1组数据
 */
- (void)setupGroup1
{
    MJSettingItem *update = [MJSettingArrowItem itemWithIcon:@"MoreUpdate" title:@"检查新版本"];
    update.option = ^{
        // 弹框提示
        NSLog(@"正在检查更新");
    };
    MJSettingItem *help = [MJSettingArrowItem itemWithIcon:@"MoreHelp" title:@"帮助" destVcClass:[MJTest1ViewController class]];
    MJSettingItem *share = [MJSettingArrowItem itemWithIcon:@"MoreShare" title:@"分享" destVcClass:[MJTest1ViewController class]];
    MJSettingItem *viewMsg = [MJSettingArrowItem itemWithIcon:@"MoreMessage" title:@"查看消息" destVcClass:[MJTest1ViewController class]];
    MJSettingItem *product = [MJSettingArrowItem itemWithIcon:@"MoreNetease" title:@"产品推荐" destVcClass:[MJTest1ViewController class]];
    MJSettingItem *about = [MJSettingArrowItem itemWithIcon:@"MoreAbout" title:@"关于" destVcClass:[MJTest1ViewController class]];
    
    MJSettingGroup *group = [[MJSettingGroup alloc] init];
    group.items = @[update, help, share, viewMsg, product, about];
    [self.data addObject:group];
}

/**
 *  第0组数据
 */
- (void)setupGroup0
{
    MJSettingItem *pushNotice = [MJSettingArrowItem itemWithIcon:@"MorePush" title:@"推送和提醒" destVcClass:[MJTest1ViewController class]];
    MJSettingItem *handShake = [MJSettingSwitchItem itemWithIcon:@"handShake" title:@"摇一摇机选"];
    MJSettingItem *soundEffect = [MJSettingSwitchItem itemWithIcon:@"sound_Effect" title:@"声音效果"];
    
    MJSettingGroup *group = [[MJSettingGroup alloc] init];
    group.items = @[pushNotice, handShake, soundEffect];
    [self.data addObject:group];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 1.标题
    self.title = @"设置";
    
    // 2.添加数据
    [self setupGroup0];
    [self setupGroup1];
}
@end
复制代码

效果如下:

 

系统源码下载:点击下载

 

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示