iOS开发 - 链式编程的应用X_Kit

2019-03-18  本文已影响0人  来者可追文过饰非
1.发现问题

假如我们写一个UILabel,我们的代码会可能会如下所示

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    label.text = @"Hello world!";
    label.textColor = [UIColor whiteColor];
    label.font = [UIFont systemFontOfSize:15];
    label.textAlignment = NSTextAlignmentCenter;
    label.backgroundColor = [UIColor redColor];
    label.layer.cornerRadius = 25;
    label.clipsToBounds = YES;
    [self.view addSubview:label];

这段代码有什么问题呢? 没错!就是非常臃肿,像这些简单的模块如果不加以封装,项目中代码量将会非常多,既不利于阅读,也不利于维护.

2.初步封装

在分类中添加方法

// .h
+ (UILabel *)labelWithText:(NSString *)text textColor:(UIColor *)color font:(UIFont *)font textAlign:(NSTextAlignment)align bgColor:(UIColor *)bgColor cornerRadius:(CGFloat)radius .......;

// .m
+ (UILabel *)labelWithText:(NSString *)text textColor:(UIColor *)color font:(UIFont *)font textAlign:(NSTextAlignment)align bgColor:(UIColor *)bgColor cornerRadius:(CGFloat)radius ....... {
    UILabel *label = [[UILabel alloc] init];
    label.text = text;
    label.textColor = color;
    ...
    return label;
}

这样的话,我们每次在初始化一个控件的时候,只需要1行代码就可以基本完成问题.但是这样写也有一个新的问题,就是可拓展性的问题.
-----假如我们只需要设置text和textColor,我们需要写上这么一行,然后后边参数传空
-----假如我们需要另外设置numOfLines或者其他没有封装到的属性,我们也需要写上这么一行,然后额外设置其他属性

3.继续优化

最近再看Masonry源码,其中的链式编程思想对我有很大的启发,我想是否可以采用block的形式设置属性,并用链式编程将他它们链接起来呢?
说干就干!
.h文件

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

typedef UILabel* (^xl_Block)(id);

@interface UILabel (X)

@property (nonatomic, copy, readonly) xl_Block x_text;
@property (nonatomic, copy, readonly) xl_Block x_font;
@property (nonatomic, copy, readonly) xl_Block x_textColor;
@property (nonatomic, copy, readonly) xl_Block x_align;


@property (nonatomic, copy, readonly) xl_Block x_numOfLines;
@property (nonatomic, copy, readonly) xl_Block x_shadowOffset;
@property (nonatomic, copy, readonly) xl_Block x_lineBreakMode;
@property (nonatomic, copy, readonly) xl_Block x_attributedText;

@end

NS_ASSUME_NONNULL_END

.m文件

#import "UILabel+X.h"
#import "XKit.h"
@implementation UILabel (X)

- (xl_Block)x_text
{
    xl_Block block = ^UILabel* (NSString *text) {
        self.text = text;
        return self;
    };
    return block;
}

- (xl_Block)x_font {
    xl_Block block = ^UILabel* (UIFont *font) {
        self.font = font;
        return self;
    };
    return block;
}

- (xl_Block)x_textColor {
    xl_Block block = ^UILabel* (UIColor *color) {
        self.textColor = color;
        return self;
    };
    return block;
}

- (xl_Block)x_align {
    xl_Block block = ^UILabel* (NSNumber *align) {
        self.textAlignment = align.integerValue;
        return self;
    };
    return block;
}

- (xl_Block)x_numOfLines {
    xl_Block block = ^UILabel* (NSNumber *num) {
        self.numberOfLines = num.integerValue;
        return self;
    };
    return block;
}

- (xl_Block)x_shadowOffset {
    xl_Block block = ^UILabel *(NSValue *value) {
        self.shadowOffset = value.CGSizeValue;
        return self;
    };
    return block;
}

- (xl_Block)x_lineBreakMode {
    xl_Block block = ^UILabel *(NSNumber *mode) {
        self.lineBreakMode = mode.integerValue;
        return self;
    };
    return block;
}

- (xl_Block)x_attributedText {
    xl_Block block = ^UILabel *(NSAttributedString *attr) {
        self.attributedText = attr;
        return self;
    };
    return block;
}
@end

这样,我们的代码便可以这样写

    UILabel *label = [UILabel new];
    label
    .x_text(@"Hello world!")
    .x_textColor([UIColor whiteColor])
    .x_font([UIFont systemFontOfSize:15])
    .x_align(@(NSTextAlignmentCenter))
    .x_sizeToFit(@(YES));
    
    label
    .x_cornerRadius(@(15))
    .x_clipToBounds(@(YES))
    .x_backgroundColor([UIColor redColor]);
    [self.view addSubview:label];

是不是很神奇😆

4.项目地址

具体源码可以去我的github上下载
前往github
希望大家多提意见和建议,可以的话给个小星星奖励

5.存在的问题

1.以UILabel 和 UIView为例
写分类方法的时候,我将
UILabel的属性写到UILabel+X中,其中block返回的为UILabel对象
UIView的属性写到UIView+X中,其中的block返回的为UIView对象
所以就会出现例如label.x_backgroundColor([UIColor whiteColor]) 之后再去调用UILabel的属性后报错的情况

目前暂时的解决方法:写的时候要先写UILabel的属性,再写UIView的属性,例如:

    UILabel *label = [UILabel new];
    label
    .x_text(@"Hello world!")
    .x_textColor([UIColor whiteColor])
    .x_font([UIFont systemFontOfSize:15])
    .x_align(@(NSTextAlignmentCenter))
    .x_sizeToFit(@(YES));
    // 上边为UILabel的属性,下边为父类UIView的属性
    .x_cornerRadius(@(15))
    .x_clipToBounds(@(YES))
    .x_backgroundColor([UIColor redColor]);
    [self.view addSubview:label];

2.以UITableView为例,如果想使用链式及block的形式实现UITableView的代理方法,便会自动设置delegate 和 datasource为当前的tableview,并走UITableView+X中实现的delegate 和 datasource方法,之前设置的delegate 和 dataSource 将会无效,例如:

UITableView *table = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
    table
    .x_rowHeight(@60)
    .x_sectionHeaderHeight(@0.01)  // 在iOS11之后需要设置该属性,才会走heightForHeader的代理方法
    .x_sectionFooterHeight(@0.01) // 在iOS11之后需要设置该属性,才会走heightForFooter的代理方法
    .x_registerCell([UITableViewCell class], cellID)
    .x_delegate(self)
    .x_dataSource(self)
    // 如果实现了delegate 或者 datasource的block,就会自动设置当前为delegate和datasource为table本身,并走UITableView+X中实现的delegate 和 datasource方法,之前设置的delegate 和 dataSource 将会无效
    .x_viewForHeaderInSection(^UIView * _Nonnull(UITableView * _Nonnull table, NSInteger section) {
        UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, 200)];
        header.backgroundColor = [UIColor redColor];
        return header;
    })
    .x_viewForFooterInSection(^UIView * _Nonnull(UITableView * _Nonnull table, NSInteger section) {
        UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, 200)];
        footer.backgroundColor = [UIColor blueColor];
        return footer;
    })
    .x_heightForFooterInSection(^NSInteger(UITableView * _Nonnull table, NSInteger section) {
        return 20;
    })
    .x_heightForHeaderInSection(^NSInteger(UITableView * _Nonnull table, NSInteger section) {
        return 20;
    })
    .x_numofSectionsInTableView(^NSInteger(UITableView * _Nonnull table) {
        return self.dataSource.count;
    })
    .x_numOfRowsInSection(^NSInteger(UITableView * _Nonnull table, NSInteger section) {
        return [self.dataSource[section] count];
    })
    .x_cellForRowAtIndexPath(^UITableViewCell * _Nonnull(UITableView * _Nonnull table, NSIndexPath * _Nonnull indexpath) {
        UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:cellID forIndexPath:indexpath];
        cell.textLabel.text = self.dataSource[indexpath.section][indexpath.row];
        return cell;
    })
    ;
    
    [self.view addSubview:table];

3.后续我们持续更新该框架,感谢大家支持!

上一篇下一篇

猜你喜欢

热点阅读