iOS之基于MVP架构实现view的复用

手机软件开发 2024-9-20 19:13:15 69 0 来自 中国
场景:假如一个包罗头像、名字、自我介绍文案的自界说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即可
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 10:20, Processed in 0.153884 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表