所闻所获2:使用块回调来实现代理的功能

首先回顾一下代理模式,它的基本说明如下图:

控制器先成为子控件的代理(delegate)并实现相应的代理方法,那么子控件在运作的过程中,遇到某些需要控制器进行配合的场景时,就可以通过delegate属性调用对应场景的代理方法,实现让控制器进行对应操作的效果。

块回调的基本模式如下图:

块回调方法的模型是这样的:在一个方法B内嵌套另一个方法C,当其他地方的方法A使用B这个方法的时候,B方法能够通过C来将一些信息传递回给A方法,A自己就可以拿到传回来的这些信息进行操作(C的调用在B内,具体实现在A内)。

下面以一个自定义的segmentControl为例来演示如何使用块回调来实现代理的效果,要求点击这个segmentControl上的按钮后,页面会有相应反应。它的效果如下图:

具体代码可在以下链接下载:

blockCallbackReplaceDelegate.zip

首先看看使用代理模式的实现方法:

(1)、首先定义segmentControl,这个自定义控件是根据初始化方法传进来的frame和按钮标题数组把整个segmentControl等分为对应个数的UIButton,这个最主要的初始化入口方法如下:

- (instancetype)initWithTitles:(NSArray *)titles frame:(CGRect)frame delegate:(id<SYSegmentControlDelegate>) theDelegate {
    if (self = [super initWithFrame:frame]) {
        ...//把传进来的参数赋给自身属性
        self.delegate = theDelegate;//把传进来的控制器设为代理
        [self configView];//初始化segmentControl的各部分布局
        return self;
    }
    return nil;
} 

(2)、然后在segmentControl里的UIButton的点击监听方法如下:

-(void)buttonClick:(UIButton *)button{
    self.selectedIndex = button.tag; //记录下点击的是哪个按钮
    [self refreshButton]; //根据记录的内容刷新按钮的显示
    //然后使用delegate调用代理方法,让控制器执行对应代码

    if ([self.delegate respondsToSelector:@selector(segmentControl:didSelectedIndex:)]) {
        [self.delegate segmentControl:self didSelectedIndex:self.selectedIndex];
    }
}

(3)、在控制器中,viewDidLoad方法首先调用了usingDelegate方法来初始化segmentControl,usingDelegate方法的代码如下:

- (void)usingDelegate {
    NSArray *items = ...
    CGRect segmentControlFrame = ...
    SYSegmentControl *segmentControl = [[SYSegmentControl alloc]initWithTitles:items frame:segmentControlFrame delegate:self];
    [self.view addSubview:segmentControl];

    //refreshView方法会根据self.selectedIndex的内容决定显示什么内容
    [self refreshView];
}

同时这种模式还需要代理方法的配合,控制器需要实现对应的代理方法,如下:

- (void)segmentControl:(SYSegmentControl *)segmentControl didSelectedIndex:(NSInteger)index {
    self.selectedIndex = index;
    [self refreshView];
}

在整个过程中,当segmentControl中的某个按钮被点中之后,按钮的点击监听方法会被调用,在这个方法中使用delegate(也就是控制器)去调用对应的代理方法,在控制器实现的代理方法中,根据segmentControl传过来的参数去刷新页面,显示对应内容。

然后看看使用块回调模式的实现方法:

(1)、块回调模式中,segmentControl的初始化入口方法和代理模式的有所区别,代码如下:

- (instancetype)initWithTitles:(NSArray *)titles frame:(CGRect)frame callback:(callbackBlock)block{
    if (self = [super initWithFrame:frame]) {
        ...//把传进来的参数赋给自身属性
        self.block = block; //把传进来的块赋给自身属性

        [self configView];
        return self;
    }
    return nil;
}

(2)、segmentControl的按钮点击方法修改如下:

//两种模式共用一个点击触发方法,在方法内判断模式类型
-(void)buttonClick:(UIButton *)button{
    ...
    if (...) {
        //省略代理模式的代码
        ...

    }else if (self.block) {
        //在此处进行块回调
        self.block(self.selectedIndex);
    }
}

(3)、在控制器中,viewDidLoad方法首先调用了usingBlockCallback方法来初始化segmentControl,代码如下:

- (void)usingBlockCallback {
    NSArray *items = ...
    CGRect segmentControlFrame = ...
    __weak ViewController *weakself = self;
    SYSegmentControl *segmentControl = [[SYSegmentControl alloc]initWithTitles:items frame:segmentControlFrame callback:^(NSInteger index) {
        //块回调会回调到下面这两句代码
        weakself.selectedIndex = index;
        [weakself refreshView];
    }];
    [self.view addSubview:segmentControl];

    [self refreshView];
}

在这个过程中,控制器在初始化segmentControl的时候就将需要回调的代码也定义好了,segmentControl在需要控制器配合的时候,可以直接调用块实现回调控制器里的块代码的效果,控制器只需在块代码里定义好对应操作即可。

对比两种方法可以发现,使用块回调回比使用代理更加轻便,因为不需要去声明协议以及代理方法,也不用去实现代理方法,只需把要回调的内容定义在块参数里即可。

但是在使用场景比较复杂的情况下,块代码回调这种方式就不如代理模式了。比如说要实现在segmentControl刚开始点击、刚放开点击或者在某个特定按钮状态下点击另一个按钮等这些场景中让控制器做相应反应,那么块回调方式实现起来就不如代理模式了,需要定义多个块参数以对应多种回调情况,会让segmentControl的初始化方法显得臃肿。这时候使用代理模式可以比较清晰区分各个场景的逻辑。

时间: 2024-10-25 02:59:57

所闻所获2:使用块回调来实现代理的功能的相关文章

代码块回调

所谓的代码块的回调,本质上就是类B调用方法Method1(blockParams),类A将代码块的值blockData传入形参blockParams中,(也就是所谓的实现在类A中),类B中使用blockData将具体的参数传入blockData,实现功能. 类B在使用代码块时并不需要知道其具体的值,只是当作一种数据类型使用,真正的值是在类A中,也就是说先使用了类型,具体的值后面传进来,这就是所谓的代码块回调. 反过来看就是,代码块作为一种数据类型,正常使用,但是具体的值在以后传入.粗浅的见解 V

所闻所获3:下拉刷新控件1

本文主要是讨论在最近项目中遇到的一个下拉刷新控件,这个控件的效果如下图: 在这里会用两篇博文的篇幅来解析这个控件,第一篇解析控件的框架,第二篇解析动画.源代码可以在下面的链接下载: TestPullToRefresh.zip 1.这个控件由以下几个文件组成:GMPullToAction.CircleProgressView.GMActivityView,其中GMPullToAction文件包含两个类:GMPullToRefresh和UIScrollView (GMPullToAction),Ci

所闻所获6:meditashayne项目总结

项目源码下载地址: https://github.com/ShayneYeorg/Meditashayne 1.首先一开始设计这个App的时候,我就希望它能比系统自带的备忘录更方便:比如备忘录需要手动去点击一下保存,我希望我的App可以省略掉点击保存这一步,只需要退出随笔的详情页面便可自动保存内容. 那么退出详情页面有两种方式:左上角的“返回”以及UIViewController自带的左划退出. (1).在处理完“返回”按钮之后,发现了第一个问题:自定义NavigationController上

所闻所获4:下拉刷新控件2

在上一篇博文讨论了下拉刷新控件的框架,这一篇博文将会主要讨论刷新过程中控件的动画效果. 1.首先回顾一下在GMPullToRefresh类中的初始化方法: - (id)initWithScrollView:(UIScrollView *)scrollView { //初始化 ... //定制提示文字 ... //矩形上升动画图 self.activityView=[self activityIndicatorView]; //圆圈转动动画 self.circleView=[[CircleProg

C++教程之lambda表达式一

什么是Lambda? C++ 11加入了一个非常重要的特性--Lambda表达式.营里(戴维营)的兄弟都对Objective-C很熟悉,许多人多block情有独钟,将各种回调函数.代理通通都用它来实现.甚至有人选择用FBKVOController.BlocksKit等开源框架将KVO.控件事件处理都改为通过block解决.原因就是简单.方便.直观,函数的定义和使用出现在同一个地方.这里的Lambda表达式实际上和block非常类似,当然如果你用它和Swift语言的闭包比较,那就是一回事了.下面先

OC教程6-代码块block回调

OC6-代码块回调 本章教程主要对代码块回调模式进行讲解,已经分析其他回调的各种优缺点和适合的使用场景. 代码块机制 Block变量类型 Block代码封装及调用 Block变量对普通变量作用域的影响 Block回调接口使用 1,代码块机制 苹果公司在iOS4 SDK中首次支持代码块机制,随后代码块机制被广泛应用于各种编码场景,最常见的为回调机制,也成为Block回调. 代码块也称Block.是封装代码的一种机制,也可以称为匿名函数. 使用这种机制可以将一段代码放入一个Block变量中进行存储,

IOS_地图_定位_天气预报_Block回调_单例

H:/1021/00_block回调.h /* 通过block回调 定义block代码块,目的是解析完成之后调用 返回值是 void 参数是 数组,里面的每个成员是一个NSString*/ typedef void(^WeatherFinishedBlock)(NSArray *dataList); @interface WeatherXMLPaser : NSObject // 解析器解析数据,参数1是要解析的数据,参数2是解析完毕回调的代码块 - (void)parserWeatherDat

[objective-c] 05 - 委托模式回调

本章主要讲解委托模式以及通过委托模式实现的回调接口. 1.委托模式 委托模式是OC语法独有的开发模式.是基于组件拼装的一种快速开发模式.该模式下,可以保证组件的高度灵活性和通用性.属于组件的一种开放式接口. 下面通过一个现实生活中的场景简单理解下委托模式的应用. 例如我们现在有一个公司.公司想要进行IPO.可是公司老总并不熟悉资本操作,这时候就需要委托一个人或者一个机构来作这件事. 公司首先要提出能做IPO这件事的详细要求,然后通过猎头寻找合适的人选. 用代码描述应该是这样的: 首先用协议描述公

iOS_JSON_XML_图片内存缓存_Block回调

H:/1010/00_JSON_XML_MainViewController.m // MainViewController.m // JSON & XML // Created by apple on 13-10-10. /* 异步加载网络图像的内存缓存解决方法 1. 在对象中定义一个UIImage 2. 在控制器中,填充表格内容时,判断UIImage是否存在内容 1> 如果cacheImage不存在,显示占位图像,同时开启异步网络连接加载网络图像 网络图像加载完成后,先设置对象的cach