来看一下MVVM的工作原理图: mvvm.png前言: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.png工程目录结构:
MVVM代码实例:
Model:
#import "MAnimalEntity.h"
@protocol MAnimalModelDelegate;
@interface MAnimalModel : NSObject
@property (nonatomic,weak) id<MAnimalModelDelegate> delegate;
@property (nonatomic,strong) NSArray <MAnimalEntity*>*dataSource;
- (void)downloadImageWtihEntity:(MAnimalEntity *)entity;
@end
@protocol MAnimalModelDelegate<NSObject>
- (void)animalShowImage:(MAnimalEntity *)entity row:(NSInteger)row;
@end
#import "MAnimalModel.h"
@implementation MAnimalModel
- (instancetype)init {
self = [super init];
if (self) {
[self loadDataSource];
}
return self;
}
- (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) {
MAnimalEntity *entity = [[MAnimalEntity alloc] init];
entity.identifier = [[NSDate date] timeIntervalSince1970];
entity.imageUrl = [dictionary objectForKey:@"url"];
entity.name = [dictionary objectForKey:@"name"];
entity.summary = [dictionary objectForKey:@"summary"];
[mutableArray addObject:entity];
}
_dataSource = mutableArray;
}
- (void)downloadImageWtihEntity:(MAnimalEntity *)entity {
if (entity.isLoading || !entity.imageUrl || entity.imageUrl.length == 0) {
return;
}
entity.isLoading = YES;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *imageURL = [NSURL URLWithString:entity.imageUrl];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
if (imageData) {
entity.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:entity];
[self.delegate animalShowImage:entity row:row];
}
});
});
}
@end
//MAnimalEntity
@interface MAnimalEntity : 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 "MAnimalEntity.h"
@implementation MAnimalEntity
@end
View:
//TableView
#import "MAnimalViewModel.h"
@interface MAnimalTableView : UITableView
@property (nonatomic,strong) MAnimalViewModel *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 = [[MAnimalViewModel alloc] init];
self.viewModel.delegate = self;
}
return self;
}
- (void)refreshData {
[self.viewModel reloadData];
}
#pragma mark -
#pragma mark -- MAnimalViewModel Delegate
- (void)viewModel:(MAnimalViewModel *)viewModel reloadRow:(NSInteger)row {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
[self reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
- (void)reloadDataWithViewModel:(MAnimalViewModel *)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];
}
MAnimalViewEntity *entity = [_viewModel animalEntityWitIndexPath:indexPath.row];
cell.textLabel.text = entity.name;
cell.detailTextLabel.text = entity.summary;
[cell showImageWithData:entity.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:
#import "MAnimalViewEntity.h"
@protocol MAnimalViewModelDelegate;
@interface MAnimalViewModel : NSObject
@property (nonatomic,weak) id<MAnimalViewModelDelegate> delegate;
@property (nonatomic,strong,readonly) NSMutableArray <MAnimalViewEntity*>*dataSource;
- (MAnimalViewEntity *)animalEntityWitIndexPath:(NSInteger)row;
- (void)deleteWithRow:(NSInteger)row;
- (void)reloadData;
@end
@protocol MAnimalViewModelDelegate<NSObject>
- (void)viewModel:(MAnimalViewModel *)viewModel reloadRow:(NSInteger)row;
- (void)reloadDataWithViewModel:(MAnimalViewModel *)viewModel;
@end
#import "MAnimalViewModel.h"
#import "MAnimalModel.h"
@interface MAnimalViewModel()<MAnimalModelDelegate>
@property (nonatomic,strong) MAnimalModel *animalModel;
@end
@implementation MAnimalViewModel
- (instancetype)init {
self = [super init];
if (self) {
_animalModel = [[MAnimalModel alloc] init];
_animalModel.delegate = self;
[self reloadData];
}
return self;
}
- (MAnimalViewEntity *)animalEntityWitIndexPath:(NSInteger)row {
MAnimalViewEntity *viewEntity = [_dataSource objectAtIndex:row];
if (_animalModel.dataSource.count > row) {
MAnimalEntity *entity = [_animalModel.dataSource objectAtIndex:row];
[_animalModel downloadImageWtihEntity:entity];
}
return viewEntity;
}
- (void)animalShowImage:(MAnimalEntity *)entity row:(NSInteger)row {
MAnimalViewEntity *viewEntity = [_dataSource objectAtIndex:row];
viewEntity.imageData = entity.imageData;
if (_delegate && [_delegate respondsToSelector:@selector(viewModel:reloadRow:)]) {
[_delegate viewModel:self reloadRow:row];
}
}
- (void)deleteWithRow:(NSInteger)row {
[_dataSource removeObjectAtIndex:row];
}
- (void)reloadData {
MAnimalViewEntity *vieweEtity = nil;
NSMutableArray *mutableArray = [NSMutableArray array];
for (MAnimalEntity *entity in _animalModel.dataSource) {
vieweEtity = [[MAnimalViewEntity alloc] init];
vieweEtity.identifier = entity.identifier;
vieweEtity.imageData = entity.imageData;
vieweEtity.name = entity.name;
vieweEtity.summary = entity.summary;
[mutableArray addObject:entity];
}
_dataSource = mutableArray;
if (_delegate && [_delegate respondsToSelector:@selector(reloadDataWithViewModel:)]) {
[_delegate reloadDataWithViewModel:self];
}
}
@end
@interface MAnimalViewEntity : NSObject
@property (nonatomic,assign) NSInteger identifier;
@property (nonatomic,strong) NSData *imageData;
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *summary;
@end
#import "MAnimalViewEntity.h"
@implementation MAnimalViewEntity
@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
网友评论