MVVM+RAC简单使用教程
来源:互联网 发布:js两个对象数组合并 编辑:程序博客网 时间:2024/06/14 22:59
MVVM+RAC简单使用教程
既然是简单使用那就不讲那些概念了。什么是MVC、MVP、MVVM这些就忽略啦!很多大神写了很多,有关他们的优缺点都写的非常的好,我就没必要再写一遍了。
直接开始写怎么开始一个 MVVM+RAC 的教程了。
我喜欢在写代码的时候先写思路,这样就顺便把注释也写了。
写Demo和下厨是一样一样的,得有先材料。
那么写这个也要准备一下材料:
1. 导入ReactiveCocoa框架。
这里没有两个版本一个OC和一个Swift版本。如果你的项目是OC的你没必要在把Swift的下载下来,而且还要配置不然工程会报错。
Swift:
use_frameworks! target 'Target名称' do pod 'ReactiveCocoa', '5.0.0-alpha.3' end
OC:
use_frameworks! target 'Target名称' do pod 'ReactiveObjC', '~> 2.1.0' end
我用的是OC版本,网络我用到了AFNetworking
use_frameworks! target 'Target名称' do pod 'ReactiveObjC', '~> 2.1.0' pod 'AFNetworking', '~> 3.0' end
2. New一个MVVM项目 我们就由一个简单的登录业务模块开始,然后跳转到一个网络加载的数据列表页,点击Cell跳转页面。这样一个小的Demo,也比较容易去理解整个MVVM和RAC的使用。
新建工程后:
2.1 新建一个MVVM-prefix.pch 文件
#import <ReactiveObjC/ReactiveObjC.h> #import <AFNetworking/AFNetworking.h>
因为是Demo所以就这么写了。
2.2新建一个 model 用来当信号里的属性 用来监听信号
#import <Foundation/Foundation.h>@interface AccountModel : NSObject@property(nonatomic,strong)NSString *userName;@property(nonatomic,strong)NSString *pwd;@end
2.3 新建一个 ViewModel
#import <Foundation/Foundation.h>#import "AccountModel.h"@interface LoginViewModel : NSObject@property(nonatomic,strong)AccountModel *account;@property(nonatomic,strong)RACSignal *listeningSignal;@property(nonatomic,strong)RACSubject *btnColorSubject; @property(nonatomic,strong)RACCommand *loginCommand;@property(nonatomic,strong)UINavigationController *nav;@end#import "LoginViewModel.h"#import "ListViewController.h"@implementation LoginViewModel-(AccountModel*)account{ if (!_account) { _account = [[AccountModel alloc]init]; } return _account;}-(instancetype)init{ if (self=[super init]) { [self initalBind]; } return self;}-(void)initalBind{ ////创建信号 通过combinelatest 讲两个型号组合一起 通过一个宏RACObserve 来观察Account 里面的 属性的变化 _listeningSignal = [RACSignal combineLatest:@[RACObserve(self.account, userName),RACObserve(self.account, pwd)] reduce:^id(NSString *account,NSString *pwd){ ////这里就判断 发送哪个信号出去 用来改变颜色// if (account.length && pwd.length) {// [self.btnColorSubject sendNext:@1];// } else {// [self.btnColorSubject sendNext:@2];// } return @(account.length&&pwd.length); }]; ////创建一个RACCommand 事件 // 1.signalBlock必须要返回一个信号,不能传nil. // 2.如果不想要传递信号,直接创建空的信号[RACSignal empty]; // 3.RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。 // 4.RACCommand需要被强引用,否则接收不到RACCommand中的信号,因此RACCommand中的信号是延迟发送的。 _loginCommand = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) { RACSignal *loginSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { ////这里就是模拟网络的一个block [Netwrok getData:^(NSString *str) { [subscriber sendNext:str]; [subscriber sendCompleted]; }] ; return [RACDisposable disposableWithBlock:^{ NSLog(@"信号销毁"); }]; }]; return loginSignal; }]; ////这里就是订阅command 里面的信号 根据不同信号来跳转 和 处理业务 [_loginCommand.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) { NSLog(@"%@",x); if ([x isEqualToString:@"成功"]) { ListViewController *vc = [[ListViewController alloc]init]; [self.nav pushViewController:vc animated:YES]; } }]; ////这里就是订阅 command 里面的 信号 使用skip 是过滤第一个信号因为默认会调用一次 [[_loginCommand.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) { NSLog(@"%@",x); if ([x isEqual:@(YES)]) { NSLog(@"正在登录"); }else{ NSLog(@"%@",x); } }]; } @end
2.4 最简单的 VC 了
#import "ViewController.h"#import "LoginViewModel.h" @interface ViewController ()@property(nonatomic,strong)LoginViewModel *viewModel;@property (weak, nonatomic) IBOutlet UITextField *nameTextField;@property (weak, nonatomic) IBOutlet UITextField *pwdTextField;@property (weak, nonatomic) IBOutlet UIButton *loginBtn;@end@implementation ViewController-(LoginViewModel*)viewModel{ if (!_viewModel) { _viewModel = [[LoginViewModel alloc]init]; } return _viewModel;}- (void)viewDidLoad { [super viewDidLoad]; ////这里就是监听 每个控件值的变化 RAC(self.viewModel.account,userName) = self.nameTextField.rac_textSignal; RAC(self.viewModel.account,pwd) = self.pwdTextField.rac_textSignal; RAC(self.loginBtn,enabled) = self.viewModel.listeningSignal; ///这里是监听按钮的颜色 是有可以点击的时候改变颜色 ///1. 这种方式是直接 监听按钮的一个enabled 属性,是否可以点击 [RACObserve(self.loginBtn, enabled) subscribeNext:^(id x) { if ([x isEqual:@1]) { self.loginBtn.backgroundColor = [UIColor greenColor]; } else { self.loginBtn.backgroundColor = [UIColor redColor]; } NSLog(@"是否可以点击%@",x); }]; ////2. 这种是再viewmodel 里面再创建一个RACSubject 信号 用来发送信号以便改变颜色// self.viewModel.btnColorSubject = [RACSubject subject];// [self.viewModel.btnColorSubject subscribeNext:^(id _Nullable x) {// if ([x isEqual:@1]) {// self.loginBtn.backgroundColor = [UIColor greenColor];// } else {// self.loginBtn.backgroundColor = [UIColor redColor];// }// NSLog(@"----%@",x);// }]; /*这里就是 导航栏 放到ViewModel里面。也有人认为放到C里面去跳转。我个人比较倾向在ViewModel里面去跳转,既然ViewModel里面都是出来业务逻辑,那么跳转也应该属于其中一部分。*/ self.viewModel.nav = self.navigationController; ////监听按钮的点击事件,当按钮收到可以点击事件的时候 LoginCommand 就可以执行 execute 执行命名 [[_loginBtn rac_signalForControlEvents:1<<6] subscribeNext:^(__kindof UIControl * _Nullable x) { [self.viewModel.loginCommand execute:nil]; }]; } @end
2.5 创建一个 NetWork 用来获取数据
#import <Foundation/Foundation.h> typedef void(^NetWorkBlock)(NSString *str);typedef void(^NetWorkDataBlock)(id data); @interface Netwrok : NSObject+(void)getData:(NetWorkBlock)block;+(void)getListData:(NetWorkDataBlock)block;@end#import "Netwrok.h" @implementation Netwrok +(void)getData:(NetWorkBlock)block{ block(@"成功");}+(void)getListData:(NetWorkDataBlock)block{ NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; parameters[@"q"] = @"基础"; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; [manager GET:@"https://api.douban.com/v2/book/search" parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { block(responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { }];}
2.6 创建一个 BookModel 用来传当cell里面的数据模型
@interface BookModel : NSObject@property(nonatomic,strong)NSString *subtitle;@property(nonatomic,strong)NSString *author;@property(nonatomic,strong)NSString *pubdate;@property(nonatomic,strong)NSString *image;@property(nonatomic,strong)NSString *binding;@property(nonatomic,strong)NSString *catalog;+(BookModel*)bookWithDict:(NSDictionary*)dict;@end@implementation BookModel+(BookModel*)bookWithDict:(NSDictionary *)dict{ BookModel *book = [[BookModel alloc]init]; book.subtitle = dict[@"subtitle"]; book.author = dict[@"author"]; book.pubdate = dict[@"pubdate"]; book.image = dict[@"image"]; book.catalog = dict[@"catalog"]; return book;}@end
#import "BookModel.h"@interface BookTableViewCell : UITableViewCell@property(nonatomic,strong)UILabel *titleLabel;@property(nonatomic,strong)UILabel *dextLabel;@property(nonatomic,strong)BookModel *book;+(instancetype)cellWithTableView:(UITableView*)tableView;@end@implementation BookTableViewCell+(instancetype)cellWithTableView:(UITableView*)tableView{ static NSString *identfier = @"BookTableViewCell"; //先在缓存池中取 BookTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identfier]; //缓存池中没有再创建,并添加标识,cell移出屏幕时放入缓存池以复用 if (cell == nil) { cell = [[BookTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identfier]; } return cell; }-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self=[super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 200, 20)]; [self.contentView addSubview:self.titleLabel]; self.dextLabel= [[UILabel alloc]initWithFrame:CGRectMake(0, 20, 200, 20)]; [self.contentView addSubview:self.dextLabel]; } return self;}-(void)setBook:(BookModel *)book{ _book = book; self.titleLabel.text = book.subtitle; self.dextLabel.text = book.pubdate;} @end
2.7 创建一个 ListViewModel 处理数据返回 将数据展示V上
@interface ListViewModel : NSObject <UITableViewDataSource,UITableViewDelegate>@property(nonatomic,strong)RACCommand *requeseCommand;@property(nonatomic,strong)NSArray *models;@property(nonatomic,strong)RACSubject *subject;@property(nonatomic,strong)UINavigationController *nav; @end#import "ListViewModel.h"#import "Network.h"#import "BookModel.h"#import "BookTableViewCell.h" @implementation ListViewModel -(instancetype)init{ if (self = [super init]) { [self initabBind]; } return self;} -(void)initabBind{ _requeseCommand = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) { RACSignal *requestSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [Network getList:^(id data) { [subscriber sendNext:data]; [subscriber sendCompleted]; }]; return [RACDisposable disposableWithBlock:^{ NSLog(@"信号销毁"); }]; }]; return [requestSignal map:^id _Nullable(id _Nullable value) { NSMutableArray *dictArray = value[@"books"]; NSArray *modelArray = [[dictArray.rac_sequence map:^id _Nullable(id _Nullable value) { return [BookModel bookWithDict:value]; }]array]; return modelArray; }]; }];}-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.models.count;}-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ BookTableViewCell *cell = [BookTableViewCell cellWithTableView:tableView]; cell.book=self.models[indexPath.row]; return cell;}-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ BookModel *book= self.models[indexPath.row]; UIViewController *vc = [[UIViewController alloc]init]; vc.title=book.subtitle; vc.view.backgroundColor = [UIColor whiteColor]; [self.nav pushViewController:vc animated:YES];}-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 50;} @end
2.8创建C 装载视图 让后执行Command 命名刷新表格数据
#import "ListViewController.h"#import "ListViewModel.h"#import "BookModel.h"@interface ListViewController ()@property(nonatomic,strong)UITableView *tableView;@property(nonatomic,strong)ListViewModel *requestViewModel;@end@implementation ListViewController-(ListViewModel*)requestViewModel{ if (!_requestViewModel) { _requestViewModel = [[ListViewModel alloc]init]; } return _requestViewModel;}- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor=[UIColor whiteColor]; self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; self.tableView.dataSource = self.requestViewModel; self.tableView.delegate=self.requestViewModel; self.requestViewModel.nav = self.navigationController; [self.view addSubview:self.tableView]; RACSignal *requestSiganl = [self.requestViewModel.requeseCommand execute:nil]; [requestSiganl subscribeNext:^(id _Nullable x) { self.requestViewModel.models = x; [self.tableView reloadData]; }];}
在此所有的代码都在这里了,写完你会发现结构确实要比MVC清晰多了。各项职能都很明确。
当然怎么去选择都是根据具体业务去实现,业务简单的完全就可以MVC了。没必要写成这样,复杂一点的,就可以使用MVVM让你的结构变得更加清晰。
阅读全文
0 0
- MVVM+RAC简单使用教程
- MVVM+RAC
- MVVM+RAC
- MVVM+RAC
- MVVM的简单使用
- MVVM的简单使用
- MVVM架构简单使用
- MVVM架构简单使用
- MVVM的简单使用流程
- RAC And MVVM (一)
- iOS架构:MVVM+RAC
- RAC + MVVM 2
- RAC + MVVM 1
- MVVM + RAC 实践
- 九,Runtime&mvvm&Rac
- iOS 浅谈MVVM+RAC
- MVVM和RAC介绍
- RAC--MVVM 心得
- 程序员如何快速成长
- 编程规范
- 第十三周 【项目1
- 链表、队列、栈和字符串的相关应用(三)字符串的旋转
- 新厂商磨拳切入智能车联网,但可靠性关口怎么跨?---凯利讯半导体
- MVVM+RAC简单使用教程
- 遗传算法
- CocoaPods按装
- 第十三周项目一(1)——验证折半查找算法
- 使用微信小程序自定义组件实现的tabs选项卡功能
- Linux中rename命令
- 编译easydss遇到stray '\239' inprogram,stray '\187' inprogram,stray '\191' inprogram的解决办法
- QML 之 Shared JavaScript Resources
- zynq Boot 寻址空间