封装同步的UIActionSheet

发问题

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

- (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];
} - (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 文件:

// SynchronizedUIActionSheet.h
#import <Foundation/Foundation.h> @interface SynchronizedUIActionSheet : NSObject<UIActionSheetDelegate> @property (nonatomic, strong) NSArray * titles;
@property (nonatomic, assign) NSInteger destructiveButtonIndex;
@property (nonatomic, assign) NSInteger cancelButtonIndex; - (id)initWithTitles:(NSArray *)titles; - (NSInteger)showInView:(UIView *)view; @end

SynchronizedUIActionSheet.m 文件:

#import "SynchronizedUIActionSheet.h"

@implementation SynchronizedUIActionSheet {
UIActionSheet * _actionSheet;
NSInteger _selectedIndex;
} @synthesize titles = _titles;
@synthesize destructiveButtonIndex = _destructiveButtonIndex;
@synthesize cancelButtonIndex = _cancelButtonIndex; - (id)initWithTitles:(NSArray *)titles {
self = [super init];
if (self) {
_titles = titles;
_destructiveButtonIndex = 0;
_cancelButtonIndex = titles.count - 1;
}
return self;
} - (void)setTitles:(NSArray *)titles {
_titles = titles;
_cancelButtonIndex = titles.count - 1;
} - (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;
} - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
_selectedIndex = buttonIndex;
_actionSheet = nil;
CFRunLoopStop(CFRunLoopGetCurrent());
} @end

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

- (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 来使我们的异步方法调用暂停在某一行,直到获得用户的反馈后,再往下执行,示例代码如下:

- (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. div设置边框黑框显示

    style="width:756px; height:68px; border:1px solid #000000;"

  2. powerdesigner奇淫技

    在日常开发中数据库的设计常常需要建立模型,而powerdesigner是个不错的选择.但很多时候用powerdesigner生成模型后再去创建表结构,会觉得烦和别扭.那么能不能数据库表建好后再生成模型 ...

  3. Heap:Moo University - Financial Aid(POJ 2010)

       牛的学校 题目大意:这只Bessie真是太顽皮了,她又搞了个学校,准备招生,准备通过一个考试筛选考生,但是不能招到每个学生,每个学生也不能一定能上学,要资助,问你在一定资金内,怎么收学生,使收到 ...

  4. codeforces C. Fixing Typos 解题报告

    题目链接:http://codeforces.com/problemset/problem/363/C 题目意思:纠正两种类型的typos.第一种为同一个字母连续出现3次以上(包括3次):另一种为两个 ...

  5. Cocos2d-JS游戏导演

    什么是游戏的导演 具体来说,Cocos2d-JS中的导演是一个对象,它负责设置游戏的运行环境,控制游戏的住循环并且管理场景和场景的切换. 导演的任务 Cocos2d-JS中导演对象名为:cc.dire ...

  6. 图像边缘检测——Sobel算子

    边缘是图像最基本的特征,其在计算机视觉.图像分析等应用中起着重要的作用,这是因为图像的边缘包含了用于识别的有用信息,是图像分析和模式识别的主要特征提取手段. 1.何为“图像边缘”? 在图像中,“边缘” ...

  7. ImageLoader实现图片异步加载

    ImageLoader是一个广泛使用的图片库,在向网络请求图片时,使用imageView和smartView常会产生outofmemory错误,这时ImageLoader可以起到很大的作用,主要有如下 ...

  8. python实现简单爬虫抓取图片

    最近在学习python,正如大家所知,python在网络爬虫方面有着广泛的应用,下面是一个利用python程序抓取网络图片的简单程序,可以批量下载一个网站更新的图片,其中使用了代理IP的技术. imp ...

  9. Java Hour 24 JDBC

    有句名言,叫做10000小时成为某一个领域的专家.姑且不辩论这句话是否正确,让我们到达10000小时的时候再回头来看吧. 虽然现在都不会直接写JDBC 了,但是了解毕竟是需要的.当然这个和ADO.Ne ...

  10. 随机sample文件Python程序

    经常遇到由于样本巨大,需要sample一部分文件进行案例分析的情况,下面的程序主要为了随机抽取一个大文件中的N行. #!/usr/bin/python # -*- coding: <encodi ...