OC 链式编程第一部分 - UI创建篇

通常我们在创建UI的时候,会初始化UI对象,然后设置一大堆的属性,如果给控件添加事件,所添加的selector还要分开来写;以一个button来为例,如果一个页面内有很多的Button,每个button都添加一个selector,这样积累多了,不仅代码不美观,在维护上也很头痛。所以我们引入链式编程,实现UI创建时候,通过点语法来创建。

本篇文章代码https://github.com/zfc769956454/ChainedDemo(下载下来运行ChainedDemoUI控件链式创建这个target)

image.png

1. 链式编程

说到链式编程,做过iOS开发的都知道,我们在做UI布局使用的masonry,或者SDAutoLayout都是使用了链式编程的思想。其核心思想就是某个对象A,里面有很多的函数,每个函数的返回值是个block,block的返回值还是该对象A。

2. UI链式创建

接下来就是本文的核心,先上一段代码(以button为例),然后我们再分析实现过程

UIButton *button = [UIButton ZFC_ButtonChainedCreater:^(ZFC_ButtonChainedCreater *creater) {
        creater.backgroundColor([UIColor cyanColor])
        .tag(3)
        .frame(CGRectMake(getCenterX(100), CGRectGetMaxY(imageView.frame) + 20, 100, 40))
        .titleLabelFont([UIFont systemFontOfSize:15])
        .title(@"未选中按钮", UIControlStateNormal)
        .title(@"未选中按钮", UIControlStateSelected)
        .titleColor([UIColor lightGrayColor], UIControlStateNormal)
        .titleColor([UIColor redColor], UIControlStateSelected)
        .layerCornerRadius(5)
        .layerBorderWidthAndBorderColor(1, [UIColor blueColor])
        .addIntoView(self.view)
        .actionBlock(^(UIButton *button) {
            button.selected = !button.selected;
            NSLog(@"-------点击了button--------");
        });
    }];

在这段代码中,我们可以看到,一个button的创建全是.语法出来的,而且包含了button常用的属性比如frame、title、backgroundColor等等,而且button的点击事件也是和button的创建在一块,看起来不仅简洁,而且维护起来很方便。

3. 过程分析

基于以上代码,我们来分析一下。
3.1 我们先从.语法开始分析,首先creater.backgroundColor([UIColor cyanColor]),create是一个对象,通过.语法调用,那么可以确定backgroundColor是其属性,属性调用.语法要么是setter方法、要么是getter方法,此处就是getter方法
3.2 backgroundColor([UIColor cyanColor])后面是一个()调用,[UIColor cyanColor]是个实参,()调用的我们常用的是一个函数,另外一个就是block,上一步我们已经知道了backgroundColor是一个属性的getter方法,我们知道普通的属性(比如name是named)的getter方法是没有参数的,所以我们排除了它是一个普通的属性,那么它就是一个block作为对象的属性
3.3 在backgroundColor([UIColor cyanColor])后面我们可以看到它又调用了一个.tag(3),说明backgroundColor([UIColor cyanColor])的返回值还是一个creater对象
3.4 通过上面的分析我们可以很清晰的看出backgroundColor是一个block,参数是UIColor *类型,返回值是create对象,这不正是链式编程的核心思想嘛。
3.5 我们来看看源码中的定义

@property (nonatomic,copy,readonly)ZFC_ButtonChainedCreater *(^backgroundColor)(UIColor *backgroundColor);

基于以上分析,我们都可以将button常用的属性通过这种方式实现

4. 难点

基于上面的过程,我们可以把button的常用属性通过链式编程的方式很快的写出来,但是button的点击事件我们怎么把它和链式编程联系起来呢?
4.1 我们找到切入点,链式编程嘛,那么我们还是仿造上面的方式写出一个actionBlock的属性

@property (nonatomic,copy,readonly)ZFC_ButtonChainedCreater *(^actionBlock)();

4.2 上面一步我们已经设置了button的block,但是我们的真实目的是将button的事件也转成链式调用,不要再通过addTarget的方式,将selector和button分开。其实通过我们日常开发的经验来说,button的target就是一个函数,说到函数我们完全可以用block来代替(block的本质就是一个函数),所有我们就传递一个block作为actionBlock的参数,所以变成下面的

@property (nonatomic,copy,readonly)ZFC_ButtonChainedCreater *(^actionBlock)(ZFC_CreaterButtonActionBlock actionBlock)

ZFC_CreaterButtonActionBlock是我定义的一个typedef,如下

typedef void(^ZFC_CreaterButtonActionBlock)(UIButton *button);

4.3 基本工作已经做完了,接下来就是我们怎么把button的点击事件转化成actionBlock了,这里我通过在创建button的时候,调用一个函数,函数的参数是一个blockA,然后在函数内部为button添加target-selector,并通过runtime的方式关联这个blockA,在button的target方法里面执行blockA,这样就把button的target事件传递到了button创建的地方,然后在这个地方调用actionBlock(),这样就把事件通过block的方式传递出去,主要代码

- (UIButton *)chainedButton {
    if(_chainedButton == nil){
        _chainedButton = [ZFC_CusNoHightedButton new];
        //执行函数 ->参数是个blockA
        [_chainedButton creater_actionBlock:^ (UIButton *button){
            //blockA的回调
            if(self.keepActionBlock){ //keepActionBlock就是上面的actionBlock
                self.keepActionBlock(button);
            }
        } controlEvent:UIControlEventTouchUpInside];
    }
    return _chainedButton ;
}
- (ZFC_ButtonChainedCreater *(^)(ZFC_CreaterButtonActionBlock))actionBlock {
    return ^ZFC_ButtonChainedCreater *(ZFC_CreaterButtonActionBlock actionBlock) {
        if(actionBlock){
            self.keepActionBlock = actionBlock;
        }
        return self;
    };
}
-(void)creater_actionBlock:(ZFC_CreaterButtonActionBlock)byValueBlock controlEvent:(UIControlEvents )event {
    if(byValueBlock){
        objc_setAssociatedObject(self, @selector(buttonClickAction:), byValueBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    [self addTarget:self action:@selector(buttonClickAction:) forControlEvents:event];
}

- (void)buttonClickAction:(UIButton *)button {
    
    ZFC_CreaterButtonActionBlock byValueBlock = objc_getAssociatedObject(self, _cmd);
    
    if(byValueBlock){
        byValueBlock(button);
    }
}

以上就是整个button的链式创建的主要步骤,随便提一下ZFC_ButtonChainedCreater这里是给button创建了一个分类,参数就是ZFC_ButtonChainedCreater这个类,返回值是对应的UI控件的类

5. 其它UI控件的链式创建

    //view
    UIView *chainedView = [UIView ZFC_ViewChainedCreater:^(ZFC_ViewChainedCreater *creater) {
        creater.backgroundColor([UIColor cyanColor])
        .frame(CGRectMake(getCenterX(100), 100, 100, 40))
        .tag(1)
        .isUserTapTapGesture(YES)
        .addIntoView(self.view)
        .layerCornerRadius(5)
        .tapBlock(^(UIView *view) {
            NSLog(@"-------点击了view--------");
        });
    }];
    
    //label
    UILabel *label = [UILabel ZFC_LabelChainedCreater:^(ZFC_LabelChainedCreater *creater) {
        creater.backgroundColor([UIColor cyanColor])
        .frame(CGRectMake(getCenterX(100), CGRectGetMaxY(chainedView.frame) + 20, 100, 40))
        .tag(2)
        .text(@"我是文字")
        .textColor([UIColor blackColor])
        .font([UIFont systemFontOfSize:15])
        .textAlignment(NSTextAlignmentCenter)
        .numberOfLines(1)
        .addIntoView(self.view)
        .layerCornerRadius(5)
        .tapBlock(^(UILabel *label) {
            NSLog(@"-------点击了label--------");
        });
    }];
    
    //imageView
    UIImageView *imageView = [UIImageView ZFC_ImageViewChainedCreater:^(ZFC_ImageViewChainedCreater *creater) {
        creater.backgroundColor([UIColor cyanColor])
        .frame(CGRectMake(getCenterX(100), CGRectGetMaxY(label.frame) + 20, 100, 40))
        .tag(2)
        .image([UIImage imageNamed:@"test.jpg"])
        .layerCornerRadius(5)
        .addIntoView(self.view)
        .tapBlock(^(UIImageView *imageView) {
             NSLog(@"-------点击了imageView--------");
        });

    }];
    
    //textField
    UITextField *textField = [UITextField ZFC_TextFieldChainedCreater:^(ZFC_TextFieldChainedCreater *creater) {
        creater.backgroundColor([UIColor cyanColor])
        .tag(4)
         .frame(CGRectMake(getCenterX(100), CGRectGetMaxY(button.frame) + 20, 200, 40))
        .placeholder(@"请输入文字...")
        .font([UIFont systemFontOfSize:15])
        .keyboardType(UIKeyboardTypeDefault)
        .clearButtonMode(YES)
        .layerBorderWidthAndBorderColor(1, [UIColor blackColor])
        .layerCornerRadius(5)
        .addIntoView(self.view);
    }];
    
    //textView
   [UITextView ZFC_TextViewChainedCreater:^(ZFC_TextViewChainedCreater *creater) {
        creater.backgroundColor([UIColor cyanColor])
        .tag(5)
        .placeholder(@"请输入文字...")
        .font([UIFont systemFontOfSize:15])
        .layerBorderWidthAndBorderColor(1,  [UIColor blackColor])
        .frame(CGRectMake(getCenterX(100), CGRectGetMaxY(textField.frame) + 20, 200, 100))
        .addIntoView(self.view);
    }];

     //tableView
     self.tableView = [UITableView ZFC_TableViewChainedCreater:^(ZFC_TableViewChainedCreater *creater) {
        creater.frameAndStyle(self.view.bounds, UITableViewStylePlain)
        .backgroundColor([UIColor whiteColor])
        .tag(1)
        .separatorStyleAndColor(UITableViewCellSeparatorStyleSingleLine, [UIColor blueColor])
        .separatorInset(UIEdgeInsetsMake(0, 30, 0, 30))
        .rowHeight(60)
        .sectionFooterHeight(40)
        .sectionFooterHeight(30)
        .tableHeaderView(headerView)
        .tableFooterView(footerView)
        .addIntoView(self.view)
        ;
    }];

    //collectionView
    self.collectionView = [UICollectionView ZFC_CollectionViewChainedCreater:^(ZFC_CollectionChainedCreater *creater) {
        creater.layout_minimumLineSpacing(10)
        .layout_minimumInteritemSpacing(15)
        .layout_itemSize(CGSizeMake(100, 100))
        .layout_headerReferenceSize(CGSizeMake(50, 50))
        .layout_footerReferenceSize(CGSizeMake(50, 50))
        .layout_scrollDirection(UICollectionViewScrollDirectionVertical)
        .layout_sectionHeadersPinToVisibleBounds(YES)
        .layout_sectionHeadersPinToVisibleBounds(YES)
        .frame(self.view.bounds)
        .tag(2)
        .backgroundColor([UIColor whiteColor])
        .addIntoView(self.view);
    }];

6. 总结

以上就是UI的链式创建部分,基于这种思想,对于UITableView/UICollectionView的代理方式进行提取,形成一套链式调用,详情参见:OC 链式编程第二部分 - 调用篇,而且对于复杂tableView的调用也进行了代理方法的抽取,详情参见OC 链式编程第三部分 - 复杂tableView的抽取。最后,我把这三篇文章整理了一套小工具库ZFCChainedCreater,支持pod导入pod 'ZFCChainedCreater', '~> 1.0.2',github地址:https://github.com/zfc769956454/ZFCChainedCreater

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明AI阅读 16,025评论 3 119
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,161评论 1 32
  • 一直以来,我都被朋友们笑称为“文艺青年”,因为我总是喜欢通过文字记录生活,好像各种小事于我而言,都极其的有趣味。 ...
    城市的知了阅读 219评论 2 3
  • 20170213 周一 南昌 晴 气温5-17 轻度污染 昨天下午成长小组又睡了一下午,大家谈了谈过年发生的事情,...
    cai彩阅读 224评论 0 0
  • 今天晚上对小玥玥进行说教,中间问她,知道吗?懂不懂? 她回答:“知道,懂”。萌翻了。 把我的手机指环扣掰断了,然后...
    简宁思静阅读 157评论 0 0