模仿UIAlertView,iOS模式对话框

7 Comments

在程序UI设计上,很多情况我们会需要一种模式的对话框来进行操作。现有的iOS提供如下方式满足这种需求:

  1. UIAlertView
  2. UIViewController  : - (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated

但是这两者明显都有不足,比如说UIAlertView官方只能显示title,message, buttons. 如果想要显示一个帐号密码输入框就不行)虽然网络上有一些程序员通过hack的方式实现了, http://stackoverflow.com/questions/376104/uitextfield-in-uialertview-on-iphone-how-to-make-it-responsive, 但是现在还是挺费劲的),所以想要随心所欲的在多样化模式对话框的UI界面标准的lib明显不能满足这个需求。于是我就实现了一个可以任意设置界面的模式对话框,叫 BYDialog.在给这个东西命名上,我想找一个好的前缀,但是一直没有更好的想法,所以就自恋地直接用自己的中文名的拼音开头作了前缀。

BYDialog核心思想:

  1. 新建一个UIWindow,其windowLevel比UIWindowLevelStatusBar大一点,这样就可以让显示的内容完全覆盖status bar了。
  2. UIWindow背景透明,然后添加一个拥有灰灰背景图片的UIImageView,暂且加maskview。这样原来的内容就会有像UIAlertView一样的淡化效果,突出显示模式对话框的内容。
  3. 在UIWindow中添加BYDialog(是UIView的subclass),BYDialog并显示bounce动画效果。

这样就能够像UIAlertView一现显示出来了:)

如何自定义BYDialog用户界面:(具体可以参考TestDialog的实现)

  1. subclass BYDialog, 然后必须改写- (void)loadContentView,并提供你自己想要显示的UI内容,注意这个内容要适当大小,不要超出屏幕大小。
  2. override - (void)willPresentDialog;- (void)didPresentDialog;- (void)willDismissDialog;- (void)didDismissDialog;并响应这种事件消息。
  3. 可以为自己的类提供各种delegate函数,根据以上捕捉到的事件消息或者其他地方,触发delegate函数调用。

所以整个UIAlertView 模仿过程完成了,效果如截图:

---------------------------------------------

源代码地址:

Google Code : http://code.google.com/p/bydialog/

---------------------------------------------

如果你觉得这个很cool的话,在下面顶一下吧 ;)

readonly property的retain和copy

2 Comments

一直以来个人觉得如果一个类是的property是readonly的那么再指定其他的如assign/retain/copy这样的属性就实在是没有什么意义了。确实你想想既然都readonly了,肯定是没有setter的,既然没有setter那么谈assign/retain/copy又有什么意义呢?所以一直以来我从来不对readonly的property加retain/copy属性申明,默认assign就足够了,simple is beautiful!

但是我现在发现我错了,其实一直都有一种这种感觉,只是没有找到100%的充分理由为readonly加上retain/copy。但是假如你要在你的subclass改写property,而加入你要改写的是一个NSString,你像把这个属性设置为readwrite和copy,往往杯具就发生了,编译时候可恶的warning 产生了,因为copy和之前默认的assign明显不相同啊!

终上,不论什么时候,都要为你的readonly的对象属性加上合适的retain/copy申明。你现在不用,但不说明你将来就不会用,出来混迟早都要还的!

Objetive C 开放封闭原则 心得

No Comments

开放封闭原则(OCP)就是,“对扩展开放,对更改封闭”。是所有面向对象设计的一个核心宗旨。感兴趣的可以看百度百科的一些解释:http://baike.baidu.com/view/2493421.htm

在用Objective C进行开发的时候,OCP当然也是宗旨。利用继承,多态是一个很好的保持OCP的办法,也是最常见的一种方法。Objective C还支持另外两种语法来支持OCP:Protocol和Category。(想要了解Objective c语法的可以看这里:http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html)。

Protocol只能定义一套接口,而不能提供实现,变相的也是一种Abstract class的实现方式(oc 语法上本身不支持抽象基类)。Category可以为类提供额外的接口和实现。那么到底三者(继承, Protocol,Category)在使用上到底有什么本质的区别呢?在我看来,protocol的作用是为一些列类仅仅提供一套公用的接口,而完全没有办法也没可能去提供具体的一些实现情况;category则是为一个已有的类提供一些额外的接口和具体实现;而继承则基于两者之间,既可以想protocol一样提供只是纯粹提供接口,也可以像Category一样提供完整的实现,而且继承还能对类以后的功能进行改写,所以说继承的力量是最强大的。那么具体在使用的时候各自都适合什么样的情况呢?

  1. Protocol是定义行为而不管谁去怎么实现,这是一种比较洒脱和不负责的情况,就好像在外包项目中的客户一样,他只是他需要什么什么东西,具体实现他不会也不能给出一样。delegate datasource这样的就用protocol实现比较好
  2. Category是对一个功能完备的类的一种补充,就像是一个东西的主要基本功能都完成了,可以用category为这个类添加不同的组件,使得这个类能够适应不同情况的需求(但是这些不同需求最核心的需求要一致)。找个就像你已经有了一辆能够开动的汽车一样,我们可以用Category为你的汽车添加各种之前没有的功能,最后让这辆汽车变成超级跑车一样。
  3. 继承则是都可以完成上面的工作,但是继承有很大的代价问题,一是通过继承来进行扩展是一种耦合很高的行为,对父类可以说是完全依赖;二是继承由于对父类依赖,所以开发代价相对大,要求对父类的工作流程相对熟悉;三是继承体系如果太复杂会导致整个系统混乱,难以维护。所以在能够用上面两种方法完成扩展的时候,就千万不要使用继承。什么情况才是迫不得已要使用继承呢?那就是如果你既想提供一系列接口的定义,同时又想提供一些但是又不能提供全部的实现的时候,这种情况就要使用继承了。所以这么看来继承是对上面两种功能的一个黏合剂。

之所以会想到这些,是因为自己在写一个webservice加载库的时候得到的体会。这个库已经提供了一系列最基本的加载webservice的功能,但是不同的程序webservice的api和类型都不是相同的,要怎么才能应用到不同程序中去呢?之前我一直想都不想直接继承了,产生一个新的webservice加载器,然后对不同api分别进行实现。但是每次这么做的时候就发现继承的时候做了很多重复而且没有什么创造性的工作。今天才突然发现了问题的所在,既然我的webservice库已经是一个功能基本齐备的加载器了,为什么我不简单的为这个加载器在不同的程序中创建不用的category呢,然后在复用一些已有的api,不就能够完成工作了吗?确实,我现在通过category的方式减少了之前太多太多的重复劳动,真恨自己为什么没有提早注意到这个问题。

NSManagedObject property没有实例变量的模拟

4 Comments

趁现在还记忆犹新,而且还小有兴奋,把自己怎么实现RT话题写下来。

这个问题其实是在我学习CoreData的时候就一直存在,在CoreData里面有一个“很酷”的类NSManagedObject,只要是找个类的子类,那么这些类的property只要申明并且在.m里面申明为dynamic就可以,而不需要为property指定特定的实力变量,用起来特别方便。这让我这个经常实用property的人觉得真的是匪夷所思啊。一直在想找个玩意儿到底是采用了什么魔法,才能够导致property和实力变量之间的依赖可以剥离。而且前段时间和Kevin同学一起讨论了一下,但是都表示没有头绪。

最近又刚好温习了一下Objective-C Runtime Programming Guide这才恍然大悟,于是花了一天的时间就实现出了类似的效果。并且通过这次实践,对动态语言到底有多动态又有了从来没有过的深入认识,这种感觉很过瘾。

具体是怎么实现的不想多讲,原理不难,过程也不复杂,自己看源代码去,但是要求对oc runtime要有所了解。最终的效果就是我实现一个叫SmartObject 的类,只要是从找个类继承下来的类就可以想NSManagedObject的子类那样不用给出实力变量就可以使用property了!具体使用如下:

1
2
3
4
5
6
7
#import "SmartObject.h"
@interface TestClass : SmartObject {
}
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *location;
@property (nonatomic, retain) NSDate *date;
@end
1
2
3
4
5
6
#import "TestClass.h"
@implementation TestClass
@dynamic title;
@dynamic location;
@dynamic date;
@end

结果就可以这样使用了:

1
2
3
4
5
6
7
8
9
10
TestClass *_myObject;
...
_myObject = [[TestClass alloc] init];
_myObject.title = @"this is title";
_myObject.location = @"this is location";
_myObject.date = [NSDate date];
...
NSLog(_myObject.title, nil);
NSLog(_myObject.location, nil);
NSLog([_myObject.date description], nil);

感觉是不是很酷啊~

点击下载源代码

UIView创建不规则视图

No Comments

[申明]本文有点标题党,因为通过本文严格上讲并不能创建一个规则的UIView。
总所周知,UIView都是方形的,并不能产生一个不规则的形状的view(小弟才疏学浅,目前确实没有发现能够创建真实不规则视图的方法,如果有,不吝赐教,感激涕零!)。为什么我们有创建不规则view的需求?如果只是为了在View上显示不规则图形那大可不必,直接讲不规则图形添加到view上,然后讲view的backgroundColor设置为UIColor clearColor就可以;但是之所以有这样的需求,很大部分就是为了判断不规则的图形去响应触摸事件,判断图形是否被触摸选中了这样的要求,我们最直接的想法就是每个不规则图形都是一个view,那么图形是否选中就可以通过UIResponder的那一系列触摸有关的响应函数得知了,所以这个时候我们就需要不规则的view。但是显示的杯具是,iOS并没有提供这样的不规则view,如果要完成刚才的需求,就只能手动判断触摸的点是否在不规则图形里面了,这有的时候将是一件比较痛苦的事情。那么现在提供一种“创建不规则view”的解决方案:

不知道大家有没有注意到UIView有这样一个函数:- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 这个函数就是用来判断指定的点是否在View内,我们方法就是在这个函数中,如果point在指定的不规则图形内返回YES,反之就返回NO。这样不规则图形的bounds就相当于代表了view自己的bounds。这样当你触摸view的时候,当且只有当触摸到指定图形内才会使得view被触摸到,才会调用到UIResponder一系列触摸响应事件。

代码如下:

RoundView.h

1
2
3
4
5
6
7
@interface MyView : UIView {
  UIBezierPath *_path;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

@end

RoundView.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Print current selector's name
#define PRINT_CURRENT_SEL NSLog(@"<%@> %@", NSStringFromClass([self class]),NSStringFromSelector(_cmd))

//
// PrivateMethods
// This category provide private apis for RoundView class
//
@interface RoundView(PrivateMethods)

// Initialize all view's state
- (void)_init;

// Highlight view's border, better visual effect for testing.
- (void) _highlightBorder;

@end
@implementation RoundView(PrivateMethods)

- (void)_init{
  [self _highlightBorder];
  self.backgroundColor = [UIColor clearColor];

  // Create path object as round rect
  _path = [[UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds,  20, 20) cornerRadius:60] retain];
}

- (void) _highlightBorder{
  CALayer *theLayer = [self layer];
  theLayer.borderColor = [UIColor blueColor].CGColor;
  theLayer.borderWidth = 2;
}

@end

#pragma mark -

@implementation RoundView

- (id)initWithCoder:(NSCoder *)aDecoder{
  if((self = [super initWithCoder:aDecoder])){
    [self _init];
  }
  return self;
}

- (id)initWithFrame:(CGRect)frame {
  if ((self = [super initWithFrame:frame])) {
    [self _init];
  }
  return self;
}

- (void)drawRect:(CGRect)rect {
  [[UIColor redColor] setFill];
  [_path fill];
}

- (void)dealloc {
  [_path release];

  [super dealloc];
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
  return [_path containsPoint:point];
}

#pragma mark -
#pragma mark UIResponder touches

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
  PRINT_CURRENT_SEL;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
  PRINT_CURRENT_SEL;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
  PRINT_CURRENT_SEL;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
  PRINT_CURRENT_SEL;
}
@end

这个demo中用到了3.2以后提供的UIBezierPath类,来创建一个图形的path,在pointInside:withEvent:中判断点是不是在图形的里面,如果是返回YES,反之NO。这样就用找个图形的path代表了view的bounds。那么即使你触摸在view内但是没有在找个图形上,touchesBegan就不会被调用,不会打印相应信息在console中。
所以这样这个view的边界就变成了这个不规则图形的边界了,就变相地创建出了一个不规则的view了!cheers!

点击下载 源代码

NSLog warning:”format not a string literal and no format arguments”

2 Comments

我觉得这个问题已经烦躁了我很久很久,因为自打我学习Objective c的时候,写这样一句是没有任何问题的:
----------------------
NSString *name = @”Mimi”;
NSLog(name);

而如今升级到SnowLeopard,用上Xcode 3.2之后,如果这么写总是会有一个“format not a string literal and no format arguments”另人烦躁的warning, 虽然比较恶心,但是还是看看为什么会有这个warning再说。

其实一切都是为了安全着想,加入你写了下面的一段程序:
----------------------
NSString *nameFormat = @”%@ %@”;
NSString *firstName = @”Jon”;
NSString *lastName = @”Hess %@”;
NSString *name = [NSString stringWithFormat:nameFormat, firstName, lastName];
NSLog(name);

那么就相当于
----------------------
NSLog(@”Jon Hess %@”);

这样自然程序运行是有问题的!

所以为了避免这么无辜而且隐蔽的错误,xcode添加了类型检查。

但是如果你觉得你不需要xcode为你操这些心,方便才是王道的话,你可以在xcode里面将GCC 4.2-Warnings中的Typecheck Calls to printf/scanf选项去掉就可以解决问题:

NSURLConnection timeout失效问题

2 Comments

今天在编译3.0的程序时候无意中写了这么一段代码:

?View Code OBJETIVE-C
1
2
3
4
5
6
7
8
9
10
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:@"..." cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:15];
NSString *string = @"FUCK GFW THREE DAYS!";
NSData *bodyData = [string dataUsingEncoding:NSUTF8StringEncoding];
...
[request setValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%u", [bodyData length]] forHTTPHeaderField:@"Content-Length"];
[request setHTTPBody: bodyData];
[request setHTTPMethod: @"POST"];
...
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];

結果由于本部龟速的网络导致程序迟迟没有反应,一直处于loading,但是很奇怪的是在15s之后程序仍然没有NSURLConnection fail之類的回调,我测试了一下程序最终在一分多钟以后才返回了,也就是说我们设置的timeout失效了。

*原因:这个问题只有在3.0以及之后的os中才有的,而且只有在当调用了setHTTPBody之后才会出现timeout失效。这个是苹果公司对URL Loading System的在OS3.0中的一个改动,不过在我看来其实这就是一个bug!在setHTTPBody之后,request的timeout会被改为240s(这个你可以通过NSLog [request timeoutInterval]查看),苹果开发人员的解释就是通常我们自己设置的太短的timeout其实是没什么作用的,尤其对移动设备上来讲与网络沟通需要的时间往往是比较长的,假如你的timeout是10s,在WWAN的网络环境下,可能才刚刚“bring WWAN Interface up”(不知道怎么翻译,囧)。所以自从OS 3后,如果设置了HTTP body的data,系统就会自动设置一个最低的timeout值,即240s,而且这个值都是不能被改动的,即是你自己再次设置了timeoutInterval,你通过NSLog [request timeoutInterval]得到的还是240S!!

*解决方案:这里我们就可以自己开启一个timer,然后将timer的interval设置为你想设置给connection的timeout,然后在timer的响应selector中讲connection cancel掉,这样就能够像原来一样在timeout之后cancel connection了。如果想同样像以前一样有- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error这样的回调信息,你自己在timer的selector中手动调用[delegate connection:theConnection didFailWithError: …];就可以了,有点tricky,但是很实用。

PS:自己可以看一下的帖子:

https://devforums.apple.com/thread/25282

https://devforums.apple.com/message/14845#14845

https://devforums.apple.com/message/37677

关于Beta SDK/Xcode蛋疼问题的解决之道

No Comments

最近我身边有不知道多少个兄弟都被iPhone OS 4.0的各种beta版本给搞疯掉了,所以很多人都有回退到3.0 sdk/xcode的想法。但是由于手上的设备有一些是4.0,3.0sdk/xcode不能编译上去,所以最好的办法就是既能编3.0又能编4.0的该多好啊。昨天晚上,我做到了,虽然办法很土,但是很管用。

1.首先按照下面的办法将当前的xcode和sdk删除干净:
“To uninstall Xcode developer tools on the boot volume along with the directory, from a Terminal window type:
$ sudo
/Library/uninstall-devtools –mode=all”

2.重启电脑

3.安装xcode 3.2(没有这个的可以找我要,或者找苹果俱乐部),然后安装3.0 SDK。这样就能够继续编3.0的app了,开心。

4.安装4.0 beta sdk/xcode3.2.3(这个自己上官网下,如果你没有iDP,同样可以联系我或者联系苹果俱乐部),在安装的时候注意不要和刚才安装xcode 的路径一样!!!!如果一样会导致之前的xcode/sdk覆盖,导致很多你们现在碰到的悲剧的发生!
例如我之前将xcode安装在默认路径“/Developer”,那么现在就不要选这个路径了,我选择的是”~/Developer”,这样我的beta sdk/xcode就不会干扰我之前安装的3.0的版本了!

于是的结果就是有两套 sdk和xcode,虽然占用了很多额外的硬盘空间,但是起码我现在能够分别编译3.0和4.0了!办法很土,但是很管用。

总结,劝大家不要老师尝试新鲜事物,以后beta的东西在非必要的情况下千万别装了!即是是要装,请装在别的地方,这样才不会打扰您的正常工作了!

const int和数组的关系

No Comments

作为一个从c  >>  c++  >>  objective-c 这样一个语言学习过程过来的程序员来讲.
写出这样到代码是一件很随意的事情:

1
2
const int size = 10;
int a[size];

但是请注意的是GCC中C和OC都不支持这种方式,只有C++才支持。也就是说如果你把上面一段代码写在.c/.m文件就会得到编译错误,只有将文件后缀改为.cpp/.mm才能编译通过。这个让人很生气啊!

Core Animation硬件加速切身体验

No Comments

一直以来对Core Animation的理解都只是停留在这个技术是用来做动画的。通过CA为什么会得到流畅的动画效果,那是因为Core Animation是提供硬件加速的。然后,就没有然后了。。。

因为毕设的一部分是做一个iPhone的视频播放器,当然渲染解码出来的数据就是一个问题了。最先用很粗糙的UIImageView通过setImage的方式来呈现每一帧的数据。额的个天啊,虽然视频是播放出来了,但是明显有很卡的感觉!而且如果UIImageView越大,那么越卡,也就是渲染一张图片的时间越长。于是就想到了最坏的打算:用OPENGL来渲染。但是本人一向对非navtive sdk的开发比较反感,因为和系统打交道比较麻烦,所以这只是最坏的打算。于是有投奔CALayer,通过CAlayer的contents来渲染每一帧,结果效果大让人满意,渲染每一帧所花的时间基本上是同UIImageView所需时间的60%左右!播放那是非常流畅!

总结,Core Animation有硬件加速支持,所以通过CALayer的contents来渲染帧数据就会得到硬件加速带来的好处。所以如果您以后有同样要高效播放视频或者是多张图片(往往在做动画效果的时候需要)的时候,如果UIImageView不能满足你的需求,就用CALayer吧!

Older Entries Newer Entries

  • RSS
  • Twitter
  • Buzz
  • LinkedIn
  • Flickr