Controller fat, how to lose weight?

Introduction: The need to build UI below presented to the user, the business logic (1. Tap head needs to determine the user level 2. Click the different operations into the cell may carry data need to request details page 3. Background logoff the user authentication information, and determine whether the balance is zero, if there are coupons, the final request for cancellation of the background).

A network request, parses the data extracted. Using block direct callback model

1. Create a new class PersonalCenterService, specialized processing network requests and callback model. analytical model using third-party YYModel. Data structure into two portions, the head returns a dictionary, a second part array. Note: Data are themselves built, this process is only simulated network request process.

#import "PersonalCenterService.h"
#import "PersonalCenterModel.h"

@implementation PersonalCenterService

+ (void)getPersonalCenterData:(void(^)(PersonalCenterModel *,NSArray *))successBlock
{
    //模拟网络请求-后台返回数据
    NSDictionary *dictionary = @{
                                 @"head":@{
                                         @"name":@"怪咖君",
                                         @"autograph":@"人生得意须尽欢,莫使金樽空对月",
                                         @"grade":@"青铜",
                                         @"money":@"100",
                                         @"blind":@"2",
                                         @"cardCoupon":@"2",
                                         },
                                 @"rowInfomation":@[
                                         @{@"title":@"支付"},
                                         @{@"title":@"收藏"},
                                         @{@"title":@"卡劵"},
                                         @{@"title":@"用户反馈"},
                                         @{@"title":@"系统设置"},
                                         ]
                          };
    
    //数据解析
    NSDictionary *head = [dictionary objectForKey:@"head"];
    //头部model
    PersonalCenterModel*model =  [PersonalCenterModel  yy_modelWithDictionary:head];
    
    //section->model利用YYModel解析装入数组、便于操作
    NSArray *rowInfomation = [dictionary objectForKey:@"rowInfomation"];
    NSArray *dataArray = [NSArray yy_modelArrayWithClass:[personalSecontionModel class] json:rowInfomation];
    
    successBlock(model,dataArray);

}

@end

复制代码

2.controller, the direct callback block, refresh the data

[PersonalCenterService getPersonalCenterData:^(PersonalCenterModel * headModel,NSArray *sectionDataArray) {
   //接收数据
   self.headModel = headModel;
   self.sectionArray = sectionDataArray;
   //刷新数据
    [self.tableView reloadData];
}];
    
复制代码

Second, the use of inheritance, encapsulation, category unitized manner

1.cell subclass

Custom TableViewCell, sub-layout view in full into the cell, cellforRow simplify the code, create a child without any view and add target event.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *identifier = @"TableViewCell";
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[TableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
    }
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.model = self.dataArray[indexPath.section];
    return cell;
}
复制代码

This still looks a little more, our custom BaseCell, cell creation code into BaseCell in. cell-related property is also placed in the sub-cell. After it becomes the following code, clear and concise.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TableViewCell *cell = [TableViewCell cellWithTableView:tableView];
    cell.model = self.dataArray[indexPath.section];
    return cell;
}

复制代码

We can package as BaseCell

/**
 *  创建单元格
 */
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
    NSString *className = NSStringFromClass([self class]);
    NSString *identifier = [NSString stringWithFormat:@"%@",className];
    
    BaseTableViewCell *baseCell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if(baseCell == nil){
        baseCell = [[self alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    return baseCell;
}
复制代码

2.HeadView subclass

HeadViev we can be encapsulated into a UIView subclass, as tableViewHeadView handle just fine. Click the event you can use target-action or other treatment agents and block, where I used the block, clear and concise.

- (UIView*)headView
{
    PersonalCenterView *centerView = [[PersonalCenterView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 100) clickBlock:^{
    //业务操作-根据model判断等级 进行不同的的处理
    if ([self.headModel.grade isEqualToString:@"青铜"])
    {
      [ASRequestHUD showErrorWithStatus:@"您还不具备修改资格,努力升级吧"];
    }else if ([self.headModel.grade isEqualToString:@"黄金"])
    {
        //逻辑处理
        
    }else
    {
        //钻石
        
    }
  }];
    centerView.model = self.headModel;
    return centerView;
}
复制代码

3. Data show into view in

Data show, set the method of replication model like (we used methods, apple of MVC architecture model and View are unable to communicate)

//headView
- (void)setModel:(PersonalCenterModel *)model
{
    self.nameLabel.text = model.name;
    self.autographLabel.text = model.autograph;
}

//cell
- (void)setModel:(personalSecontionModel *)model
{
    self.titleLabel.text = model.title;
}
复制代码

4. Packaging Tools

For empty string sentence, as well as prompt information can be encapsulated. Word code calls like. The following is a write-off simulation. ASRequestHUD string and an empty judgment, to acquire all of the tools currently packaged controller. Note: The example used here logoff process, but also to reflect Controller processing business logic code that would take up too much

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"验证信息" message:nil preferredStyle:UIAlertControllerStyleAlert];
   
// 添加文本框
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
    textField.placeholder = @"请输入姓名";
}];
   
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
    textField.placeholder = @"请输入身份证号码";
}];
  
UIAlertAction * firstAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
    UITextField *name= alertController.textFields.firstObject;
    UITextField *idCard= alertController.textFields.lastObject;
    if ([NSString isEmptString:name.text]&&[NSString isEmptString:idCard.text]) {
        [ASRequestHUD showErrorWithStatus:@"信息输入不完整~"];
            return ;
        }
        //网络请求1-假设请求成功 处理业务
        //网络请求2-假设请求成功 处理业务
    if ([NSString isEmptString:model.money]) {
        [ASRequestHUD showErrorWithStatus:@"有余额未体现,请提现后注销"];
        return;
    }else if ([NSString isEmptString:model.blind])
    {
        [ASRequestHUD showErrorWithStatus:[NSString stringWithFormat:@"您还有%@张卡未解除,请前往解除绑定",model.blind]];
        return;
    }else if ([NSString isEmptString:model.cardCoupon])
    {
        [ASRequestHUD showErrorWithStatus:[NSString stringWithFormat:@"您还有%@张优惠卷未使用,请前往删除或使用",model.cardCoupon]];
        return;
    }else
    {
        //网络请求3-注销-假设请求成功
        //假设需要刷新数据
        [self.tableView reloadUI];
    }];
 
    UIAlertAction *secondAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil];
    [alertController addAction:firstAction];
    [alertController addAction:secondAction];
   
复制代码

Third, the UITableViewDataSource UITableView extracted from the Controller and UITableViewDelegate

By extracting a series of the above method in a network request or the like, Controller number is thin. However, due to too many UITableView agreement, still bloated. TableView agreement now needs to be stripped out, the controller becomes a clean up. Data Sources achieve agreement or the core, so we only incoming data sources, the problem is solved.

1. Define class TableViewData

#import "TableViewDataSource.h"
#import "TableViewCell.h"

@implementation TableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.dataArray.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TableViewCell *cell = [TableViewCell cellWithTableView:tableView];
    cell.model = self.dataArray[indexPath.section];
    return cell;
}

@end
复制代码

2. Define TableViewDelegate class

@interface TableViewDelegate()

@property(nonatomic ,strong)PresenterPesonalCenter *present;

@end

@implementation TableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //逻辑业务处理-进入详情页面
    personalSecontionModel *model = self.dataArray[indexPath.section];
    PersonalCenterDeatilController *controller = [PersonalCenterDeatilController new];
    controller.title = model.title;
    [[self  jsd_getCurrentViewController].navigationController pushViewController:controller animated:YES];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 50;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 10;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    return 0;
}

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return nil;
}

- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    return nil;
}

@end
复制代码

As long as the code is like 3.Controller

//初始化协议
tableDataSource = [[TableViewDataSource alloc]init];
tableDelegate = [[TableViewDelegate alloc]init];
    
//请求数据
[PersonalCenterService getPersonalCenterData:^(PersonalCenterModel * headModel,NSArray *sectionDataArray) {
    self.headModel = headModel;
    tableDataSource.dataArray = sectionDataArray;
    tableDelegate.dataArray = sectionDataArray;
    [self.tableView reloadData];
}];

复制代码

Fourth, define the protocol to extract business logic to Prsenter class, controller and view only responsible for the UI layout and display.

After more than three steps after, Controller has reached a slimming effect. But if too many actual business logic development, Controller it is still very fat. In this case we need to put the Controller to extract business logic to Prsenter class, Controller responsible for UI display, layout, no longer cares about business operations. The idea here is to expand the use of the MVC based on the MVP, we define protocols for class, let Prsenter class to implement, Controller just like to communicate with Presenter

1. Protocol section

#import <Foundation/Foundation.h>
#import "PersonalCenterModel.h"

NS_ASSUME_NONNULL_BEGIN

@protocol PersonalCenterProtocol <NSObject>

@optional

/***
 定义协议、所有逻辑业务都让Present类去处理、controller和view只负责UI布局和数据显示
 **/

//注销-prsenter实现
- (void)cancellation:(PersonalCenterModel*)model;

//刷新UI->tableView----->让controller去实现(比较方便-为了清晰、明了也可以单独给controller定义协议类)
- (void)reloadUI;

//点击头部-进入编辑页面-prsenter实现
- (void)clickHead:(PersonalCenterModel*)model;

//cell点击事件-进入详情页面并携带参数-prsenter实现
- (void)enterDetailPage:(personalSecontionModel*)model;

@end

NS_ASSUME_NONNULL_END
复制代码

2.Prsenter processing

The method implements the protocol, business logic. Prsenter like a middleman, to get data from the model, notify the view to update the data. model and view independent of each other.

#import "PresenterPesonalCenter.h"
#import "PersonalCenterViewController.h"
#import "PersonalCenterDeatilController.h"

@implementation PresenterPesonalCenter

//实现协议方法 注销账号&&点击头部进入想象
- (void)cancellation:(PersonalCenterModel*)model
{
    PersonalCenterViewController *controller = (PersonalCenterViewController*)[self jsd_getCurrentViewController];

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"验证信息" message:nil preferredStyle:UIAlertControllerStyleAlert];
   
    // 添加文本框
    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
        textField.placeholder = @"请输入姓名";
    }];
   
    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
        textField.placeholder = @"请输入身份证号码";
    }];
  
    UIAlertAction * firstAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
        UITextField *name= alertController.textFields.firstObject;
        UITextField *idCard= alertController.textFields.lastObject;
        if ([NSString isEmptString:name.text]&&[NSString isEmptString:idCard.text]) {
            [ASRequestHUD showErrorWithStatus:@"信息输入不完整~"];
            return ;
        }
        //网络请求1-假设请求成功 处理业务
        //网络请求2-假设请求成功 处理业务
        if ([NSString isEmptString:model.money]) {
            [ASRequestHUD showErrorWithStatus:@"有余额未体现,请提现后注销"];
            return;
        }else if ([NSString isEmptString:model.blind])
        {
            [ASRequestHUD showErrorWithStatus:[NSString stringWithFormat:@"您还有%@张卡未解除,请前往解除绑定",model.blind]];
            return;
        }else if ([NSString isEmptString:model.cardCoupon])
        {
            [ASRequestHUD showErrorWithStatus:[NSString stringWithFormat:@"您还有%@张优惠卷未使用,请前往删除或使用",model.cardCoupon]];
            return;
        }else
        {
            //网络请求3-注销-假设请求成功
            //假设需要刷新数据
            if (controller &&[controller respondsToSelector:@selector(reloadUI)]) {
                [controller reloadUI];
            }
        }
        
    }];
 
    UIAlertAction *secondAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil];
    [alertController addAction:firstAction];
    [alertController addAction:secondAction];
   
    [controller presentViewController:alertController animated:YES completion:nil];
}

- (void)clickHead:(PersonalCenterModel*)model
{
    if ([model.grade isEqualToString:@"青铜"])
    {
        [ASRequestHUD showErrorWithStatus:@"您还不具备修改资格,努力升级吧"];
    }else if ([model.grade isEqualToString:@"黄金"])
    {
        //逻辑处理
        
    }else
    {
        //钻石
        
    }
}

- (void)enterDetailPage:(personalSecontionModel *)model
{
    //进入详情页面
    PersonalCenterDeatilController *controller = [PersonalCenterDeatilController new];
    controller.title = model.title;
    [[self  jsd_getCurrentViewController].navigationController pushViewController:controller animated:YES];
}

复制代码

3.controller processing

Click on the event, specify that the presenter to realize just fine

//头部点击事件
if (weakSelf.presenter&&[weakSelf.presenter respondsToSelector:@selector(clickHead:)]) {
    [weakSelf.presenter clickHead:weakSelf.headModel];
}

//注销点击
if (self.presenter&&[self.presenter respondsToSelector:@selector(cancellation:)]) {
    [self.presenter cancellation:self.headModel];
}

cell中点击进入详情
self.present = [[PresenterPesonalCenter alloc]init];
personalSecontionModel *model = self.dataArray[indexPath.section];
    
if (self.present&&[self.present respondsToSelector:@selector(enterDetailPage:)]) {
    [self.present enterDetailPage:model];
}

复制代码

V. Summary

1. By the above process, the controller gentle smile, said to himself, "I finally slim down," I'm going to look in the mirror.

#import "PersonalCenterViewController.h"
#import "PersonalCenterView.h"
#import "PersonalCenterService.h"

#import "TableViewDataSource.h"
#import "TableViewDelegate.h"

#import "PresenterPesonalCenter.h"

@interface PersonalCenterViewController ()
{
    TableViewDataSource *tableDataSource;
    TableViewDelegate *tableDelegate;
}

@property(strong ,nonatomic)PresenterPesonalCenter *presenter;
@property(strong ,nonatomic)UITableView *tableView;
@property(strong ,nonatomic)PersonalCenterModel *headModel;
@property(strong,nonatomic)UIButton *footButton;

@end

@implementation PersonalCenterViewController

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"个人中心";
    
    //初始化协议
    tableDataSource = [[TableViewDataSource alloc]init];
    tableDelegate = [[TableViewDelegate alloc]init];
    
    //请求数据
    [PersonalCenterService getPersonalCenterData:^(PersonalCenterModel * headModel,NSArray *sectionDataArray) {
        self.headModel = headModel;
        tableDataSource.dataArray = sectionDataArray;
        tableDelegate.dataArray = sectionDataArray;
        [self.tableView reloadData];
    }];
    
    //tableView
    [self.view addSubview:self.tableView];
    
    self.presenter = [[PresenterPesonalCenter alloc]init];
    
}

- (UITableView*)tableView
{
    if (!_tableView) {
        _tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, KNavBarHeight, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-KNavBarHeight) style:(UITableViewStyleGrouped)];
        _tableView.dataSource = tableDataSource;
        _tableView.delegate = tableDelegate;
        _tableView.tableHeaderView = [self headView];
        _tableView.tableFooterView = [self footView];
    }
    return _tableView;
}

- (UIView*)headView
{
    WeakSelf;
    PersonalCenterView *centerView = [[PersonalCenterView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 100) clickBlock:^{
        //操作
        if (weakSelf.presenter&&[weakSelf.presenter respondsToSelector:@selector(clickHead:)]) {
            [weakSelf.presenter clickHead:weakSelf.headModel];
        }
    }];
    centerView.model = self.headModel;
    return centerView;
}

- (UIView*)footView
{
    UIButton*(^creatButton)(NSString*,CGRect) = ^(NSString *title,CGRect frame)
    {
        UIButton *button = [[UIButton alloc] initWithFrame:frame];
        button.backgroundColor = K_WhiteColor;
        [button setTitle:title forState:UIControlStateNormal];
        [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [button addTarget:self action:@selector(cancellation) forControlEvents:UIControlEventTouchUpInside];
        return button;
    };
    UIView *footView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 100)];
    footView.backgroundColor = [UIColor groupTableViewBackgroundColor];
    
    UIButton *footbutton = creatButton(@"注销",CGRectMake(0, 0, kScreenWidth, 50));
    footbutton.center = footView.center;
    
    [footView addSubview:footbutton];
    
    return footView;
}

//注销方法
- (void)cancellation
{
    if (self.presenter&&[self.presenter respondsToSelector:@selector(cancellation:)]) {
        [self.presenter cancellation:self.headModel];
    }
}

#pragma mark-实现协议方法
- (void)reloadUI {
    //假设需求需要清空数据-更新UI
    tableDataSource.dataArray = @[];
    [self.tableView reloadData];
}

- (void)dealloc
{
    NSLog(@"控制器释放了!!!!!!!!");
}

@end

复制代码

2.Prsenter back a laugh, really good woman, but a hundred also. Heroes square in the world, as I also sympathizing with. Controller is no longer found himself a man of the world.

3.MVVM by way binding, the Controller can also achieve weight-loss effect. Learning higher costs than the first two, there are also disadvantages. Personally I think that according to the actual business scenarios, not particularly complex page, you can use MVC MVC, the code is simple, straightforward enough. Finally, attach the structure of FIG.

Reproduced in: https: //juejin.im/post/5d004eafe51d4510bf1d665f

Guess you like

Origin blog.csdn.net/weixin_33943347/article/details/93178700