iOS开发 | 看待view要像看待模块一样

Yoona

背景

最近产品部门提了一点需求,修改了一下“选择收货时间”页面,我也因此回顾了一下前年(16年)写的代码,不得不说,太臭了。

详情

如图:

这里面有个温馨提示view:

展示数据来自后台,这里只是一条数据,其实有可能是多条数据,如:

多条数据

16年的做法

那个时候我对MVC的理解还不到位,这种模块view我都是一口气写到控制器里的。

或许你觉得这个view并不复杂,但是写个上百行代码还是没问题的。

现在的做法

将这个view看作一个模块,然后抽离出来。代码文件如下:


总共分成了三个类:

1. CQPickTimeAlertModel

@interface CQPickTimeAlertModel : NSObject

/** 标题 */
@property (nonatomic, copy) NSString *title;
/** 内容 */
@property (nonatomic, copy) NSString *desc;

@end

2. CQPickTimeAlertItem

对应一个model,它的表现形式如下:


item

.h文件:

@interface CQPickTimeAlertItem : UIView

@property (nonatomic, strong) CQPickTimeAlertModel *model;

@end

.m文件:

@interface CQPickTimeAlertItem ()

@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *descLabel;

@end

@implementation CQPickTimeAlertItem

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setUpUI];
    }
    return self;
}

- (void)setUpUI {
    self.titleLabel = [[UILabel alloc] init];
    [self addSubview:self.titleLabel];
    self.titleLabel.font = [UIFont systemFontOfSize:10];
    self.titleLabel.textColor = [UIColor redColor];
    self.titleLabel.textAlignment = NSTextAlignmentCenter;
    self.titleLabel.layer.cornerRadius = 3;
    self.titleLabel.layer.borderWidth = 1;
    self.titleLabel.layer.borderColor = [UIColor redColor].CGColor;
    
    
    self.descLabel = [[UILabel alloc] init];
    [self addSubview:self.descLabel];
    self.descLabel.font = [UIFont systemFontOfSize:12];
    self.descLabel.textColor = [UIColor orangeColor];
    self.descLabel.numberOfLines = 0;
    
    [self bringSubviewToFront:self.titleLabel];
}

- (void)setModel:(CQPickTimeAlertModel *)model {
    _model = model;
    
    self.titleLabel.text = _model.title;
    self.descLabel.text  = _model.desc;
    
    [self layoutIfNeeded];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    [self.titleLabel sizeToFit];
    self.titleLabel.frame = CGRectMake(0, 0, self.titleLabel.frame.size.width + 6, self.titleLabel.frame.size.height + 2);
    
    // 设置富文本:首行缩进
    NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc]init];
    style.firstLineHeadIndent = self.titleLabel.frame.size.width + 4;
    NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:self.descLabel.text];
    [attrString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, self.titleLabel.text.length)];
    
    self.descLabel.attributedText = attrString;
    
    self.descLabel.frame = CGRectMake(0, 0, self.frame.size.width, 30);
    [self.descLabel sizeToFit];
    
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(self.descLabel.frame));
}

@end

3. CQPickTimeAlertView

也就是这整个的模块view:


.h文件:

@interface CQPickTimeAlertView : UIView

@property (nonatomic, strong) NSArray *itemsArray;

@end

.m文件:

@implementation CQPickTimeAlertView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setUpUI];
    }
    return self;
}

- (void)setUpUI {
    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, 100, 30)];
    [self addSubview:titleLabel];
    titleLabel.text = @"温馨提示";
    titleLabel.font = [UIFont systemFontOfSize:14];
    titleLabel.textColor = [UIColor greenColor];
}

- (void)setItemsArray:(NSArray *)itemsArray {
    _itemsArray = itemsArray;
    
    UIView *lastView = nil;
    
    for (int i = 0; i < _itemsArray.count; i ++) {
        CQPickTimeAlertItem *itemView = [[CQPickTimeAlertItem alloc] init];
        [self addSubview:itemView];
        if (!lastView) {
            itemView.frame = CGRectMake(5, 40, self.frame.size.width-10, 0);
        } else {
            itemView.frame = CGRectMake(5, CGRectGetMaxY(lastView.frame) + 5, self.frame.size.width-10, 0);
        }
        itemView.model = _itemsArray[i];
        lastView = itemView;
    }
    
    // 调整自身高度
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(lastView.frame)+10);
    
    // 虚线边框
    CAShapeLayer *imaginaryLine = [CAShapeLayer layer];
    imaginaryLine.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    imaginaryLine.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:0].CGPath;
    imaginaryLine.lineWidth = 1. / [[UIScreen mainScreen] scale];
    imaginaryLine.lineDashPattern = @[@2, @2];
    imaginaryLine.fillColor = [UIColor clearColor].CGColor;
    imaginaryLine.strokeColor = [UIColor grayColor].CGColor;
    [self.layer addSublayer:imaginaryLine];
}

@end

注:以上是我demo里的代码,没有用任何三方或category,命名也比较随意,大家就别吐槽了。主要看思路。

相比16年的代码有何进步?

极大的减少了C层的代码,更加简洁及优雅。

现在创建这个模块只需几行代码:

CQPickTimeAlertView *aview = [[CQPickTimeAlertView alloc] initWithFrame:CGRectMake(50, 100, self.view.frame.size.width - 100, 20)];
[self.view addSubview:aview];
aview.itemsArray = modelsArray;

分层更清晰了修改自然也就更容易了。

有用的知识点

1.首行缩进:

NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc]init];
style.firstLineHeadIndent = self.titleLabel.frame.size.width + 4;

这个很关键,要不然处理下面这两个label的时候你可能会走弯路。

2.虚线边框

CAShapeLayer:

CAShapeLayer *imaginaryLine = [CAShapeLayer layer];
imaginaryLine.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
imaginaryLine.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:0].CGPath;
imaginaryLine.lineWidth = 1. / [[UIScreen mainScreen] scale];
imaginaryLine.lineDashPattern = @[@2, @2];
imaginaryLine.fillColor = [UIColor clearColor].CGColor;
imaginaryLine.strokeColor = [UIColor grayColor].CGColor;
[self.layer addSublayer:imaginaryLine];

Demo

https://github.com/CaiWanFeng/BeautifulView

demo效果

总结

看待view要像看待模块一样;
对待装备要像对待情人一样。

死肥宅.gif

推荐阅读更多精彩内容