一. 前言
-
對於在 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
四. 結尾
- 一點一滴,僅此記錄。
