来看一下MVVM的工作原理图:前言:iOS的MVVM如何工作呢?各自的职责是什么呢?首先我们先来介绍一下纯粹的MVVM架构模式,再来介绍MVVM的双向绑定。
MVVM的通讯关系:
1.View与Model是不直接通讯的。
2.ViewController与Model是不直接通讯的。
3.View只与ViewController/View Model两者发生关系。
4.Model只与View Model通讯。
责任划分:
Model:业务逻辑处理、数据控制(本地数据、网络加载数据)。
View:显示用户可见得视图控件、与用户交互事件。
ViewModel:是组织生成和维护的视图数据层。在这一层开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。
ViewController:界面的生命周期控制和业务间切换控制。
工程目录结构:
MVVM代码实例:
Model:
//-----------MAnimalsModel----------
#import "MAnimalModel.h"
@protocol MAnimalModelDelegate;
@interface MAnimalsModel : NSObject
@property (nonatomic,weak) id<MAnimalModelDelegate> delegate;
@property (nonatomic,strong) NSArray <MAnimalModel*>*dataSource;
- (void)downloadImageWtihModel:(MAnimalModel *)animalModel;
@end
@protocol MAnimalModelDelegate<NSObject>
- (void)animalShowImage:(MAnimalModel *)animalModel row:(NSInteger)row;
@end
#import "MAnimalsModel.h"
@implementation MAnimalsModel
- (instancetype)init {
self = [super init];
if (self) {
[self loadDataSource];
}
return self;
}
- (void)dealloc {
NSLog(@"%s",__func__);
}
- (NSArray *)loadJson {
NSString *path = [[NSBundle mainBundle] pathForResource:@"jsonString" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:path];
NSError *error = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:(NSJSONReadingMutableLeaves) error:&error];
if ([jsonObject isKindOfClass:[NSArray class]]) {
return jsonObject;
}
else {
return nil;
}
}
- (void)loadDataSource {
NSArray *array = [self loadJson];
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:array.count];
for (NSDictionary *dictionary in array) {
MAnimalModel *model = [[MAnimalModel alloc] init];
model.identifier = [[NSDate date] timeIntervalSince1970];
model.imageUrl = [dictionary objectForKey:@"url"];
model.name = [dictionary objectForKey:@"name"];
model.summary = [dictionary objectForKey:@"summary"];
[mutableArray addObject:model];
}
_dataSource = mutableArray;
}
- (void)downloadImageWtihModel:(MAnimalModel *)animalModel {
if (animalModel.isLoading || !animalModel.imageUrl || animalModel.imageUrl.length == 0) {
return;
}
animalModel.isLoading = YES;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *imageURL = [NSURL URLWithString:animalModel.imageUrl];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
if (imageData) {
animalModel.imageData = imageData;
}
NSLog(@"imageData = %@",imageData);
dispatch_async(dispatch_get_main_queue(), ^{
if (self.delegate && [self.delegate respondsToSelector:@selector(animalShowImage:row:)]) {
NSInteger row = [self.dataSource indexOfObject:animalModel];
[self.delegate animalShowImage:animalModel row:row];
}
});
});
}
@end
//---------MAnimalModel----------
@interface MAnimalModel : NSObject
@property (nonatomic,assign) NSInteger identifier;
@property (nonatomic,strong) NSData *imageData;
@property (nonatomic,copy) NSString *imageUrl;
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *summary;
@property (nonatomic,assign) BOOL isLoading;
@end
#import "MAnimalModel.h"
@implementation MAnimalModel
- (void)dealloc {
NSLog(@"%s",__func__);
}
@end
View:
//---------------TableView-------------
#import "MAnimalsViewModel.h"
@interface MAnimalTableView : UITableView
@property (nonatomic,strong) MAnimalsViewModel *viewModel;
- (void)refreshData;
@end
#import "MAnimalTableView.h"
#import "MAnimalCell.h"
@interface MAnimalTableView()<UITableViewDelegate,UITableViewDataSource,MAnimalViewModelDelegate>
@end
@implementation MAnimalTableView
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
self = [super initWithFrame:frame style:style];
if (self) {
self.delegate = self;
self.dataSource = self;
self.viewModel = [[MAnimalsViewModel alloc] init];
self.viewModel.delegate = self;
}
return self;
}
- (void)dealloc {
NSLog(@"%s",__func__);
}
- (void)refreshData {
[self.viewModel reloadData];
}
#pragma mark -
#pragma mark -- MAnimalViewModel Delegate
- (void)viewModel:(MAnimalsViewModel *)viewModel reloadRow:(NSInteger)row {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
[self reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
- (void)reloadDataWithViewModel:(MAnimalsViewModel *)viewModel {
[self reloadData];
}
#pragma mark -
#pragma mark -- Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.viewModel.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *reuseIdentifier = @"reuseIdentifier";
MAnimalCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[MAnimalCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}
MAnimalViewModel *viewModel = [_viewModel animalEntityWitIndexPath:indexPath.row];
cell.textLabel.text = viewModel.name;
cell.detailTextLabel.text = viewModel.summary;
[cell showImageWithData:viewModel.imageData];
return cell;
}
#pragma mark -
#pragma mark -- UITableView Delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
title:@"Delete"
handler:^(UITableViewRowAction *action,
NSIndexPath *indexPath) {
[self.viewModel deleteWithRow:indexPath.row];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}];
return @[deleteAction];
}
@end
//-------------Cell--------------
@interface MAnimalCell : UITableViewCell
- (void)showImageWithData:(NSData *)data;
@end
#import "MAnimalCell.h"
@implementation MAnimalCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)showImageWithData:(NSData *)data {
if (data) {
self.imageView.image = [UIImage imageWithData:data];
}
}
@end
ViewModel:
//-------------ViewModels--------------
#import "MAnimalViewModel.h"
@protocol MAnimalViewModelDelegate;
@interface MAnimalsViewModel : NSObject
@property (nonatomic,weak) id<MAnimalViewModelDelegate> delegate;
@property (nonatomic,strong,readonly) NSMutableArray <MAnimalViewModel*>*dataSource;
- (MAnimalViewModel *)animalEntityWitIndexPath:(NSInteger)row;
- (void)deleteWithRow:(NSInteger)row;
- (void)reloadData;
@end
@protocol MAnimalViewModelDelegate<NSObject>
- (void)viewModel:(MAnimalsViewModel *)viewModel reloadRow:(NSInteger)row;
- (void)reloadDataWithViewModel:(MAnimalsViewModel *)viewModel;
@end
#import "MAnimalsViewModel.h"
#import "MAnimalsModel.h"
@interface MAnimalsViewModel()<MAnimalModelDelegate>
@property (nonatomic,strong) MAnimalsModel *animalsModel;
@end
@implementation MAnimalsViewModel
- (instancetype)init {
self = [super init];
if (self) {
_animalsModel = [[MAnimalsModel alloc] init];
_animalsModel.delegate = self;
[self reloadData];
}
return self;
}
- (void)dealloc {
NSLog(@"%s",__func__);
}
- (MAnimalViewModel *)animalEntityWitIndexPath:(NSInteger)row {
MAnimalViewModel *viewModel = [_dataSource objectAtIndex:row];
if (_animalsModel.dataSource.count > row) {
MAnimalModel *model = [_animalsModel.dataSource objectAtIndex:row];
[_animalsModel downloadImageWtihModel:model];
}
return viewModel;
}
- (void)animalShowImage:(MAnimalModel *)animalModel row:(NSInteger)row {
MAnimalViewModel *viewModel = [_dataSource objectAtIndex:row];
viewModel.imageData = animalModel.imageData;
if (_delegate && [_delegate respondsToSelector:@selector(viewModel:reloadRow:)]) {
[_delegate viewModel:self reloadRow:row];
}
}
- (void)deleteWithRow:(NSInteger)row {
[_dataSource removeObjectAtIndex:row];
}
- (void)reloadData {
MAnimalViewModel *vieweModel = nil;
NSMutableArray *mutableArray = [NSMutableArray array];
for (MAnimalViewModel *eachViewModel in _animalsModel.dataSource) {
vieweModel = [[MAnimalViewModel alloc] init];
vieweModel.identifier = eachViewModel.identifier;
vieweModel.imageData = eachViewModel.imageData;
vieweModel.name = eachViewModel.name;
vieweModel.summary = eachViewModel.summary;
[mutableArray addObject:vieweModel];
}
_dataSource = mutableArray;
if (_delegate && [_delegate respondsToSelector:@selector(reloadDataWithViewModel:)]) {
[_delegate reloadDataWithViewModel:self];
}
}
@end
//------------------ViewModel-------------
@interface MAnimalViewModel : NSObject
@property (nonatomic,assign) NSInteger identifier;
@property (nonatomic,strong) NSData *imageData;
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *summary;
@end
#import "MAnimalViewModel.h"
@implementation MAnimalViewModel
- (void)dealloc {
NSLog(@"%s",__func__);
}
@end
ViewController:
#import <UIKit/UIKit.h>
@interface MAnimalViewController : UIViewController
@end
#import "MAnimalViewController.h"
#import "MAnimalTableView.h"
@interface MAnimalViewController ()
@end
@implementation MAnimalViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"MVVM";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Refresh"
style:UIBarButtonItemStylePlain
target:self
action:@selector(refreshAction)];
MAnimalTableView *tableView = [[MAnimalTableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
self.view = tableView;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)refreshAction {
MAnimalTableView *tableView = (MAnimalTableView *)self.view;
[tableView refreshData];
}
@end