美文网首页ios 知识点
iOS的MVVM框架模式

iOS的MVVM框架模式

作者: WorldPeace_hp | 来源:发表于2018-09-25 11:53 被阅读3184次

    前言:iOS的MVVM如何工作呢?各自的职责是什么呢?首先我们先来介绍一下纯粹的MVVM架构模式,再来介绍MVVM的双向绑定。

    来看一下MVVM的工作原理图: mvvm.png

    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
    
    

    Demo:https://github.com/PeaceWanghp/iOSArchitecture

    相关文章

      网友评论

        本文标题:iOS的MVVM框架模式

        本文链接:https://www.haomeiwen.com/subject/bkpnyftx.html