第三,四天,主要是针对几个典型的应用编写相关代码。虽然说前面看过sample code,研究过一些语法,但是真正上手写时仍然是举步维艰,可能一句话就能解决的问题往往要查上很久,但是个人认为这种方式也比较快,往往针对问题。至于研究得深入不深入,就要看时间够不够了,我觉得要在初学时候所有东西都深入研究还是有一定难度的,但是一些常见的应该要深入进去。
1. HTTP访问web,目标是封装成为一个今后也可以使用的通用类库。下面就是一个asihttprequest的restful的例子,由于未完全理解objective-c的东西,估计还有更好的方式。需要明白,安装一个类库一般需要拷贝头文件,添加链接类库几个步骤,如果是在ARC的环境中,还要到build phases中手动将手动引用计数的文件的编译标志中加上一个-fno-objc-arc
#import "HttpClient.h"
#import "ASIHTTPRequest.h"
#import "ResponseProcessDelegate.h"
@implementation HttpClient
NSString * configParam(NSString * template, NSDictionary * param){
NSString *result;
for(NSString * key in [param allKeys]){
result = [template stringByReplacingOccurrencesOfString:key withString:[param objectForKey:key]];
}
return result;
}
- (void) get:(NSString *)url with:(HttpRequestParam *) param responseDelegate:(id<ResponseProcessDelegate>) delegate{
sendRequest(url,nil,param,@"GET",delegate);
}
- (void) post:(NSString *)url and:(NSMutableString *)body with:(HttpRequestParam *) param responseDelegate:(id<ResponseProcessDelegate>) delegate{
sendRequest(url,body,param,@"POST", delegate);
}
void sendRequest(NSString * url, NSString * body, HttpRequestParam * param, NSString * method, id<ResponseProcessDelegate> delegate){
if(param != nil && param.requestParam){
url = configParam(url,param.requestParam);
}
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSString *requestUrl = [NSString stringWithFormat:@"%@://%@:%@/%@",[defaults objectForKey:@"protocol"], [defaults objectForKey:@"server"], [defaults objectForKey:@"port"], url];
NSURL *actualUrl = [NSURL URLWithString:requestUrl];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:actualUrl];
NSLog(@"send request:%@:", requestUrl);
[request setTimeOutSeconds:1000];
if([method isEqualToString:@"POST"]){
[request setRequestMethod:@"POST"];
if(param != nil){
body = configParam(body,param.bodyParam);
}
if(body != nil){
[request appendPostData: [body dataUsingEncoding:NSUTF8StringEncoding]];
}
}else{
[request setRequestMethod:@"GET"];
}
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
[delegate success:responseString withParam:param];
NSLog(@"%@%@", @"request success",responseString);
}];
[request setFailedBlock:^{
NSLog(@"%@%d%@", @"request failed",[request responseStatusCode], [request responseHeaders]);
[delegate fail];
}];
[request startAsynchronous];
}
@end
随着研究的深入,发现这种写法并不是一种比较好的调用方式,因为需要在每个controller的实现中添加success和fail这两个delegate,比较好的做法是用block,于是有了下面的代码。
- (void) get:(NSString *)url
onSuccess:(VoidBlock) successBlock
onError:(ErrorBlock) errorBlock{
sendRequest(url,nil,successBlock, errorBlock);
}
- (void) post:(NSString *)url
body:(NSMutableString *)body
onSuccess:(VoidBlock) successBlock
onError:(ErrorBlock) errorBlock{
sendRequest(url,body,successBlock, errorBlock);
}
void sendRequest(NSString * url,
NSString * body,
VoidBlock successedBlock,
ErrorBlock errorBlock){
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSString *requestUrl = [NSString stringWithFormat:@"%@://%@:%@/%@",[defaults objectForKey:@"protocol"], [defaults objectForKey:@"server"], [defaults objectForKey:@"port"], url];
NSURL *actualUrl = [NSURL URLWithString:requestUrl];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:actualUrl];
NSLog(@"send request:%@:", requestUrl);
[request setTimeOutSeconds:10];
if(body != nil){
[request appendPostData: [body dataUsingEncoding:NSUTF8StringEncoding]];
[request setRequestMethod:@"POST"];
}else{
[request setRequestMethod:@"GET"];
}
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
successedBlock(responseString);
// NSLog(@"%@%@", @"request success",responseString);
}];
[request setFailedBlock:^{
NSLog(@"%@%d%@", @"request failed",[request responseStatusCode], [request responseHeaders]);
errorBlock([request error]);
}];
[request startAsynchronous];
}
其中,block的定义如下
typedef void (^VoidBlock)(NSString *response);
typedef void (^ErrorBlock)(NSError *error);
2. 应用设置,应当知道如何在应用程序里做设置以及读取设置。
ios中设置非常简单。只需要在根目录new File->Resource->Settings Bundle,即可生成一个Settings.bundle,这个文件可以表示setting中的各种控件,比如Test field,Switch等等。简而言之,你只需要配置这个bundle,其他的显示通通由ios自己搞定。真是简单啊。
而读取只需要操作单例类NSUserDefaults,初始如下调用,接下来就可以像操作NSDictionary一样使用它。
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults objectForKey:@"port"];
但是有个坑,读取默认配置,可能你会认为设置了Default value,它随后就会读出来,实际情况是默认配置跟真正的值没有关系。也就是说,你第一次读取出来的值是null。
查了一下,最佳实践应该是这样,在appDelegate的didFinishLauchingWithOptions中(还记得生命周期图吗)添加如下方法,查了一下AppPrefs的sample code,原来人家也是类似的做法:
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]
NSString * port = [defaults objectForKey:@"port"];
if(!port){
port = @"8888";
}
NSDictionary *defaultValues = [NSDictionary dictionaryWithObjectsAndKeys:port, @"port",nil];
[[NSUsertDefaults standardUserDefaults] registerDefaults:defaultValues];
3. 简单控件的使用,比如TextField, Button,以及控件自定义,比如自定义开关。
关于简单控件的使用,参考第一天的storyboard入门一节的文章,照着那个做,基本上简单控件和流程就有个数了。
一个比较牛的自定义开关,可以自定义文字,大小等等
http://code4app.com/ios/Customized-Round-Switch/4f6d915b6803faef27000000
4. 2维图形和动画的使用。
http://blog.163.com/wkyuyang_001/blog/static/10802122820133190545227/
http://www.189works.com/article-86565-1.html
画圆带边的两种方式:
4.1 贝塞尔曲线
UIBezierPath * bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(x, y) radius:REDIUS startAngle:0 endAngle:2 * M_PI clockwise:1];
[[UIColor whiteColor] setFill];
[bezierPath closePath];
[bezierPath fill];
4.2 arc
CGContextMoveToPoint(context, x, y);
CGContextSetRGBFillColor(context, 1, 1, 1, 1.0);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1.0);
CGContextSetLineWidth(context, 0.6);
CGContextAddArc(context, x, y, REDIUS, 0, 2 * M_PI, 1);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathEOFillStroke);
5. 手势和事件的使用。
ios手势在模拟器中的快捷键
tap 触屏点击
swipe 触屏按下后扫过并松开
pinch option按下后出现两个圆球,拉远他们就是放大,反之缩小
rotation option按下后出现两个圆球,触屏按下并旋转
6. 在例子编写中犯的一些问题。
通常这类问题很傻很天真,但是我相信背后能揭示一定的东西
1) 我采用storyboard开发,最初添加了一个loadView方法,里面控制画了地图,显示得很好。接着我又在storyboard拖拽了几个view和其他控件上去,把地图显示的代码注释掉。再运行就黑屏,打印出来一句Application windows are expected to have a root view controller at the end of application launch。于是我就漫无目的的找啊找啊,先是找到要添加rootViewController的方法http://www.jnhctt.com/kf/201202/120864.html,加了半天发现不对,于是又找,才知道我用storyboard实际上是不需要加这些的,storyboard会帮你完成,可能是plist中Main storyboard file base name这条不见了,一看也在,于是又找到说是summary中main storyboard这里没填,一看填了。这时候有点小崩溃了。花了近半天时间啊。最后终于在某个问题的边角回复中发现原来loadView方法也会导致这个问题,崩溃。最开始收集的view的生命周期也没讲到这个,看起来还要不断的试错才行啊。
2) 第二个问题是直接崩溃的,报错this class is not key value coding-compliant for the key tableView。发现主要原因是之前注释了IBOutlet的属性代码,但是界面上还保留着这些已经连线的IBOutlet,界面找不到配对的属性于是报错,修改办法就是恢复注释掉的IBOutlet属性。
3) 布局问题,出现过好几个布局问题,其中一个是UIView中的子view很大,通过constraint限制四边的边距貌似不起作用,找了半天,发现菜单栏的Editor中的pin可以constraint指定宽和高,于是定下了宽高,解决问题。另一个是用程序将一个view加入container view中,我希望他们两size一样,但是无论如何都要窄一些,又捣腾了半天,发现其他方法貌似都不管用,通过设置子view的bounds=父view的bounds就轻松解决,真是坑爹啊。
4) 警告,format string is not a string literal (potentially insecure) ,新版的NSLog改成了只接受格式化的字符串,于是,格式就要类似这样
NSLog(@"%@", (NSString *)str);
7. 关于地图。
7.1 google map。google map的api是我用过的比较好用的api了,简洁快速,基本上照着官方一个简单的流程走下来就ok了。https://developers.google.com/maps/documentation/ios/start
但是我还是犯了两个菜鸟级错误:一是Other Linker Flags没有设置,报错为unrecognized selector sent to instance;二是Api_key没有生效(注意修改后bundle id后要过一段时间才生效,这时不如直接删掉重新添加),报错为Error Domain=com.google.HTTPStatus Code=400。
另外,google map的api比较简单,一个是设置cam的经纬度(即中心位置,参考中国各个城市经纬度http://hi.baidu.com/sunbolg/item/63f77439f6e5a580b611db5c),然后设定缩放级别,从小到大依次放大
7.2 Mapkit这个地图也不错。这里推荐一个sample,将mapkit和动画等等一网打尽。http://www.oschina.net/p/Thumbnail-Annotation,这里有篇关于overlay的文章http://www.raywenderlich.com/30001/overlay-images-and-overlay-views-with-mapkit-tutorial
swift书
http://download.csdn.net/album/detail/77
Cocos2dx书
http://download.csdn.net/album/detail/81