给UIViewController瘦身之MVP

大胖子

iOS开发的人都会知道,MVC模式下,UIViewController会无比笨重!什么界面显示、代理方法、网络请求、都可以往里面塞。
而MVVM还不是很了解,后续做到在写Demo。

MVP

因此借鉴了做Android的同事的做法:MVP。
如果心急,本文最下面有代码传送门

M:Model
V:UIViewController(view)
P:Presenter

前两个好理解,那Presenter呢?
个人理解,作为分担UIViewController重担的角色,把UIViewController中的除了界面显示的其他逻辑,都丢进Presenter中。一个UIViewController对应一个PresenterPresenterUIViewController中实例化的同时,把UIViewController实例传给Presenter

那么UIViewControllerPresenter如何交互。
这里我实现了两种方式,一种是协议,一种是Block。不过Block实现起来更加轻巧。不像协议,需要声明一堆方法。

这里我具体写了一个例子,在一个登陆界面,有信息显示Label,账户输入框TextField,密码输入框TextField,登录按钮Button
按下按钮之后,在Presenter执行类似请求接口进行登录的逻辑。执行登录动作,可能有三种状态,一种是登录中,一种是登录成功,一种是登录失败。那么这三种状态,通过实现协议方法,将各自的数据回传给View

给UIViewController瘦身之MVP_第1张图片
2.png

下面是文件结构:

给UIViewController瘦身之MVP_第2张图片
1.png

以下是两种实现方式的代码:

使用协议实现数据回传

view:

ViewController.h

#import 
#import "MyPresenter.h"

@interface ViewController : UIViewController

//登录信息显示
@property (weak, nonatomic) IBOutlet UILabel *infoLabel;

//账户输入框
@property (weak, nonatomic) IBOutlet UITextField *accountTextF;

//密码输入框
@property (weak, nonatomic) IBOutlet UITextField *passwordTextF;

@end

ViewController.m

#import "ViewController.h"

@interface ViewController (){
    MyPresenter *presenter;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //实例化Presenter对象,将页面的传给Presenter
    presenter = [[MyPresenter alloc]initWithViewController:self];
    presenter.myPresenterDelegate = self;
 
}

//登录事件
- (IBAction)MyBtnClick:(UIButton *)sender{
    [presenter loginWithAccount:_accountTextF.text password:_passwordTextF.text];
}

#pragma mark -MyPresenterDelegate
-(void)logining:(NSString *)info{
    _infoLabel.text = info;
}

-(void)loginSuccess:(NSString *)info{
    _infoLabel.text = info;
}

-(void)loginFailure:(NSString *)info{
    _infoLabel.text = info;
}

//触摸空白地方,键盘隐藏
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [_accountTextF resignFirstResponder];
    [_passwordTextF resignFirstResponder];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Presenter

BasePresenter.h

#import 

@interface BasePresenter : NSObject

@property (nonatomic,strong)UIViewController *vc;

-(instancetype)initWithViewController:(UIViewController*)viewC;

@end

BasePresenter.m

#import "BasePresenter.h"

@implementation BasePresenter

-(instancetype)initWithViewController:(UIViewController*)viewC{
    self = [super init];
    if (self) {
        self.vc = viewC;
    }
    return self;
}

@end

MyPresenter.h

#import "BasePresenter.h"
#import "ViewController.h"


@protocol MyPresenterDelegate//执行完,反馈给界面,传值或者使得界面做出具体操作。

- (void)logining:(NSString*)info;
- (void)loginSuccess:(NSString*)info;
- (void)loginFailure:(NSString*)info;

@end

@interface MyPresenter : BasePresenter

@property(nonatomic,weak)idmyPresenterDelegate;

-(void)loginWithAccount:(NSString*)accuout password:(NSString*)password;


@end

#import "MyPresenter.h"

@interface MyPresenter(){
    ViewController *viewController;
}

@end


@implementation MyPresenter

-(instancetype)initWithViewController:(UIViewController *)viewC{
    self = [super initWithViewController:viewC];
    if (self) {
        viewController= (ViewController*)self.vc;
    }
    return self;
}

-(void)loginWithAccount:(NSString*)accuout password:(NSString*)password{
    NSLog(@"MyPresenter logining...");
    if ([self.myPresenterDelegate respondsToSelector:@selector(logining:)]) {
        [self.myPresenterDelegate logining:@"MyPresenter logining..."];
    }
    //此处模拟网络请求数据,2秒延迟
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后异步执行这里的代码...
        // 登录成功或失败,返回数据给界面(实际应用中,此处写:访问登录接口)
        if ([accuout isEqualToString:@"vampire"]&&[password isEqualToString:@"123"]) {
            if ([self.myPresenterDelegate respondsToSelector:@selector(loginSuccess:)]) {
                [self.myPresenterDelegate loginSuccess:@"login success"];
            }
        }else{
            if ([self.myPresenterDelegate respondsToSelector:@selector(loginFailure:)]) {
                [self.myPresenterDelegate loginFailure:@"login failure"];
            }
        }
        
    });
    
}
@end

使用Block实现数据回传

View

ViewController.h

#import 
#import "MyPresenter.h"

@interface ViewController : UIViewController

//登录信息显示
@property (weak, nonatomic) IBOutlet UILabel *infoLabel;

//账户输入框
@property (weak, nonatomic) IBOutlet UITextField *accountTextF;

//密码输入框
@property (weak, nonatomic) IBOutlet UITextField *passwordTextF;

@end

ViewController.m

#import "ViewController.h"
#define WEAKSELF typeof(self) __weak weakSelf = self;

@interface ViewController (){
    MyPresenter *presenter;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //实例化Presenter对象,将页面的传给Presenter
    presenter = [[MyPresenter alloc]initWithViewController:self];

    [self loginResult];
}

//登录事件
- (IBAction)MyBtnClick:(UIButton *)sender{
//    [presenter loginWithAccount:_accountTextF.text password:_passwordTextF.text];
    
    //方式二:
    
//    WEAKSELF
//    [presenter login2WithAccount:_accountTextF.text password:_passwordTextF.text block:^(NSString *info) {
//        weakSelf.infoLabel.text = info;
//    }];
}

//Block实现
-(void)loginResult{
    WEAKSELF
    presenter.loginResult = ^(NSString *info){
        weakSelf.infoLabel.text = info;
    };
}


//触摸空白地方,键盘隐藏
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [_accountTextF resignFirstResponder];
    [_passwordTextF resignFirstResponder];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Presenter

BasePresenter文件保持不变。

MyPresenter.h

#import "BasePresenter.h"
#import "ViewController.h"

//因为三种登录状态都是传回一个NSString,所以定义一个通用Block
typedef void (^LoginResult)(NSString *info);



@interface MyPresenter : BasePresenter

@property (nonatomic,strong)LoginResult loginResult;//声明Blokc

-(void)loginWithAccount:(NSString*)accuout password:(NSString*)password;

-(void)login2WithAccount:(NSString*)accuout password:(NSString*)password block:(void(^)(NSString *info))block;

@end

MyPresenter.m

#import "MyPresenter.h"

@interface MyPresenter(){
    ViewController *viewController;
}

@end


@implementation MyPresenter

-(instancetype)initWithViewController:(UIViewController *)viewC{
    self = [super initWithViewController:viewC];
    if (self) {
        viewController= (ViewController*)self.vc;
    }
    return self;
}

-(void)loginWithAccount:(NSString*)accuout password:(NSString*)password{
    
    //Block引用
    self.loginResult(@"MyPresenter logining...");
    
    //此处模拟网络请求数据,2秒延迟
    __weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后异步执行这里的代码...
        // 登录成功或失败,返回数据给界面(实际应用中,此处写:访问登录接口)
        if ([accuout isEqualToString:@"vampire"]&&[password isEqualToString:@"123"]) {
            
            weakSelf.loginResult(@"login success");
            
        }else{
            
            weakSelf.loginResult(@"login failure");
        }
    });
}

//当然Block也可以放到方法上作为参数
-(void)login2WithAccount:(NSString*)accuout password:(NSString*)password block:(void(^)(NSString *info))block{
    
    //Block引用
    block(@"MyPresenter logining...");
    
    //此处模拟网络请求数据,2秒延迟
//    __weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后异步执行这里的代码...
        // 登录成功或失败,返回数据给界面(实际应用中,此处写:访问登录接口)
        if ([accuout isEqualToString:@"vampire"]&&[password isEqualToString:@"123"]) {
            
            block(@"login success");
            
        }else{
            
            block(@"login failure");
            
        }
    });
}

@end

可以看到以上例子,Block的声明方式是不同的,具体的写法也是不一样的。

小结

通过以上两个例子,可以发现,Presenter使得UIViewController得以解放,很多逻辑可以写到Presenter中。而逻辑的执行结果,需要返回给UIViewController,所以就需要使用协议或者Block。

代码传送门

MVPDemo
MVPBlockDemo

看懂了的朋友,点个吧!么么哒~~~

你可能感兴趣的:(给UIViewController瘦身之MVP)