封装同步的UIActionSheet

发问题

做 iOS 开发的同学想必都用过 UIActionSheet。UIActionSheet 可以弹出一个选择列表,让用户选择列表中的某一项操作。使用 UIActionSheet 非常简单,以下是一个简单的示例代码:

  1. - (void)someButtonClicked {
    UIActionSheet * sheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"ddd" destructiveButtonTitle:@"aaa" otherButtonTitles:@"bbb", @"ccc", @"ddd", nil];
    sheet.destructiveButtonIndex = 1;
    [sheet showInView:self.view];
    }
  2.  
  3. - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    int result = buttonIndex;
    NSLog(@"result = %d", result);
    }

但我个人在使用时,感觉 UIActionSheet 有以下 2 个问题:

  1. UIActionSheet 是一个异步的调用,需要设置 delegate 来获得用户选择的结果。这么小粒度的选择界面,把调用显示和回调方法分开写在 2 个方法中,使得原本简单的逻辑复杂了。虽然也不会复杂到哪儿去,但是每次调用 UIActionSheet 就需要另外写一个 delegate 回调方法,让我觉得这是一个过度的设计。如果 UIActionSheet 在弹出界面时,是一个同步调用,在调用完 showInView 方法后,就能获得用户的点击结果,那该多方便。

  2. UIActionSheet 默认的 init 方法比较恶心。cancel Button 其实默认是在最底部的,但是在 init 方法中是放在第一个参数。destructive 默认是列表的第一个。如果你需要的界面不是将 destructive button 放在第一个,就需要再指定一次 destructiveButtonIndex,而这个 index 的下标,是忽略 cancel button 来数的,虽说也不是很麻烦,但是心里感觉比较恶心。

改造 UIActionSheet

基于上面 2 个原因,我想把 UIActionSheet 改造成一个同步的调用。这样,在我调用它的 showInView 方法后,我希望它直接同步地返回用户的选择项,而不是通过一个 Delegate 方法来回调我。另外,我也不希望 init 方法有那么多麻烦的参数,我只希望 init 的时候,指定一个数组能够设置每个 button 的 title 就行了。

于是我写了一个 SynchronizedUIActionSheet 类,这个类将 UIActionSheet 简单封装了一下,利用 CFRunLoopRun 和 CFRunLoopStop 方法来将 UIActionSheet 改造成同步的调用。整个代码如下所示:

SynchronizedUIActionSheet.h 文件:

  1. // SynchronizedUIActionSheet.h
    #import <Foundation/Foundation.h>
  2.  
  3. @interface SynchronizedUIActionSheet : NSObject<UIActionSheetDelegate>
  4.  
  5. @property (nonatomic, strong) NSArray * titles;
    @property (nonatomic, assign) NSInteger destructiveButtonIndex;
    @property (nonatomic, assign) NSInteger cancelButtonIndex;
  6.  
  7. - (id)initWithTitles:(NSArray *)titles;
  8.  
  9. - (NSInteger)showInView:(UIView *)view;
  10.  
  11. @end

SynchronizedUIActionSheet.m 文件:

  1. #import "SynchronizedUIActionSheet.h"
  2.  
  3. @implementation SynchronizedUIActionSheet {
    UIActionSheet * _actionSheet;
    NSInteger _selectedIndex;
    }
  4.  
  5. @synthesize titles = _titles;
    @synthesize destructiveButtonIndex = _destructiveButtonIndex;
    @synthesize cancelButtonIndex = _cancelButtonIndex;
  6.  
  7. - (id)initWithTitles:(NSArray *)titles {
    self = [super init];
    if (self) {
    _titles = titles;
    _destructiveButtonIndex = 0;
    _cancelButtonIndex = titles.count - 1;
    }
    return self;
    }
  8.  
  9. - (void)setTitles:(NSArray *)titles {
    _titles = titles;
    _cancelButtonIndex = titles.count - 1;
    }
  10.  
  11. - (NSInteger)showInView:(UIView *)view {
    _actionSheet = [[UIActionSheet alloc] init];
    for (NSString * title in _titles) {
    [_actionSheet addButtonWithTitle:title];
    }
    if (_destructiveButtonIndex != -1) {
    _actionSheet.destructiveButtonIndex = _destructiveButtonIndex;
    }
    if (_cancelButtonIndex != -1) {
    _actionSheet.cancelButtonIndex = _cancelButtonIndex;
    }
    [_actionSheet showInView:view];
    CFRunLoopRun();
    return _selectedIndex;
    }
  12.  
  13. - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    _selectedIndex = buttonIndex;
    _actionSheet = nil;
    CFRunLoopStop(CFRunLoopGetCurrent());
    }
  14.  
  15. @end

在改造后,调用 ActionSheet 的示例代码如下,是不是感觉逻辑清爽了一些?

  1. - (IBAction)testButtonPressed:(id)sender {
    SynchronizedUIActionSheet * synActionSheet = [[SynchronizedUIActionSheet alloc] init];
    synActionSheet.titles = [NSArray arrayWithObjects:@"aaa", @"bbb", @"ccc", @"ddd", nil];
    synActionSheet.destructiveButtonIndex = 1;
    NSUInteger result = [synActionSheet showInView:self.view];
    NSLog(@"result = %d", result);
    }

总结

利用 NSRunLoop 来将原本的异步方法改成同步,可以使我们在某些情形下,方便地将异步方法变成同步方法来执行。

例如以前我们在做有道云笔记 iPad 版的时候,采用的图片多选控件需要用户允许我们获得地理位置信息,如果用户没有选择允许,那个这个图片多选控件就会执行失败。为了不让这个控件挂掉,我们想在用户禁止访问地理位置时,不使用该控件,而使用系统自带的图片单选的 UIImagePickerController 控件来选择图片。对于这个需求,我们明显就希望将获得地理位置信息这个系统确认框做成同步的,使得我们可以根据用户的选择再决定用哪种图片选择方式。最终,我们也用类似上面的方法,用 NSRunLoop 来使我们的异步方法调用暂停在某一行,直到获得用户的反馈后,再往下执行,示例代码如下:

  1. - (id)someCheck {
    BOOL isOver = NO;
    // do the async check method, after the method return, set isOver to YES
    while (!isOver) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
    return value;
    }

封装同步的UIActionSheet的更多相关文章

  1. c++封装编写线程池

    在csapp学习或者其他linux底层编程的过程中,一般都会举一些多线程或多进程的例子,配合底层同步原语.系统调用api来解释怎么创建多线程/多进程. 但是这些例子和实际项目中所用到的多线程/多进程编 ...

  2. Promise的封装

    要封装Promise,首先要了解Promise的使用. Promise有以下几个特点:1.Promise是一个构造函数 2.实例化Promise时有两个回调函数,resolve,reject ,成功执 ...

  3. iOS面试必看

    转载:http://www.jianshu.com/p/5d2163640e26 序言 目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了.今年,找过工作人可能会更深刻地体会到今年的就业形 ...

  4. Java基础高级二(多线程)

    1.进程和线程的区别:线程是轻量级的,本省不会持太多资源,需要的时候向进程申请 2.线程的状态:创建,可执行,执行中,等待,休眠,阻塞 3.线程状态之间的转换 4.线程API:Thread类,Runn ...

  5. Javascript 中的神器——Promise

    Promise in js 回调函数真正的问题在于他剥夺了我们使用 return 和 throw 这些关键字的能力.而 Promise 很好地解决了这一切. 2015 年 6 月,ECMAScript ...

  6. We have a problem with promises

    原文地址:http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/ 用Javascript的小伙伴们,是时候承认了,关于 p ...

  7. 李洪强iOS经典面试题129

    1. 怎么解决缓存池满的问题(cell) ios中不存在缓存池满的情况,因为通常我们ios中开发,对象都是在需要的时候才会创建,有种常用的说话叫做懒加载,还有在UITableView中一般只会创建刚开 ...

  8. iOS 知识点梳理

    OC的理解与特性 OC作为一门面向对象的语言,自然具有面向对象的语言特性:封装.继承.多态.它既具有静态语言的特性(如C++),又有动态语言的效率(动态绑定.动态加载等).总体来讲,OC确实是一门不错 ...

  9. iOS面试必看,最全梳理

    序言 目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了.今年,找过工作人可能会更深刻地体会到今年的就业形势不容乐观,加之,培训机构一火车地向用人单位输送iOS开发人员,打破了生态圈的动态 ...

随机推荐

  1. spring boot实战(第十二篇)整合RabbitMQ

    前言 最近几篇文章将围绕消息中间件RabbitMQ展开,对于RabbitMQ基本概念这里不阐述,主要讲解RabbitMQ的基本用法.Java客户端API介绍.spring Boot与RabbitMQ整 ...

  2. jquery 常用类别选择器

    1.$('#showDiv'):  id选择器,相当于javascript中的documentgetElementById("showDiv"); 2.$("onecla ...

  3. Greedy:Saruman's Army(POJ 3069)

    2015-09-06 萨鲁曼军队 问题大意:萨鲁曼白想要让他的军队从sengard到Helm’s Deep,为了跟踪他的军队,他在军队中放置了魔法石(军队是一条线),魔法石可以看到前后距离为R的距离, ...

  4. css样式自适应,支持数字

    td加上style="word-break: break-all;word-wrap: break-word;"样式即可

  5. swift复合类型

     1.元组类型 (tuple) 元组就是多个元素的组合,是一个用圆括号括起来分号分隔的多个数据的一个集合体. 例如:定义一个学生变量,要求姓名 jim,年龄 19,性别 male 的元组变量为  // ...

  6. popular net

    陈皓<跟我一起写makefile>http://blog.csdn.net/haoel/article/details/2886/

  7. markdown下编辑latex数学公式

    在利用为知笔记编写笔记的时候,有时需要用的markdown,只要把文件名加上后缀.md,就可以使用markdown语法,以下介绍在markdown下编辑latex数学公式. 使用LaTeX写公式的基本 ...

  8. centos下安装五笔输入法的教程

    [root@ok ~]# yum update [root@ok ~]# yum install ibus-table-chinese-wubi-haifeng 以上两步已经成功!! #yum ins ...

  9. 使用rsync 的 --delete参数删除目标目录比源目录多余的文件

    root@v01 ~]# mkdir dir01 dir02 [root@v01 ~]# ls anaconda-ks.cfg dir02 framework install.log.syslog m ...

  10. Ubuntu could not write bytes broken pipe

    一.环境变量问题 1 到登录界面的时候,进入命令行模式: alt+ctrl+F1 2 登录 3 修改环境变量(当我输入ls的时候  竟然没有找到命令,然后果断的知道是环境变量的事情,于是改之!) 4 ...