Beginning iOS 5 Development

第三章 基础交互
类似于Hello world的一个程序
1. 意识到了Action中sender这个变量的作用,之前从来没用过这个变量的。。。

lblShow.text= [NSStringstringWithFormat:@"%@ button pressed.", [sendertitleForState:UIControlStateNormal]];

这里可以直接获得按下的那个按钮上面的文字
2. 在iOS 4中通常习惯用alloc分配一个新的NSString,initWithFormat后使用,然后用完再release,在上一个版本的书里都是这样写的。不过新版的书都将其改为了直接调用类方法stringWithFormat。我以为这是iOS 5的新函数,一查文档在iOS 2时就有了,不知道为啥现在才拿来用。网上的说法是,这个类方法也会申请内存,但是autorelease的,这样说来iOS 4也可以这样写嘛,可 为什么到现在才改成这样呢?难道跟iOS 5的ARC有关么。。。
3. Product菜单中的Analyze命令可以分析并发现内存泄露等缺陷,貌似很好用,之前没发现。

第四章控件大杂烩
1. 学会了一个常用的快捷键,Command + =,让控件大小适应内容。如果一个Label要显示长度会变的内容时,在设计界面时就先敲入可能的最长内容,然后按这个快捷键,控件的大小就刚好合适了。
2. 按背景隐藏键盘,这个我之前的做法一直是插入一个透明的Custom按钮,放大到全屏,再加Action,还要调整层叠次序。这章介绍的方法是直接将视图的基类从默认的UIView改成UIControl,这样视图本身就可以响应事件了~太神奇了!而且超方便~
3. 为了节省资源,控件的Clip Subviews和Clears Graphics Context选项要关掉,Opaque选项不影响视觉效果的话能开就开
4. 之前不知道拖控件时穿过Text Box的那条参考线是什么意思,现在知道那叫Baseline guide,就是输入的文本的基线。类似于初中写作业用的凯撒牌英语本上的那条红线,哈哈
5. 提示信息用UIAlertView,里面不能放太多按钮。通过 [alertshow];显示。让用户选择用UIActionSheet,可以放多个按钮。通过 [actionSheetshowInView:self.view];显示。得到用户按了哪个按钮,都需要通过delegate。这种阻断程序,让用户做选择的东西统称modal view,貌似是翻译成“模态视图”
6. 继续体会到了sender的强大,功能类似的一组控件基本上都可以将事件响应写到一个Action响应的函数里,然后根据sender判断按了哪个。还有在slider滑动时的action中直接根据sender获得其value,而不需要为其建立专门的outlet。另外判别是哪个控件触发时,可以利用控件的tag字段
7. 可伸缩图片!!超实用的技巧!!设置不可伸缩部分(Cap),然后剩余部分就被自动拉长,填满控件。书上的代码还不太完善,他将topCapHeight设为了0,导致按钮很高时,两条水平的边缘变得很丑。我将topCapHeight也设为了12,大按钮看起来就很美观了。代码比较经典,直接贴上来备忘吧!用的时候别忘了把Button的类型从Round Rect改为Custom

UIImage*imageNormal = [UIImageimageNamed:@"whiteButton.png"];

UIImage*strhNormal = [imageNormalstretchableImageWithLeftCapWidth:12topCapHeight:12];

UIImage*imageHighlighted = [UIImageimageNamed:@"blueButton.png"];

UIImage*strhHighlighted = [imageHighlightedstretchableImageWithLeftCapWidth:12topCapHeight:12];

[dosthButtonsetBackgroundImage:strhNormalforState:UIControlStateNormal];

[dosthButtonsetBackgroundImage:strhHighlightedforState:UIControlStateHighlighted];

8. 在Action响应函数中,那些以Index标志的东西要加上注释,提高代码的可读性。比如index为0的segmented control是哪一个,比如ActionSheet中的第1个按钮是什么含义等等。

第五章 自动旋转
1. 先学学英语吧,protrait指的是竖屏(又高又窄),landscape指的是横屏(又矮又宽)
2. 建立一个iPhone工程,默认的Supported Device Orientations是除了“Upside down”以外的三个,而建立一个iPad工程时,默认则是支持全部的四个方向。这是为什么呢?因为iPhone在使用过程中面临突然有电话打进来的情况,用户没法在设备倒置的情况下打电话,因为话筒和听筒安装在电话的两端,没法混用,这就造成了不方便。而iPad则不存在电话和听筒位置的问题,所以默认是支持四个方向。一个细节而已,没啥用,就是挺吃惊的。
3. 支持多个方向最简单的方法是修改控件Autosizing的方式,图形化的界面,非常方便。但在复杂情况下不能满足需求,就要写代码手动调整控件的位置和大小。如果两个方向的界面相差很大,可以在一个Nib里建立两个View,分别制作好,通过代码切换这两个View。按Alt键拖动View在侧边栏里的条目可以复制View。
4. 通过 willAnimateRotationToInterfaceOrientation响应设备旋转。这里涉及的技术稍微复杂一些,切换view只需要为View建立outlet并赋值给self.view,重要的是view的定位和方向。开始我没明白为什么要旋转,如果不明白的话,把给transform赋值的那几句话去掉,再运行一下看效果,你瞬间就会明白的~!这里利用的是图形学中的旋转矩阵,当年学OpenGL生不如死的现在貌似用上了,一看那个先搞成单位矩阵,再搞仿射变换,瞬间感到一种亲切感!这段代码也挺经典的,以后可以直接拷贝来用,这里收藏一下~

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation duration:(NSTimeInterval)duration {

if(interfaceOrientation ==UIInterfaceOrientationPortrait)

{

self.view=self.portrait;

self.view.transform=CGAffineTransformIdentity;

//self.view.transform = CGAffineTransformMakeRotation(0);

self.view.bounds=CGRectMake(0.0,0.0,320.0,460.0);

}

elseif(interfaceOrientation ==UIInterfaceOrientationLandscapeRight)

{

self.view=self.landscape;

self.view.transform=CGAffineTransformIdentity;

self.view.transform=CGAffineTransformMakeRotation(0.5*M_PI);

self.view.bounds=CGRectMake(0.0,0.0,480,300);

}

elseif(interfaceOrientation ==UIInterfaceOrientationLandscapeLeft)

{

self.view=self.landscape;

self.view.transform=CGAffineTransformIdentity;

self.view.transform=CGAffineTransformMakeRotation(-0.5*M_PI);

self.view.bounds=CGRectMake(0.0,0.0,480,300);

}

}

需要注意的有(1)仿射变换的参数是弧度制,不是角度制 (2)RectMake的时候要考虑状态栏的高度 (3)直立状态时也要为transform赋值,将界面转回正常,否则开始运行时是正确的,横过来再竖过来就错了
5. iOS的新特性outlet collection,Outlet具有一对一的特点,一个Outlet只能对应一个控件,一个控件对应多个Outlet也没有意义。为了能让一个Outlet控制多个控件,新版iOS(据说是iOS 4引入的?不过在这本新版iOS 5的书上才讲到,上一个版本的书没讲)引入了Outlet Collection这个概念,想建立控件-Outlet多对一的关系时,可以创建Outlet Collection,它实际上就是一个Outlet的数组,类型是NSArray。可以用for...in循环来遍历,用containsObject实例方法来查找。
下面代码展示了将一个Outlet Collection中所有的控件的属性全部改变的循环

if([topscontainsObject:sender]){

msg =@"Top button pressed.";

for(UIButton*btnintops){

btn.hidden=YES;

}

}

6. 一个新的关键词,strong,没懂啥意思,后来在StackOverflow上搜到了一个很好的解释
It's a replacement for theretainattribute, as part ofObjective-C Automated Reference Counting (ARC). In non-ARC code it's just a synonym forretain.
我就来翻译一下吧:它作为Objective-C自动引用计数(ARC)的一部分,用来替换retain修饰符。在非ARC代码中,它就是retain的同义词。

第六章 多视图应用
很长的一章,代码挺多的,第一次从空工程构建出一个App。。。
1. 在AppDelegate里加载第一个ViewController的时候要计算status bar的高度。但是直接建出的工程就没有这个步骤,不知道为啥。。。计算的代码是

self.window= [[[UIWindowalloc]initWithFrame:[[UIScreenmainScreen]bounds]]autorelease];

SwitchViewController*switchController = [[SwitchViewControlleralloc]initWithNibName:@"SwitchView"bundle:nil];

self.switchViewController= switchController;

UIView*switchView =self.switchViewController.view;

CGRectframe = switchView.frame;

frame.origin.y+= [UIApplicationsharedApplication].statusBarFrame.size.height;

switchView.frame= frame;

[self.windowaddSubview:switchView];

self.window.backgroundColor= [UIColorwhiteColor];

[self.windowmakeKeyAndVisible];

[switchControllerrelease];

returnYES;

2. 这里再说一下内存管理吧。。。到第六章才意识到这个问题显然有点晚。通过这本书新旧两个版本的比较阅读,我发现最大的区别就是内存管理。由于ARC的引入,前面版本里跟内存管理有关的讲解和代码在新版里消失的无影无踪,之前各种经典的内存泄露在新版里则成为了范例代码。这些代码搬到iOS 4可以用肆无忌惮来形容,哈哈!不过iOS 5不会那么快的普及,因此我们还是应该了解iOS在内存管理方面的特点,避免内存泄露。
就拿上面的那段代码来说,要注意的是
SwitchViewController*switchController = [[SwitchViewControlleralloc]initWithNibName:@"SwitchView"bundle:nil];

self.switchViewController= switchController;

C++用户可能会疑惑,为什么要switchController这个指针多此一举呢?下面这样一句话解决不行吗?

self.switchViewController= [[SwitchViewControlleralloc]initWithNibName:@"SwitchView"bundle:nil];

这是Objective-C的特性决定的,属性switchViewController是由retain修饰的,即对赋值操作自己持有一个副本。这样alloc出来的对象占用一块内存,属性switchViewController由于持有该对象就再占用一块内存。类析构时只能释放后者,而前者则泄露。
因此在OC里,如果不开ARC,那么那个临时的指针变量以及随后的release语句就是必须的了。
3. 分别建立nib和代码文件时,需要建立两者的联系。需要做的有(1)点nib里的File‘s owner,设置其Custom Class为其Controller类的名字 (2)将View与File's owner里的view建立outlet。这两项做完就可以在Assistant界面上拖Outlet和Action了
4. 一点 疑问,书上说superview有status bar了,那么subview就不应该设置status bar,这样系统才能正确计算出尺寸。我实验中恰好相反,在subview里加上了status bar,显示出的控件位置才跟Interface Builder里的一样。
5. 视图切换的动画。AnimationCurve的概念,Animation block的概念。

第七章 TabBar和DataPicker
1. 学会了如何手动从空工程建立Tabbed Application。可以直接在AppDelegate里载入TabBar View Controller的xib,代码只要在 didFinishLaunchingWithOptions方法中加两行

[[NSBundlemainBundle]loadNibNamed:@"TabBarController"owner:selfoptions:nil];

[self.windowaddSubview:rootController.view];

当然别忘了把xib中File's owner的Class改成AppDelegate,还有将TabBar Controller与手动敲的

@property(strong,nonatomic)IBOutletUITabBarController*rootController;

建立outlet连接
2. Tab中每个Item除了图标和Title之外还要改两处(1)Custom Class (2)Nib Name
都改成要载入的那个View的类名和xib名
3. Picker里最常用的一个方法是 selectedRowInComponent
4. Picker的两个协议(代理和数据源)中,必须要实现的方法有3个
(1) - (NSInteger) numberOfComponentsInPickerView:(UIPickerView*)pickerView
(2) - (NSInteger)pickerView:(UIPickerView*)pickerView numberOfRowsInComponent:(NSInteger)component
(3.1) - (NSString*)pickerView:(UIPickerView*)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
(3.2) - (UIView*)pickerView:(UIPickerView*)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView*)view
其中(3.1)和(3.2)可以选一个实现。可选的方法是:

- (void)pickerView:(UIPickerView*)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

必选的三个方法记忆方法就是2个NSInteger和一个NSString*,只有记住第一个括号,然后敲Esc补全就可以了。还跟新浪网院的老师学了个歪招,就是按住Command键,点头文件里的协议名,看协议的声明,然后复制协议方法的原型到自己的代码中。
5. 用 NSDate*selected = [datePickerdate];获取Date Picker的日期数据,但返回的是中央时区的时间,貌似还需要Format。这里犯过一个低级错误,就是把NSDate写成了NSData,不仅能过编译,而且还能正常运行!只是这句话上一直有警告。端详了老半天才发现一个字母的差异。。。
6. Double Picker那个View写起来明显熟练了很多,基本上没出啥问题,书瞄上一眼就能自己写出来。
7. plist读进来是一个Dictionary,用valueForKey方法查找
8. 如果picker的component之间有依赖关系,需要在didSelectRow协议方法中调用picker的reloadComponent方法来重新载入需要改变的component,或者直接reloadAllComponent。。。
9. picker有多个component时,可以把每一列的编号define成一个宏常量,这样判断时就可以用有意义的字符串代替编号,提高代码的可读性
10. 通过widthForComponent方法可以指定列宽,从而形成各个component不等宽的效果,如果几个Component数据长度差异较大,可以考虑实现这个方法。看书上的代码,各个component的宽度之和为 290个像素。另外还有一个指定每行高度的方法叫rowHeightForComponent,还没用过。
11. 一些需要收藏备用的代码
(1) 用代码选定picker中的某一行 [pickerselectRow:0inComponent:kZipComponentanimated:YES];
(2) 上一条的逆操作,即得到现在选中了哪一行 [pickerselectedRowInComponent:kStateComponent]
(3) 对NSArray排序(实例名为sorted) sorted = [sortedsortedArrayUsingSelector:@selector(compare:)];
(4) 获取资源的NSURL: NSURL*resURL = [[NSBundlemainBundle]URLForResource:@"文件名"withExtension:@"扩展名"];
(5) 通过NSURL将PList读入到NSDictionary: NSDictionary*dict = [[NSDictionaryalloc]initWithContentsOfURL:resURL];
(6) 从资源中载入图片 UIImage*seven = [UIImageimageNamed:@"seven.png"];
(7) 动态的通过属性的名称为其赋值 [selfsetValue:新的值forKey:本类中的属性名];
(8) 延迟一会儿再调用某个方法: [selfperformSelector:@selector(要调用的方法名)withObject:nilafterDelay:0.5];
想不到SDK里居然有这种奇葩的函数。延迟的单位是秒。对另一个函数的调用是同步的,也就是说如果被调的函数执行时间很长,那这样调用仍然会卡住UI线程
(9) 播放声音

NSURL*soundURL = [[NSBundlemainBundle]URLForResource:@"声音文件名"withExtension:@"wav"];

SystemSoundIDsoundID;

AudioServicesCreateSystemSoundID((__bridgeCFURLRef)soundURL, &soundID);

AudioServicesPlaySystemSound(soundID);

那个斜体的__bridge是给ARC看的,iOS4中不用加。使用这些代码需要加头文件 #import<AudioToolbox/AudioToolbox.h>,当然还需加框架AudioToolbox~,还记得作为一名文艺青年应该怎么加框架吗?
重要提示:通过与iOS4旧版图书的比较阅读,发现以上播放声音的代码在没有ARC的时候会导致内存泄露!在没有ARC的情况下,正确的做法是将SystemSoundID作为类的一个属性,在视图载入时就调用 AudioServicesCreateSystemSoundID方法创建SoundID,在视图退出时调用 AudioServicesDisposeSystemSoundID(soundID);方法释放。奇葩的是,SoundID不能在播放的函数里释放,SoundID必须在播放期间都有效。否则会放不出声音。不这样释放的话,在声音被反复打开时就会出现内存泄露。还有,SoundID貌似一个类,其实就是一个无符号长整型数。

终于读完了这一章!!!好长

第八章 TableView初步
1. 还是说说内存管理吧。self.XXX = YYY; 跟 XXX = YYY; 其中XXX是声明为retain的属性。这两种写法是不一样的。前者会发挥retain的作用,而后者直接给指针赋值。如果你随后又将YYY给release了的话,那第二种写法就会出错,必须使用第一种。还有,在读取一个属性的时候,加不加self是没区别的~
2. 这一章后续的内容是(1)右侧的索引条 (2)基于Category(分类)的深拷贝 (3)搜索栏. 暂时还用不到,等回头再看吧

第九章 Navigation Controller和TableView


未完待续。。。

第十一章 定位服务(LBS)
1. Core Location框架使用很方便,经纬度、海拔、精度、旅行距离都能直接测出来
2. 编译的时候要加上CoreLocation.framework,加这个框架有3三种方法
普通青年:在原有的框架比如UIKit.framework点右键,在打开的文件夹里找到CoreLocation.framework,然后拖进Xcode里,在向导里点确定
文艺青年:在项目设置的页面点Build Phase,在Link Binary with Libraries下面点加号,选CoreLocation.framework加进来
2B青年:在项目上右键,Add files to “XXX”,然后依次展开/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/找到CoreLocation.framework加进来
话说我就是差点成了2B青年,最后用普通青年的方法加进来的,书上介绍的方法属于文艺青年~~哈哈哈
3. 程序的代码也是比较固定,可以直接粘贴到需要定位的项目里用,这里也贴上来收藏
头文件如下,需要加的有1头文件,2协议,3需要的Outlet

#import<UIKit/UIKit.h>

#import<CoreLocation/CoreLocation.h>

@interfaceFirstViewController :UIViewController<CLLocationManagerDelegate>

@property(retain,nonatomic)CLLocationManager*locManager;

@property(retain,nonatomic)CLLocation*startPoint;

@property(retain,nonatomic)IBOutletUILabel*lblLatitude;

@property(retain,nonatomic)IBOutletUILabel*lblLongitude;

@property(retain,nonatomic)IBOutletUILabel*lblHorAccu;

@property(retain,nonatomic)IBOutletUILabel*lblAltitude;

@property(retain,nonatomic)IBOutletUILabel*lblVerAccu;

@property(retain,nonatomic)IBOutletUILabel*lblDistance;

@end


.m实现文件
在视图载入后初始化 CLLocationManager、设定精度、更新位置信息,这是定位最核心的代码,却非常简洁

- (void)viewDidLoad

{

[superviewDidLoad];

locManager= [[CLLocationManageralloc]init];

locManager.delegate=self;

locManager.desiredAccuracy=kCLLocationAccuracyBest;

[locManagerstartUpdatingLocation];

}

每次更新得到新的坐标后,就会调用这个方法。坐标啥的都在newLocation里了,直接拿来用就行。还有别忘了处理一下错误,如果用户没有授权获取位置信息,那就要做响应的处理。

#pragma mark -CLLocationManagerDelegate Methods

- (void)locationManager:(CLLocationManager*)manager

didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation {

NSString*latitudeString = [NSStringstringWithFormat:@"%g\°", newLocation.coordinate.latitude];

lblLatitude.text= latitudeString;

NSString*longitudeString = [NSStringstringWithFormat:@"%g\°", newLocation.coordinate.longitude];

lblLongitude.text= longitudeString;

NSString*horizontalAccuracyString = [NSStringstringWithFormat:@"%gm", newLocation.horizontalAccuracy];

lblHorAccu.text= horizontalAccuracyString;

NSString*altitudeString = [NSStringstringWithFormat:@"%gm", newLocation.altitude];

lblAltitude.text= altitudeString;

NSString*verticalAccuracyString = [NSStringstringWithFormat:@"%gm", newLocation.verticalAccuracy];

lblVerAccu.text= verticalAccuracyString;

CLLocationDistancedistance = [newLocationdistanceFromLocation:startPoint];

NSString*distanceString = [NSStringstringWithFormat:@"%gm", distance];

lblDistance.text = distanceString;

}


- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error {

NSString*errorType = (error.code==kCLErrorDenied) ?@"Access Denied":@"Unknown Error";

UIAlertView*alert = [[UIAlertViewalloc]initWithTitle:@"Error getting Location"message:errorTypedelegate:selfcancelButtonTitle:@"Okay"otherButtonTitles:nil];

[alertshow];

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值