zoukankan      html  css  js  c++  java
  • 基于RAC的通用TableView

      最近公司的一个新项目的1.0版本开发完了,但是对于这么一个初期的项目,部分VC的代码行数仍然超过300行。我也开始感觉到有种(Massive)MVC的趋势,而且部分界面控件的创建方法还是略略有点Magic。于是我开始寻求新的架构,来改善当前的状况。我想起了之前听说过的ReactiveCocoa,加上最近Ray神和Objcio.cn的介绍,我也开始了RAC的“修炼”。
      不过,RAC的学习不但需要了解其API的作用,更重要的是用RAC的思维去思考。如果用传统的MVC架构思维,我可以很快速写出一个简单的框架,但是学习RAC和MVVM,就发现原来很多理所当然的东西也要仔细去思考。对于初学者而言,RAC并不是很友好,但掌握基本用法后,就已经可以感受到RAC的优势。
      在学习的时候,我看到了Ray神上面用Signal和Command来代替UITableViewDataSource和UITableViewDelegate的helper。不过,本人还是觉得不太满意,毕竟需要额外的helper,而且限定cell必须来自xib。于是基于Ray神的基础上,我把这套模式搬到了UITableView层上,同时也支持多个section和cell的定制。这类相对常用的控件,我不想把API设计得过于复杂,所以对于有相对特殊的要求还是需要实现DataSource和Delegate。本人意在把Ray神的思路整合到控件层,并不打算写高大上的控件。

     1 //
     2 //  PINKBindCellProtocol.h
     3 //
     4 
     5 #import <Foundation/Foundation.h>
     6 
     7 @protocol PINKBindCellProtocol <NSObject>
     8 
     9 - (void)bindCellViewModel:(id)viewModel;
    10 
    11 @end
     1 //
     2 //  PINKBindTableView.h
     3 //
     4 
     5 #import <UIKit/UIKit.h>
     6 
     7 @protocol PINKBindCellProtocol;
     8 
     9 typedef UITableViewCell *(^PINKBindTableViewCreateCellBlock)(NSIndexPath *indexPath);
    10 
    11 @interface PINKBindTableView : UITableView
    12 
    13 @property (nonatomic, getter = isAutoCheckDataSource) BOOL autoCheckDataSource;
    14 @property (nonatomic, getter = isAutoDeselect) BOOL autoDeselect;
    15 
    16 @property (nonatomic, readonly) id<UITableViewDataSource> realDataSource;
    17 @property (nonatomic, readonly) id<UITableViewDelegate> realDelegate;
    18 
    19 - (void)setDataSourceSignal:(RACSignal *)sourceSignal
    20            selectionCommand:(RACCommand *)selection
    21                   cellClass:(Class<PINKBindCellProtocol>)cellClass;
    22 
    23 - (void)setDataSourceSignal:(RACSignal *)sourceSignal
    24            selectionCommand:(RACCommand *)selection
    25             createCellBlock:(PINKBindTableViewCreateCellBlock)createCellBlock;
    26 
    27 @end
    28 
    29 //以下是截获了的方法,继承子类时可以直接重写,但还是要调用super
    30 @interface PINKBindTableView (UITableViewDataSourceIntercept)
    31 
    32 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
    33 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
    34 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
    35 
    36 @end
    37 
    38 @interface PINKBindTableView (UITableViewDelegateIntercept)
    39 
    40 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
    41 
    42 @end
      1 //
      2 //  PINKBindTableView.m
      3 //
      4 
      5 #import "PINKBindTableView.h"
      6 
      7 #import "MessageInterceptor.h"
      8 
      9 #import "PINKBindCellProtocol.h"
     10 
     11 typedef NS_OPTIONS(NSInteger, PINKBindTableView_DataSource_MethodType) {
     12     PINKBindTableView_DataSource_MethodType_numberOfSectionsInTableView  = 1 << 0,
     13     PINKBindTableView_DataSource_MethodType_numberOfRowsInSection        = 1 << 1,
     14     PINKBindTableView_DataSource_MethodType_cellForRowAtIndexPath        = 1 << 2,
     15 };
     16 
     17 @interface PINKBindTableView ()<UITableViewDataSource, UITableViewDelegate>
     18 {
     19     MessageInterceptor *_dataSourceInterceptor;
     20     MessageInterceptor *_delegateInterceptor;
     21     
     22     PINKBindTableView_DataSource_MethodType _dataSourceMethodType;
     23 }
     24 
     25 /**
     26  *  tableData为nil时,若dataSource或delegate实现了对应方法,则调用。
     27  */
     28 @property (nonatomic, strong) NSArray *tableData;
     29 @property (nonatomic, strong) RACCommand *didSelectedCommand;
     30 @property (nonatomic, unsafe_unretained) Class<PINKBindCellProtocol> cellClass;
     31 @property (nonatomic, strong) NSString *cellReuseIdentifier;
     32 @property (nonatomic, copy) PINKBindTableViewCreateCellBlock createCellBlock;
     33 
     34 @end
     35 
     36 @implementation PINKBindTableView
     37 
     38 - (id)initWithFrame:(CGRect)frame
     39 {
     40     self = [super initWithFrame:frame];
     41     
     42     if (self) {
     43         [self p_InitPINKBindTableView];
     44     }
     45     
     46     return self;
     47 }
     48 
     49 - (instancetype)initWithCoder:(NSCoder *)aDecoder
     50 {
     51     self = [super initWithCoder:aDecoder];
     52     
     53     if (self) {
     54         [self p_InitPINKBindTableView];
     55     }
     56     
     57     return self;
     58 }
     59 
     60 #pragma mark - Private Initialize
     61 - (void)p_InitPINKBindTableView
     62 {
     63     _autoCheckDataSource = YES;
     64     _autoDeselect = YES;
     65     
     66     _dataSourceInterceptor = [[MessageInterceptor alloc] init];
     67     _dataSourceInterceptor.middleMan = self;
     68     _dataSourceInterceptor.receiver = [super dataSource];
     69     [super setDataSource:(id<UITableViewDataSource>)_dataSourceInterceptor];
     70     
     71     _delegateInterceptor = [[MessageInterceptor alloc] init];
     72     _delegateInterceptor.middleMan = self;
     73     _delegateInterceptor.receiver = [super delegate];
     74     [super setDelegate:(id<UITableViewDelegate>)_delegateInterceptor];
     75 }
     76 
     77 #pragma mark - Overwrite DataSource
     78 - (void)setDataSource:(id<UITableViewDataSource>)dataSource
     79 {
     80     if (_dataSourceInterceptor) {
     81         _dataSourceInterceptor.receiver = dataSource;
     82         //UITableViewDataSource有类似缓存机制优化,所以先设置nil
     83         [super setDataSource:nil];
     84         [super setDataSource:(id<UITableViewDataSource>)_dataSourceInterceptor];
     85         
     86         [self updateDataSourceMethodType];
     87     } else {
     88         [super setDataSource:dataSource];
     89     }
     90 }
     91 
     92 - (id<UITableViewDataSource>)realDataSource
     93 {
     94     if (_dataSourceInterceptor) {
     95         return _dataSourceInterceptor.receiver;
     96     } else {
     97         return [super dataSource];
     98     }
     99 }
    100 
    101 #pragma mark - Overwrite Delegate
    102 - (void)setDelegate:(id<UITableViewDelegate>)delegate
    103 {
    104     if (_delegateInterceptor) {
    105         _delegateInterceptor.receiver = delegate;
    106         
    107         [super setDelegate:nil];
    108         [super setDelegate:(id<UITableViewDelegate>)_delegateInterceptor];
    109     } else {
    110         [super setDelegate:delegate];
    111     }
    112 }
    113 
    114 - (id<UITableViewDelegate>)realDelegate
    115 {
    116     if (_delegateInterceptor) {
    117         return _delegateInterceptor.receiver;
    118     } else {
    119         return [super delegate];
    120     }
    121 }
    122 
    123 #pragma mark - UITableViewDataSource
    124 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    125 {
    126     if (!_tableData &&
    127         _dataSourceMethodType & PINKBindTableView_DataSource_MethodType_numberOfSectionsInTableView) {
    128         return [_dataSourceInterceptor.receiver numberOfSectionsInTableView:tableView];
    129     } else
    130         return _tableData.count;
    131 }
    132 
    133 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    134 {
    135     if (!_tableData &&
    136         _dataSourceMethodType & PINKBindTableView_DataSource_MethodType_numberOfRowsInSection) {
    137         return [_dataSourceInterceptor.receiver tableView:tableView numberOfRowsInSection:section];
    138     } else
    139         return [_tableData[section] count];
    140 }
    141 
    142 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    143 {
    144     if (!_tableData &&
    145         _dataSourceMethodType & PINKBindTableView_DataSource_MethodType_cellForRowAtIndexPath) {
    146         return [_dataSourceInterceptor.receiver tableView:tableView cellForRowAtIndexPath:indexPath];
    147     } else {
    148         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:_cellReuseIdentifier];
    149         if (!cell) {
    150             if (_cellClass) {
    151                 cell = [[(Class)_cellClass alloc] init];
    152             } else if (_createCellBlock) {
    153                 cell = _createCellBlock(indexPath);
    154             }
    155         }
    156         [(id<PINKBindCellProtocol>)cell bindCellViewModel:_tableData[indexPath.section][indexPath.row]];
    157         
    158         return cell;
    159     }
    160 }
    161 
    162 #pragma mark - UITableViewDelegate
    163 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    164 {
    165     if (_autoDeselect) {
    166         [tableView deselectRowAtIndexPath:indexPath animated:YES];
    167     }
    168     
    169     if (_tableData && _didSelectedCommand) {
    170         [_didSelectedCommand execute:_tableData[indexPath.section][indexPath.row]];
    171     }
    172     
    173     if ([_delegateInterceptor.receiver respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) {
    174         [_delegateInterceptor.receiver tableView:tableView didSelectRowAtIndexPath:indexPath];
    175     }
    176 }
    177 
    178 #pragma mark - 缓存DataSource方法
    179 - (void)updateDataSourceMethodType
    180 {
    181     _dataSourceMethodType = 0;
    182     
    183     if ([_dataSourceInterceptor.receiver respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
    184         _dataSourceMethodType |= PINKBindTableView_DataSource_MethodType_numberOfSectionsInTableView;
    185     }
    186     
    187     if ([_dataSourceInterceptor.receiver respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) {
    188         _dataSourceMethodType |= PINKBindTableView_DataSource_MethodType_numberOfRowsInSection;
    189     }
    190     
    191     if ([_dataSourceInterceptor.receiver respondsToSelector:@selector(tableView:cellForRowAtIndexPath:)]) {
    192         _dataSourceMethodType |= PINKBindTableView_DataSource_MethodType_cellForRowAtIndexPath;
    193     }
    194 }
    195 
    196 #pragma mark - API
    197 - (void)setDataSourceSignal:(RACSignal *)sourceSignal
    198            selectionCommand:(RACCommand *)selection
    199                   cellClass:(Class<PINKBindCellProtocol>)cellClass
    200 {
    201     self.cellClass = cellClass;
    202     self.createCellBlock = nil;
    203     self.didSelectedCommand = selection;
    204     [self configCellReuseIdentifierAndCellHeight];
    205     
    206     @weakify(self);
    207     [sourceSignal subscribeNext:^(NSArray *source) {
    208         @strongify(self);
    209         self.tableData = source;
    210         [self reloadData];
    211     }];
    212 }
    213 
    214 - (void)setDataSourceSignal:(RACSignal *)sourceSignal
    215            selectionCommand:(RACCommand *)selection
    216             createCellBlock:(PINKBindTableViewCreateCellBlock)createCellBlock
    217 {
    218     self.cellClass = nil;
    219     self.createCellBlock = createCellBlock;
    220     self.didSelectedCommand = selection;
    221     [self configCellReuseIdentifierAndCellHeight];
    222     
    223     @weakify(self);
    224     [sourceSignal subscribeNext:^(NSArray *source) {
    225         @strongify(self);
    226         self.tableData = source;
    227         [self reloadData];
    228     }];
    229 }
    230 
    231 - (void)configCellReuseIdentifierAndCellHeight
    232 {
    233     UITableViewCell *oneCell = nil;
    234     if (_cellClass) {
    235         oneCell = [[(Class)_cellClass alloc] init];
    236     } else if (_createCellBlock) {
    237         oneCell = _createCellBlock([NSIndexPath indexPathForRow:0 inSection:0]);
    238     }
    239     _cellReuseIdentifier = oneCell.reuseIdentifier;
    240     self.rowHeight = oneCell.frame.size.height;
    241 }
    242 
    243 #pragma mark - Properties
    244 - (void)setTableData:(NSArray *)tableData
    245 {
    246     if (_autoCheckDataSource &&
    247         tableData.count &&
    248         ![tableData[0] isKindOfClass:[NSArray class]]) {
    249         _tableData = @[tableData];
    250     } else {
    251         _tableData = tableData;
    252     }
    253 }
    254 
    255 @end
  • 相关阅读:
    XML 编码
    XML CDATA
    XML 命名空间
    XML 解析器
    XML XMLHttpRequest 对象
    XML 和CSS
    XML 验证
    XML 属性
    XML 元素
    XML 语法规则
  • 原文地址:https://www.cnblogs.com/ipinka/p/3862786.html
Copyright © 2011-2022 走看看