大胖子
做iOS
开发的人都会知道,MVC
模式下,UIViewController
会无比笨重!什么界面显示、代理方法、网络请求、都可以往里面塞。
而MVVM还不是很了解,后续做到在写Demo。
MVP
因此借鉴了做Android
的同事的做法:MVP。
如果心急,本文最下面有代码传送门!
M:Model
V:UIViewController(view)
P:Presenter
前两个好理解,那Presenter
呢?
个人理解,作为分担UIViewController
重担的角色,把UIViewController
中的除了界面显示的其他逻辑,都丢进Presenter
中。一个UIViewController
对应一个Presenter
。Presenter
在UIViewController
中实例化的同时,把UIViewController
实例传给Presenter
。
那么UIViewController
跟Presenter
如何交互。
这里我实现了两种方式,一种是协议,一种是Block
。不过Block
实现起来更加轻巧。不像协议,需要声明一堆方法。
这里我具体写了一个例子,在一个登陆界面,有信息显示Label
,账户输入框TextField
,密码输入框TextField
,登录按钮Button
。
按下按钮之后,在Presenter
执行类似请求接口进行登录的逻辑。执行登录动作,可能有三种状态,一种是登录中,一种是登录成功,一种是登录失败。那么这三种状态,通过实现协议方法,将各自的数据回传给View
。
下面是文件结构:
以下是两种实现方式的代码:
使用协议实现数据回传
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
看懂了的朋友,点个吧!么么哒~~~