前言
在上一篇中自己尝试了运用RAC+MVVM方式写登录界面
RAC运用系列(五) MVVM 实现登录界面
在登录注册界面等类似输入控件占多的界面应该是可以用RAC的基本用法来解决了的。现在尝试在tableView的写法。之前在网上看到用RXSwift才能更好的体现RAC的妙用,用RAC写MVVM会有点别扭,当然在公司里除非是新项目用swift,大多数是更新迭代公司的产品app都运行好几年的了用的是OC。
需求
app中很大部分界面是TableView控件的使用,一般TableView界面长这样如下,也是今天我想要实现的,如果MVC来写的话很简单。
这里主要有以下几点
- viewModel中数据源的处理
- 请求加载时转转的显示处理
- tableViewcell 中图片的加载,点击事件
- tableView的上下拉刷新
实现
MVVM文件夹
1.1DBViewModel
这里用的是豆瓣的开发Api,因为要实现上下拉翻页,请求数据的处理是在DBViewModel中,而我下拉刷新是写在VC里,实现如下
@implementation DBViewModel
-(instancetype)init
{
if (self = [super init]) {
[self initViewModel];
}
return self;
}
-(void)initViewModel
{
//原来是想这样处理的,后面发现上拉加载更多时需要对data进行数据合并所以改为下面的方式了。
// RAC(self,dataSource) = self.netCommand.executionSignals.switchToLatest;
self.didClickedSubject = [RACSubject subject];
self.page = 0;
self.refreshSubject = [RACSubject subject];
@weakify(self);
[self.netCommand.executionSignals.switchToLatest subscribeNext:^(NSArray * x) {
@strongify(self);
if (self.page == 0) {
self.dataSource = x;
}else{
if ( self.dataSource.count == 0) {
return ;
}
NSArray * result = [[ @[self.dataSource.rac_sequence,x.rac_sequence].rac_sequence flatten] array];
NSLog(@"%@",result);
self.dataSource = result;
}
//让VC去停止刷新
[self.refreshSubject sendNext:@1];
}error:^(NSError * _Nullable error) {
//让VC去停止刷新
[self.refreshSubject sendNext:@1];
}];
}
-(RACCommand *)netCommand
{
if (_netCommand == nil) {
_netCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
return [PANetTools requestDoubanWithPage:self.page] ; //封的数据请求
}];
}
return _netCommand;
}
细节:
1.RACCommand的使用
2.上拉加载更多时,数据的合并[[ @[self.dataSource.rac_sequence,x.rac_sequence].rac_sequence flatten] array];
3.点击信号及刷新信号RACSubject
1.2 DBMovieTableViewCell
这里本想用信号在cell 与model之间属性绑定的,但那样复杂了。所以直接viewmodel传进来赋值,图片加载用SDWebImage:
-(void)bindViewModel:(DBViewModel *)viewModel indexPath:(NSIndexPath *)indexPath
{
if (viewModel.dataSource.count > 0) {
self.viewModel = viewModel;
DBMoveModel * model = viewModel.dataSource[indexPath.row];
self.movieModel = model;
NSDictionary * imageDict = model.images;
if (imageDict[@"small"]) {
NSString * imageString = imageDict[@"small"];
[self.iconImageView sd_setImageWithURL:[NSURL URLWithString:imageString]];
}
self.titleLabel.text = model.title;
self.subTitleLabel.text = model.subtype;
}
}
cell中点击事件的信号传递
-(void)bindRAC
{
@weakify(self);
[[self.clickBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);
[self.viewModel.didClickedSubject sendNext:self.viewModel];
}];
}
1.3 controller
UI布局,viewmodel绑定,MJ上下拉刷新
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
[self layoutUI];
[self bindViewModel];
@weakify(self);
self.table.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
@strongify(self);
self.viewModel.page = 0;
[self.viewModel.netCommand execute:nil];
}];
self.table.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
@strongify(self);
self.viewModel.page = self.viewModel.page + 1;
[self.viewModel.netCommand execute:nil];
}];
}
绑定viewmodel
-(void)bindViewModel
{
[self.viewModel.netCommand execute:nil];
@weakify(self);
[[self.viewModel.netCommand.executing skip:1] subscribeNext:^(NSNumber * x) {
@strongify(self);
if (x.boolValue) {
NSLog(@"显示菊花...");
[CMTipsView showProgressHud:@"数据加载中" addToView:self.view];
}else{
NSLog(@"隐藏菊花...");
[CMTipsView hideHUDForView:self.view];
}
}];
[self.viewModel.refreshSubject subscribeNext:^(id _Nullable x) {
if ([self.table.mj_header isRefreshing]) {
[self.table.mj_header endRefreshing];
}
if ([self.table.mj_footer isRefreshing]) {
[self.table.mj_footer endRefreshing];
}
[self.table reloadData];
} ];
//上面按钮被点击
[self.viewModel.didClickedSubject subscribeNext:^(DBViewModel * viewmodel) {
NSLog(@"%@",viewmodel);
TestViewController * testVC = [[TestViewController alloc] init];
[self.navigationController pushViewController:testVC animated:YES];
}];
}
最后
运行的图片就是这样子的。demo地址:ReactiveObjCDemo