场景:假如一个包罗头像、名字、自我介绍文案的自界说view在差别的列表cell的contentView中都存在,那么我们每个cell里都要去依靠这个view,但是大概差别列表的数据源模子model是差别的,那么我们须要cell.model = model赋值时,对于这个view而言,就有多个model对象,如许的代码就有点让人受不了,同一个给子控件赋值的使用由于model差别就要做多遍,怎么处理惩罚?
由于MVP架构中的P,可以实现V和M的解耦,原理是:protocal是针对于view渲染所须要的数据来设置的协议,也就是说view子控件所须要的直接数据都可以在protocal中找到,那协议中的数据从那里来呢?这个就要从服从这个协议的model类中获取,在model类的.m文件中,去给协议里的这些字段赋值,固然赋值的依据来自于model的数据(直接赋值大概颠末盘算后赋值)。
如许看来对于view来说没有依靠model,依靠的是遵循了协议的model,不管是哪个model,只要遵循了协议实现了协议中属性的赋值就可以,此时view中是不须要引入哪个model的头文件的,分析实现了V和M的解耦
整个项目布局
protocal协议
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@protocol SJTableBaseModelProtocol <NSObject>//这里都是view中可以直接赋值的一些属性@required@property (nonatomic,copy,readonly) NSString *name;///对于个人的形貌@property (nonatomic,copy,readonly) NSString *content;@property (nonatomic,copy,readonly) NSString *ageStr;@property (nonatomic,assign,readonly) BOOL isShow;@endNS_ASSUME_NONNULL_END一个根本的view类,用于设置公用属性
#import <UIKit/UIKit.h>#import "SJTableBaseModelProtocol.h"NS_ASSUME_NONNULL_BEGINstatic CGFloat kBaseCellSubViewLeftMargin = 12.0; //cell内容同一的左边距@interface SJCellBaseView : UIView- (void)configWithDataid <SJTableBaseModelProtocol>)model;+ (CGFloat)fetchHeightWithDataid <SJTableBaseModelProtocol>)model;@endNS_ASSUME_NONNULL_END#import "SJCellBaseView.h"@implementation SJCellBaseView- (void)configWithDataid <SJTableBaseModelProtocol>)model {}+ (CGFloat)fetchHeightWithDataid <SJTableBaseModelProtocol>)model { return 0;}@end继承与SJCellBaseView的子类
@interface SJCellDiyContentView : SJCellBaseView//点击变乱由外部处理惩罚@property (nonatomic, copy) dispatch_block_t clickSeeMoreBlock;@endNS_ASSUME_NONNULL_END@interface SJCellDiyContentView ()@property (nonatomic, strong) YYLabel *contentLabel;@end@implementation SJCellDiyContentView- (instancetype)initWithFrameCGRect)frame { self = [super initWithFrame:frame]; if (self) { [self configViews]; } return self;}- (void)configViews { [self addSubview:self.contentLabel];}- (void)configWithDataid < SJTableBaseModelProtocol >)model { //赋值渲染使用 ...//赋值的时间直接盘算一下自己的高度,供外层控件布局使用 self.height = [[self class] fetchHeightWithData:model];}+ (CGFloat)fetchHeightWithDataid < SJTableBaseModelProtocol >)model {//根据model赋值完,盘算控件的高度 return 40;}#pragma mark - Getter- (YYLabel *)contentLabel { if (!_contentLabel) { _contentLabel = [[YYLabel alloc] init]; _contentLabel.textVerticalAlignment = YYTextVerticalAlignmentTop; _contentLabel.displaysAsynchronously = YES; _contentLabel.ignoreCommonProperties = YES; _contentLabel.fadeOnAsynchronouslyDisplay = NO; _contentLabel.fadeOnHighlight = NO; @weakify(self) _contentLabel.highlightTapAction = ^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) { @strongify(self); [self clickNicknameWithText:text range:range]; }; } return _contentLabel;}@end后续另有多个可复用view的开发,都继承与SJCellBaseView,如许就可以使用父类- (void)configWithDataid < SJTableBaseModelProtocol >)model {}和+ (CGFloat)fetchHeightWithDataid < SJTableBaseModelProtocol >)model {}赋值和获取高度方法
假如我们后续又添加了两个自界说viewSJCellDiyContentView_two SJCellDiyContentView_three
根本的UITableViewCell类
@interface SJBaseTableViewCell : UITableViewCell- (void)configWithDataid <SJTableBaseModelProtocol>)model;+ (CGFloat)fetchHeightWithData:(id < SJTableBaseModelProtocol >)model;// 子类重写此方法,设置好所有模块,按照从上到下的次序+ (NSArray *)myComponents;#pragma mark - 业务交互方法,Cell子类实现- (void)didClickHead;- (void)didClickCancleAdvertising;...@interface SJBaseTableViewCell ()@property (nonatomic, strong) NSArray *cellComponents;//将相似tableviewcell中的控件都放在根本cell中,根据子类的cellComponents方法中添加子控件种类来添加子控件@property (nonatomic, strong) SJCellDiyContentView *cellDiyContentView;@property (nonatomic, strong) SJCellDiyContentView_two * cellDiyContentView_two;@property (nonatomic, strong) SJCellDiyContentView_three * cellDiyContentView_three;@property (nonatomic, strong) UIView *bottomView;@end@implementation SJBaseTableViewCell- (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]){ self.cellComponents = [[self class] myComponents]; [self configViews]; } return self;}- (void)configViews {//子类中通过myComponents方法中添加摆列值得方式,添加子视图,在父视图这里择环境从上往下添加子视图//继承与该cell的子类可以包罗多种组合方式 for (int i = 0; i < [self.cellComponents count]; i++) { NSNumber *num = [self.cellComponents objectAtIndex:i]; switch (num.integerValue) { //自界说view的摆列值 case kSJCellDiyContentView: [self.contentView addSubview:self.cellDiyContentView]; break; //自界说view2的摆列值 case kSJCellDiyContentView_two: [self.contentView addSubview:self.cellDiyContentView_two]; break; //自界说view3的摆列值 case kSJCellDiyContentView_three: [self.contentView addSubview:self.cellDiyContentView_three]; break; default: break; } } //self.bottomView用于封底 [self.contentView addSubview:self.bottomView]; [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.bottom.equalTo(self.contentView); make.height.equalTo(@(10)); }];}- (void)configWithData:(id < SJTableBaseModelProtocol >)model { CGFloat bottom = 0; for (int i = 0; i < [self.cellComponents count]; i++) { NSNumber *num = [self.cellComponents objectAtIndex:i]; switch (num.integerValue) { //自界说view的摆列值 case kSJCellDiyContentView: [self.cellDiyContentView configWithData:model]; self.cellDiyContentView.frame = CGRectMake(0, bottom, kScreenWidth, self.cellRecommendHeadView.height); bottom = bottom + self.cellDiyContentView.height; break; //自界说view2的摆列值 case kSJCellDiyContentView_two: [self.cellDiyContentView_two configWithData:model]; self.cellDiyContentView_two.frame = CGRectMake(0, bottom, kScreenWidth, self.cellDiyContentView_two.height); bottom = bottom + self.cellDiyContentView_two.height; break; //自界说view3的摆列值 case kSJCellDiyContentView_three: [self.cellDiyContentView_three configWithData:model]; self.cellDiyContentView_three.frame = CGRectMake(0, bottom, kScreenWidth, self.cellDiyContentView_three.height); bottom = bottom + self.cellDiyContentView_three.height; break; default: break; } }}+ (CGFloat)fetchHeightWithData:(id < SJTableBaseModelProtocol >)model { CGFloat height = 0; for (int i = 0; i < [[[self class] myComponents] count]; i++) { NSNumber *num = [[[self class] myComponents] objectAtIndex:i]; switch (num.integerValue) { //自界说view的摆列值 case kSJCellDiyContentView: height = height + [SJCellDiyContentView fetchHeightWithData:model]; break; //自界说view2的摆列值 case kSJCellDiyContentView_two: height = height + [SJCellDiyContentView_two fetchHeightWithData:model]; break; //自界说view3的摆列值 case kSJCellDiyContentView_three: height = height + [SJCellDiyContentView_three fetchHeightWithData:model]; break; default: break; } } height = height + 10 + 15; // 10 为self.bottomView 高度,15 为间隔self.bottom 顶部的间距 return height;}#pragma mark - lazyload子控件的懒加载地区#pragma mark - 交互方法,子类实现+ (NSArray *)myComponents { return @[];}//view中的点击变乱在本类中先不实现,对外提供接口,让子类实现- (void)didClickHead {}- (void)didClickCancleAdvertising {}使用 :我们有一个cell样式是由SJCellDiyContentView_two和SJCellDiyContentView_three组合的
新建一个cell类,好比customTableviewCell继承自SJBaseTableViewCell
NS_ASSUME_NONNULL_BEGIN@interface customTableviewCell : SJBaseTableViewCell@endNS_ASSUME_NONNULL_END@implementation customTableviewCell- (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setupData]; } return self;}#pragma mark - 本cell包罗的所有模块+ (NSArray *)myComponents { //对于父类的所有子控件来说,本cell只包罗了这两种view,以是只加这两种摆列,从上往下加 return @[@(kSJCellDiyContentView_two), @(kSJCellDiyContentView_three),];}- (void)configWithData:(id<SJTableBaseModelProtocol>)model { //调用父类的赋值方法 [super configWithData:model];}+ (CGFloat)fetchHeightWithData:(id < SJTableBaseModelProtocol >)model{ return [super fetchHeightWithData:model];}//这里就可以实现父类中在本cell中的具体触发到的点击变乱了- (void)didClickHead{ //具体实现。。。}model类的使用
#import <Foundation/Foundation.h>#import "SJTableBaseModelProtocol.h"NS_ASSUME_NONNULL_BEGIN@interface DiyModel : NSObject< SJTableBaseModelProtocol >@property (nonatomic,copy) NSString *name;@property (nonatomic,copy) NSString *content;@property (nonatomic,assign) NSInteger age;@endNS_ASSUME_NONNULL_END#import "DiyModel.h"@implementation DiyModel//在类中遵循了署理,就要实现协议里那些属性的赋值,给到协议的值是view可以直接用的- (NSString *)name{ return [NSString stringWithFormat"我的名字叫:%@",_name];}- (NSString *)ageStr{ return [NSString stringWithFormat"%ld",self.age];}//假如不消处理惩罚直接赋值的,注意嵌套循环造成死循环,好比这里就不能return self.content,会造成自身一直在嵌套循环- (NSString *)content{ return _content;}- (BOOL)isShow{ if (self.age > 18) { return YES; }else{ return NO; }}@end其他cell的使用
其他列表cell的数据源差别,只要也遵循协议,并把协议的属性通过自己model的属性颠末处理惩罚赋值,那么就照旧可以用户我们的view渲染试图方法- (void)configWithData:(id<SJTableBaseModelProtocol>)model由于我们要的只是遵循了SJTableBaseModelProtocol的东西,不关系他是什么
总结:用这种方式,就像堆积木一样,起首cell的子控件是通用的,我根本cell组建里包罗了所有种子控件,使用的时间只要通过摆列去添加我们所须要的控件就OK了,然后网络哀求好的数据封装model后传值给cell即可
|