• 小记: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;
            
        }
        

    三、定义 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");
        }];    
        

    四. 结尾

    • 一点一滴,仅此记录。
  • 相关阅读:
    springmvc spring mybatis框架整合
    多线程bug修复
    OutOfMemory
    CSS中强悍的相对单位之em(em-and-elastic-layouts)学习小记
    css中line-height行高的深入学习
    HTML5的新语义化的标签
    关于Three.js基本几何形状
    【Google Chrome】 Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource问题解决
    模拟Bootstrap响应式网格系统
    关于我对写博客那些事儿的实用心得
  • 原文地址:https://www.cnblogs.com/howie-ch/p/9520396.html
Copyright © 2020-2023  润新知