小记:iOS 中一般对于 view 不依赖 model 的的两种代码书写形式
一. 前言
-
对于在 MVC 的定义中,view 层是不引用 model 层,view 和 model 是不相往来的
-
一般开发中,我们都写过 在自定义 view 中增加一个 model 的属性,外接直接传个 model 来,在 view 中 model 的 set 方法里对 view 的控件赋值的代码,例如在自定义 UITableViewCell 时用的很多,此时 view 是直接引用了 model
-
基于封装的思想,我们需要竟可能的复用我们的代码,复用我们的 view,这时我们需要进行解耦,不依赖于某个特殊的 model。另外,如果对于很特殊的 view,整个项目中没有什么重用的,可以按之前情况处理
-
本文简要介绍自己常用的两种写法来解耦 view 和 model,使之更符合 MVC 的定义
二.定义 ViewModel 对象及 ViewModelType (协议) 的形式
- 说明
- 将 view 中所有控件需要的数据依次列出
- 定义 ViewModelType 一种协议,定义协议方法 ,旨在通过协议方法,返回 view 需要的数据
- view 中控件赋值,需要外接传入遵守 ViewModelType 协议的 ViewModel,在 ViewModel 的 set 方法里进行控件赋值
- 该 ViewModel 的创建是依赖具体的 model 的,需要遵守 ViewModelType 协议
- view 和 ViewModelType 为一整体,通过更换不同的 ViewModel 来达到重用。更换不同 ViewModel,也相当于更换了数据 model。
- 示例
- 如一个 cell (有大标题,几个子标题和标题对应的内容,状态,图片等)
@interface LXReservationCell : UITableViewCell @property (nonatomic, weak) id<LXReservationCellViewModelType> viewModel; @end
- LXReservationCellViewModelType 协议
@protocol LXReservationCellViewModelType <NSObject> // 标题 @property (nonatomic, copy, readonly) NSString *title; // 第一个子标题 @property (nonatomic, copy, readonly) NSString *firstItemTitle; // 第一个子标题对应的内容 @property (nonatomic, copy, readonly) NSString *firstItemContent; @end
- LXReservationCellViewModel 中
#import <Foundation/Foundation.h> @class LXReservationItem; @interface LXReservationCellViewModel : NSObject - (instancetype)initWithItem:(LXReservationItem *)item; @end
@interface LXReservationCellViewModel () <LXReservationCellViewModelType> // 标题 @property (nonatomic, copy) NSString *title; // 第一个子标题 @property (nonatomic, copy) NSString *firstItemTitle; // 第一个子标题对应的内容 @property (nonatomic, copy) NSString *firstItemContent; @end @implementation LXReservationCellViewModel - (instancetype)initWithItem:(LXReservationItem *)item { if (self = [super init]) { self.title = item.title; self.firstItemTitle = item.orderCategory; self.firstItemContent = item.orderdate; } return self; }
- 如一个 cell (有大标题,几个子标题和标题对应的内容,状态,图片等)
三、定义 view 对应的 config,使用链式编程的形式进行对 view 控件赋值
- 说明
- 将 view 中所有控件需要的数据依次列出
- 定义一个 config 对象,使用链式编程的形式进行获取 view 需要的各种数据,在 config 中弱引用 view,把各种数据在赋值给 view
- view 中需要定义一个配置数据的方法,方法的参数是个 block,block 传一个 config 对象给外界使用
- view 和 config 为一整体,并不引用 model,因此脱离的 model 的限制,具有重用性
- 示例
- 如一个简单的展示标题、内容,还有一个按钮的 view
#import <UIKit/UIKit.h> @class LXIntroduceViewConfig; @interface LXIntroduceView : UIView // 标题 @property (nonatomic, copy) NSString *title; // 内容 @property (nonatomic, copy) NSString *content; // 按钮标题 @property (nonatomic, copy) NSString *btnTitle; // 配置 view 对应的数据的方法 - (void)lx_makeWithConfig:(void(^)(LXIntroduceViewConfig *config))block;
- (void)lx_makeWithConfig:(void (^)(LXIntroduceViewConfig *))block { LXIntroduceViewConfig *config = [[LXIntroduceViewConfig alloc]initWithIntroduceView:self]; !block? : block(config); }
- LXIntroduceViewConfig.h
#import <Foundation/Foundation.h> @class LXIntroduceView; @interface LXIntroduceViewConfig : NSObject - (instancetype)initWithIntroduceView:(LXIntroduceView *)introduceView; // 设置标题 - (LXIntroduceViewConfig *(^)(NSString *))setupTitle; // 设置要显示的内容 - (LXIntroduceViewConfig *(^)(NSString *))setupContent; // 设置按钮的标题 - (LXIntroduceViewConfig *(^)(NSString *))setupBtnTitle; @end
- LXIntroduceViewConfig.m
@interface LXIntroduceViewConfig () // 弱引用 view @property (nonatomic, weak) LXIntroduceView *introduceView; @end @implementation LXIntroduceViewConfig // init - (instancetype)initWithIntroduceView:(LXIntroduceView *)introduceView { if (self = [super init]) { self.introduceView = introduceView; } return self; } // 把标题传给 view,view 中 set 方法接收 - (LXIntroduceViewConfig *(^)(NSString *))setupTitle { return ^(NSString *tmp) { self.introduceView.title = tmp; return self; }; } // 把内容传给 view,view 中 set 方法接收 - (LXIntroduceViewConfig *(^)(NSString *))setupContent { return ^(NSString *tmp) { self.introduceView.content = tmp; return self; }; } // 把按钮标题传给 view - (LXIntroduceViewConfig *(^)(NSString *))setupBtnTitle { return ^(NSString *tmp) { self.introduceView.btnTitle = tmp; return self; }; } @end
- 外界使用
LXIntroduceView *introduceView = [[LXIntroduceView alloc]init]; [introduceView lx_makeWithConfig:^(LXIntroduceViewConfig *config) { config .setupTitle(self.resultItem.title) .setupContent(self.resultItem.content) .setupBtnTitle(@"test"); }];
- 如一个简单的展示标题、内容,还有一个按钮的 view
四. 结尾
- 一点一滴,仅此记录。