iPhone开发笔记
IOS之helloworld
写在前面
第一个iphone程序helloworld
ios sdk介绍
修改ios工程属性
写在前面
一直对移动平台比较感兴趣,并且公司有兴趣小组,所以就报了IOS,本来就是搞java的所以android的新鲜感没有IOS强,事实就是这样的,公司没有一个报android兴趣小组的。。。学习IOS一段时间了,应该把学习中的点点滴滴给记录下来,方便自己方便后来人。渐渐的发现把object c 当作java来理解很多地方都是可以想通的。。。公司里的ioser也说oc更像java而不是c。
本教程是基于关东升老师的iphone和ipad实战的,本人这个学完在把斯坦福公开课看下,我相信技术上问题应该不大了。
有mac真机那是最好了,如果没有安装个黑苹果,也是比较方便的,本人就是amd的黑苹果10.6.8,xcode4.2,用着蛮好的。
1.1 第一iPhone程序-Hello World
选择New Project->Application->View-based Application 工程类型。
屏幕设计:
双击打开Hello_WorldViewController文件,打开在Interface Builder中打开NIB文件,进入屏幕设计窗口。
1.2 IOS SDK介绍
iOS SDK是开发iPhone应用程序的工具。iOS SDK必须安装在Mac OS X操作系统之上。可以使用Xcode开发iOS,不同的Xcode版本对应不同的iOS SDK版本,不同的Xcode版本要求Mac OS X版本也不同。
" iOS SDK 2.x
" iOS SDK 3.x Xcode3.2.3
" iOS SDK 4.x,Xcode 3.2.5或Xcode4.2
" iOS SDK 5,Xcode4.3
1.3 修改iOS工程属性
在工程目录下面有一个工程属性文件:HelloiPhone-Info.plist
在这个文件中我们可以设置工程图标、工程国际化等信息设定
另一种修改iOS工程属性方法:
通过为工程添加图标属性修改工程属性步骤。
添加图标必须是:
普通显示屏幕:57x57 默认命名:Icon.png,Retina显
示屏幕:114x114,默认命名:[email protected]。
修改工程编译属性:
由于iOS版本变化很快,我们需要修改工程的编译属性。可以修改编译目标,可以修改iOS基本版本。
Posts - 262 Articles - 0 Comments - 87
IOS之UI基础
2.1 增强版Hello World
2.2 MVC设计模式
2.3 Cocoa MVC
2.4 视图控制器的方法
2.5 输出口和动作
2.6 键盘输入
2.7 使用AlertView
2.8 使用ActionSheet
2.8 等待有关控件
2.9 屏幕旋转
2.1 增强版Hello World
实现步骤
1.创建Hello World工程
在Xcode中创建Hello World工程,基于iPhone试图基础应用程序。
iPhone OS ->Application ->View-based Application
2.修改Hello_WorldViewController.h
需要UITextField控件接受文字和响应一个按钮点击事件,所以在h文件中我们要定义一个UITextField属性和一个响应事件方法
#import <UIKit/UIKit.h>
@interface Hello_WorldViewController : UIViewController {
UITextField *txtField;
}
@property (nonatomic, retain) IBOutlet UITextField *txtField;
-(IBAction)onClickButton:(id)sender;
@end
3.修改Hello_WorldViewController.m
实现txtField属性
实现-(IBAction)onClickButton:(id)sender 方法
#import "Hello_WorldViewController.h"
@implementation Hello_WorldViewController
@synthesize txtField;
-(IBAction)onClickButton:(id)sender {
txtField.text = @"Hello World.";
}
- (void)viewDidUnload {
self.txtField = nil;
}
- (void)dealloc {
[txtField dealloc];
[super dealloc];
}
@end
4.使用IB设计页面,摆放控件
Resources/Hello_WorldViewController.xib文件,打开Library将控件拖入设计窗口
5.连接输出口
为了将控件属性通过屏幕“输入”或“输出”,我们需要定义“输出口”,在控制器中我们已经定义了与这个数据对 应的属性:
@property (nonatomic, retain) IBOutlet
UITextField *txtField;
UITextField用于展示输出和输入数据。在iPhone(包括Mac)开发时候控件属性要通过定义输出口才能在屏幕中使用 的。
6.处理事件
为了响应控件的事件,我们需要在控制器中定义一个事件处理方法:
-(IBAction)onClickButton:(id)sender;
在iPhone(包括Mac)开发控件事件处理要自己编写对应方法,并在IB中将控件的事件与该方法连接起来。
2.2 MVC设计模式
MVC是一种设计模式,所谓设计模式就是解决某一特定问题的方案,MVC是解决具有UI的应用系统的成熟解决方案,
在Cocoa应用系统中严格按照该模式实现。M-Model(模型)是应用系统中与视图对于部分的数据。V -View(视图)是应用系统中用户看到并与之交互的界面。C-Controller(控制器)是应用系统中起到控制器作用,接受用户事件,显示数据等等,与视图进行交互等。
2.3 Cocoa MVC
采用MVC 设计模式意味着,Interface Builder 不需要编写或生成任何代码,您只需专注于应用程序的视图。Mac 的Cocoa 绑定消除了大部分的黏合代码,它就像连接在Xcode 编写的控制器和Interface Builder 设计的视图之间的一条线,用图形化的表示方法简化了二者之间的关系。Interface Builder 和Cocoa 可以快速开发地您的应用程序。
File's owner:
2.4 视图控制器的方法
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
如果视图使用NIB文件创建,在加载视图之前调用这个方法,做一些初始化工作。
- (void)loadView
视图加载的时候调用的方法一般不使用NIB文件创建视图的时候使用,而是使用代码创建视图对象。
- (void)viewDidLoad
视图加载之后调用的方法,我们常常在这个方法中做视图初始化出来。
- (void)didReceiveMemoryWarning
当系统内存警告的时候调用的方法,我们一般在该方法释放消耗资源的对象。
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
用于处理屏幕旋转的方法。
- (void)viewDidUnload
视图对象卸载的使用调用的方法,一般在把内存视图中的属性设置为nil值。
- (void)viewDidUnload {
self.txtField = nil;
}
- (void)dealloc
视图对象内存释放的时候调用的方法,在该方法中先要释放掉成员变量。
- (void)dealloc {
[txtField dealloc];
[super dealloc];
}
2.5 输出口和动作
输出口(Outlet),为了实现控制器在视图上输入输出结果,需要定义输出口。
定义输出口,是在ViewController定义一个控件属性,如下:
//h文件
@interface Hello_WorldViewController : UIViewController {
UITextField *txtField;
}
@property (nonatomic, retain) IBOutlet UITextField *txtField;
//m文件
@synthesize txtField;
动作(Action),为了实现视图控制器响应视图事件,需要定义动作。
定义动作,是在ViewController定义一个方法,如下:
//h文件
-(IBAction)onClickButton:(id)sender;
//m文件
-(IBAction)onClickButton:(id)sender {
txtField.text = @"Hello World.";
}
动作(Action)是在控件器中的方法,但它的返回类型必须是IBAction声明的,该关键字告诉IB,此方法是个Action,可以被某个事件触发。
2.6 键盘输入
在iPhone应用程序中,键盘输入处理比较麻烦。在输入完成后我们需要自己关闭键盘。
在iPhone中我们还可以指定键盘输入类型,可以是Email、电话和数字等类型。
输入完成关闭键盘:
Phone中文本框输入后,键盘是不会关闭的,必须编写代码。
1 为关闭键盘添加事件处理方法:
//h file
-(IBAction)textFieldDoneEditing:(id)sender;
//m file
-(IBAction)textFieldDoneEditing:(id)sender {
[sender resignFirstResponder];
}
2 链接事件
文本框对象的Did End On Exit事件链接到File’s Owner。
3 小结
点击键盘中的“换行”或“return”键关闭键盘。 第一响应者是当前与用户交互的控件,在这个例子中,点击TextField控件,它就变成第一响应者键盘就会自动出现。
[sender resignFirstResponder];
是使TextField控件放弃第一响应者状态。
4 通过触摸背景关闭键盘
点击关闭键盘中的“return”关闭键盘比较麻烦,我们可以通过触摸背景关闭键盘。
//h
-(IBAction)backgroundTap:(id)sender;
//m
-(IBAction)backgroundTap:(id)sender {
[txtField resignFirstResponder];
}
5 连接动作和事件
为了使背景控件能够响应事件,我们需要背景View的父类(UIView)修改成为UIControl,UIControl是能够触发action所有控件都是UIControl的子类。而UIControl是UIView子类,也具有View基本特征。
从Touch Down事件拖到File’s Owner图标,然后选择backgroundTap:动作。这样触摸视图中没有活动的控件的任何位置就可以触发backgroundTap:动作,关闭键盘。
6 键盘输入类型
在iPhone中我们还可以指定键盘输入类型,可以是Email、电话和数字等类型。
Email键盘 数字键盘 电话键盘
7 Return Key设定
在键盘输入属性框中我们可以定义Return Key,可以有Google等等。但是这些并没有实际的含义,只是代表它可以触发输入完成事件(Did End On Exit)。
2.7 使用AlertView
修改Hello World项目代码,添加AlertView:
修改Hello-.m代码
-(IBAction)onClickButton:(id)sender {
//txtField.text = @"Hello World.";
NSString *str = [[NSString alloc] initWithFormat:@"Hello. %@", txtField.text];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hello!"
message:str delegate:self
cancelButtonTitle:@"Done"
otherButtonTitles:nil];
[alert show];
[alert release];
[str release];
}
2.8 使用ActionSheet
ActionSheet和AlertView比较相似都是给用户一个提示信息。它是从底部弹出。它通常用于确认潜在的危险或不能撤消的操作,如删除一个数据。 为了使用ActionSheet我们需要在h文件中实现UIActionSheetDelegate协议。其中,我们常常需
要实现: actionSheet:didDismissWithButtonIndex:
该方法是ActionSheet消失的时候调用。
修改Hello-.h文件
在Hello_WorldViewController.h文件中添加协议UIActionSheetDelegate:
//h
#import <UIKit/UIKit.h>
@interface Hello_WorldViewController : UIViewController
<UIActionSheetDelegate> {
UITextField *txtField;
}
@property (nonatomic, retain) IBOutlet UITextField *txtField;
-(IBAction)onClickButton:(id)sender;
@end
//m
#import "Hello_WorldViewController.h"
@implementation Hello_WorldViewController
@synthesize txtField;
-(IBAction)onClickButton:(id)sender {
//txtField.text = @"Hello World.";
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"您确认清除文本框中的数据吗?"
delegate:self
cancelButtonTitle:@"取消"
destructiveButtonTitle:@"确定"
otherButtonTitles:nil];
// NSArray *array = [[NSArray alloc] initWithObjects:
// [NSString stringWithString:@"1st Button"],
// [NSString stringWithString:@"2nd Button"],
// [NSString stringWithString:@"3rd Button"],
// [NSString stringWithString:@"4th Button"],
// nil];
//
// UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Title Here"
// delegate:self
// cancelButtonTitle:nil
// destructiveButtonTitle:nil
// otherButtonTitles:nil];
//
// for (int i = 0; i < 3; i++) {
//
// [actionSheet addButtonWithTitle:[array objectAtIndex:i]];
//
// }
//
// [actionSheet addButtonWithTitle:@"Cancel"];
// actionSheet.cancelButtonIndex = 4;
[actionSheet showInView:self.view];
[actionSheet release];
}
-(void)actionSheet:(UIActionSheet *)actionSheet
didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == [actionSheet destructiveButtonIndex]) {
txtField.text = @"";
}
}
- (void)viewDidUnload {
self.txtField = nil;
}
- (void)dealloc {
[txtField dealloc];
[super dealloc];
}
@end
2.8 等待有关控件
对于一些费时的处理,需要使用一些等待控件消除用户心里等待的时间。
等待有关的控件有:
" UIActivityIndicatorView
" UIProgressView
UIActivityIndicatorView:
UIActivity-.h
@interface UIActivityIndicatorViewController : UIViewController {
UIActivityIndicatorView * myActivityView;
}
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView * myActivityView;
-(IBAction)onClickButton: (id)sender;
@end
UIActivity-.m
#import "UIActivityIndicatorViewController.h"
@implementation UIActivityIndicatorViewController
@synthesize myActivityView;
-(IBAction)onClickButton: (id)sender {
if ([myActivityView isAnimating]) {
[myActivityView stopAnimating];
} else {
[myActivityView startAnimating];
}
}
- (void)dealloc {
[myActivityView release];
[super dealloc];
}
@end
UIProgressView
Progress-.h
@interface ProgressViewViewController : UIViewController {
IBOutlet UIProgressView *Progress;
NSTimer *timer;
}
@property (nonatomic, retain) IBOutlet UIProgressView *Progress;
@property (nonatomic, assign) NSTimer *timer;
-(IBAction)start;
@end
Progress-.m
@synthesize Progress;
@synthesize timer;
- (void)viewDidLoad {
[super viewDidLoad];
}
-(IBAction)start{
Progress.progress = 0.0;
timer = [NSTimer
scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(update)
userInfo:nil repeats:YES];
}
NSTimer是可以隐式地启动一个线程,scheduledTimerWithTimeInterval指定线程要休眠多少时间调用一次,selector所指定的方法update
Progress-.m
-(void)update{
Progress.progress = Progress.progress + 0.1;
if (Progress.progress == 1.0) {
[timer invalidate];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"任务通知"
message:@"硬盘格式化完成!"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
- (void)viewDidUnload {
self.Progress = nil;
}
- (void)dealloc {
[Progress release];
[super dealloc];
}
UIProgressView控件的progress属性是0.0~1.0烦范围。0.0时候在开始的位置,1.0时候是进度到了100%。
2.9 屏幕旋转
iPhone中有重力感应我们可以通过旋转手机使屏幕旋转。但是屏幕旋转后页面的布局需要注意。
屏幕旋转的类型
UIInterfaceOrientationPortrait,垂直向上
UIInterfaceOrientationPortraitUpsideDown,垂直倒放。
UIInterfaceOrientationLandscapeLeft,水平向左。
UIInterfaceOrientationLandscapeRight,水平向右。
注意:以手机屏幕为参照物的向左、向右。
开启旋转 :可以通过shouldAutorotateToInterfaceOrientation:方法开启或禁止旋转。
允许任何方向的旋转:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
垂直向上和水平向右:
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation duration: (NSTimeInterval) duration {
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
} else {
}
}
触发旋转事件 :
我们可以在屏幕旋转的时候触发很多事件,其中willAnimateRotationToInterfaceOrientation是我们常用的事件,这个事件是在即将开始屏幕旋转动画的时候触发。
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation duration: (NSTimeInterval) duration {
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
txtField.text = @"垂直显示。";
} else {
txtField.text = @"水平显示。";
}
}
自动调整屏幕控件
由于屏幕旋转后,控件的位置会发生变化,我们要让这些控件能够摆放相对比较合理。我们可以通过“Control Size”调整。 其中红色实线代表绝对位置, 红色虚线代表相对位置。
横屏竖屏切换不同视图
新建工程Swap
SwapViewController.h文件
#import <UIKit/UIKit.h>
#define degreesToRadians(x) (M_PI * (x) / 180.0)
@interface SwapViewController : UIViewController {
UIView *landscape;
UIView *portrait;
UIButton *landscapeFooButton;//Foo
UIButton *portraitFooButton;
UIButton *landscapeBarButton;//Bar
UIButton *portraitBarButton;
}
@property (nonatomic, retain) IBOutlet UIView *landscape;
@property (nonatomic, retain) IBOutlet UIView *portrait;
@property (nonatomic, retain) IBOutlet UIButton *landscapeFooButton;
@property (nonatomic, retain) IBOutlet UIButton *portraitFooButton;
@property (nonatomic, retain) IBOutlet UIButton *landscapeBarButton;
@property (nonatomic, retain) IBOutlet UIButton *portraitBarButton;
-(IBAction) buttonPressed:(id)sender;
@end
#define degreesToRadians(X) (M_PI * (x) / 180.0)这是一个宏,用于在度数和弧度之间的转换。
landscape水平视图,portrait垂直视图;
landscapeFooButton水平视图中Foo按钮;
portraitFooButton垂直视图中Foo按钮;
landscapeBarButton水平视图中Bar按钮;
portraitBarButton垂直视图中Bar按钮。
视图设计
输出口和事件
编写m实现文件
#import "SwapViewController.h"
@implementation SwapViewController
@synthesize landscape;
@synthesize portrait;
@synthesize landscapeFooButton;
@synthesize portraitFooButton;
@synthesize landscapeBarButton;
@synthesize portraitBarButton;
-(IBAction) buttonPressed:(id)sender {
if (sender == portraitFooButton) {
NSLog(@"portraitFooButton press.");
} else if (sender == landscapeFooButton) {
NSLog(@"landscapeFooButton press.");
} else if (sender == landscapeBarButton) {
NSLog(@"landscapeBarButton press.");
} else {
NSLog(@"portraitBarButton press.");
}
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
//return (interfaceOrientation == UIInterfaceOrientationPortrait);
return YES;
}
-(void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation duration: (NSTimeInterval)duration{
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
self.view = self.portrait;
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(0));
self.view.bounds = CGRectMake(0.0, 0.0, 320.0, 460.0);
} else if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
self.view = self.landscape;
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(-90));
self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0);
} else if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
self.view = self.portrait;
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(180));
self.view.bounds = CGRectMake(0.0, 0.0, 320.0, 460.0);
} else if (interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
self.view = self.landscape;
self.view.transform = CGAffineTransformIdentity;
self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(90));
self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0);
}
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
self.landscape = nil;
self.portrait = nil;
self.landscapeFooButton = nil;
self.portraitFooButton = nil;
self.landscapeBarButton = nil;
self.portraitBarButton = nil;
}
- (void)dealloc {
[landscape release];
[portrait release];
[landscapeFooButton release];
[portraitFooButton release];
[landscapeBarButton release];
[portraitBarButton release];
[super dealloc];
}
@end
willAnimateRotationToInterfaceOrientationduration:这个方法来自我们重写的一个父类,这个方法在旋转开始之后与旋转实际发生之前被调用。
CGAffineTransformIdentity,重置变换属性。
CGAffineTransformMakeRotation来创建一个旋转变换。
注:
1 本教程是基于关东升老师的教程
2 基于黑苹果10.6.8和xcode4.2
3 本人初学,有什么不对的望指教
4 教程会随着本人学习,持续更新
5 教程是本人从word笔记中拷贝出来了,所以格式请见谅
IOS之基本UI控件
3.1 Button控件
3.2 开关控件
3.3 滑块控件
3.4 工具栏
3.5 WebView
3.1 Button控件
iPhone的Button控件可以做的很绚丽,Button可以有多种状态:
" Default State
" Highlighted State
" Selected State
" Disabled State
实现上图的效果:新建ButtonsBackground项目:
ButtonsBackgroundViewController.h文件
@interface ButtonsBackgroundViewController : UIViewController {
UIButton * clearButton;
UIButton * smallButton;
}
@property (nonatomic, retain) IBOutlet UIButton * clearButton;
@property (nonatomic, retain) IBOutlet UIButton * smallButton;
- (IBAction) disableBut: (id) sender;
@end
ButtonsBackgroundViewController.m文件
@synthesize clearButton;
@synthesize smallButton;
- (IBAction) disableBut: (id) sender {
if(clearButton.enabled == YES) {
clearButton.enabled = NO;
smallButton.enabled = NO;
[((UIButton *) sender) setTitle:@"Enable" forState:UIControlStateNormal];
}
else {
clearButton.enabled = YES;
smallButton.enabled = YES;
[((UIButton *) sender) setTitle:@"Disable" forState:UIControlStateNormal];
}
}
- (void)dealloc {
[clearButton release];
[smallButton release];
[super dealloc];
}
点击Disable按钮时候,调用disableBut方法,在该方法中实现了clearButton按钮和smallButton按钮“可用”状态和“不可用”状态的切换。在状态发生切换时候还要改变Disable按钮的上面的“标题”和“状态”:
[((UIButton *) sender) setTitle:@"Enable"forState:UIControlStateNormal];
sender是事件源即点击的按钮对象本身。
Interface Builder设计页面 :
点击Disable按钮的时候,会改变clearButton和smallButton的标题,因此需要连接File’s Owner到两个按钮(clearButton和smallButton)的输出口。
为了响应Disable按钮的事件需要,从Disable按钮连接到File’s Owner定义的disableBut事件。
clearButton属性设定
属性框,使Shows Touch on Highlight is checked 选项被选中。
Default State Configuration选中时候,设置按钮图片power.png背景图片butbackgray.png。
Highlighted State Configuration 选中时候,设置按钮图片power.png ,背景图butbackbluegray.png。
Disabled State Configuration 选中时候,设置按钮图片powerdisabled.png背景图片,butbackgraydisabled.png。
smallButton属性设定
Default State Configuration选中时候,设置按钮图片butbackgray.png背景图片, 设置title“Shock”。
Highlighted State Configuration 选中时候,设置图片butbackbluegray.png背景图片,设置title“Shocking”。
3.2 开关控件
开关控件(Switch),有些相windows中的checkbox,它只有两种状态,true和false。
可以通过该方法改变开关控件的状态。
-(void) setOn: (BOOL) on animated: (BOOL) animated
3.3 滑块控件
滑块控件(Slider),水平放置,可以用手触摸改变它的值。
默认情况下它的最小值0最大值1.00,而.50是初始值,我们可以通过下面的方法设定值:
- (void) setValue:(float) value animated:(BOOL) animated
开关和滑块实例
我们通过在页面放在开关和滑块控件了解他们的使用情况。
新建项目SwitchSlider:
SwitchSliderViewController.h
@interface SwitchSliderViewController : UIViewController {
UISwitch * mySwitch;
}
@property(nonatomic, retain)IBOutlet UISwitch * mySwitch;
-(IBAction) handleSwitch: (id) sender;
-(IBAction) handleSlider: (id) sender;
@end
SwitchSliderViewController.m
@implementation SwitchSliderViewController
@synthesize mySwitch;
- (IBAction) handleSwitch: (id) sender {
if( [((UISwitch *) sender) isOn] == YES){
NSLog(@"It's on");
} else {
NSLog(@"It's off");
}
}
- (IBAction) handleSlider: (id) sender {
NSLog(@"value: %f", ((UISlider *)sender).value);
if( [((UISlider *) sender) value] == ((UISlider *) sender) .maximumValue) {
[mySwitch setOn:YES animated:YES];
}
}
- (void)dealloc {
[mySwitch release];
[super dealloc];
}
连接输出口和动作事件
连接开关控件到的handleSwitch: 动作。
连接滑块控件到的handleSlider: 动作。
制定开关控件输出口。
3.4 分段控件
分段控件(Segment),是有2个或更多段构成的组,它相当与独立的按钮。
新建项目Segment:
SegmentViewController.h
定义一个动作事件
@interface SegmentViewController : UIViewController {
}
- (IBAction) handleSegment: (id) sender;
SegmentViewController.m
- (IBAction) handleSegment: (id) sender {
UISegmentedControl * myseg = (UISegmentedControl *) sender;
if(myseg.selectedSegmentIndex == 0) {
NSLog(@"selected zero index...");
}
else if(myseg.selectedSegmentIndex == 1) {
NSLog(@"selected one index...");
}
else {
NSLog(@"selected two index...");
}
}
IB设计视图
连接动作事件
连接段控件到File’s Owner的handleSegment: 动作。
3.4 工具栏
工具栏(UIToolBar),一般是放置在屏幕的底部,在工具栏的内部可以放置多个按钮和控件。
新建项目ToolBar:
ToolBarViewController.h
定义两个动作事件
@interface ToolBarViewController : UIViewController {
IBOutlet UIActivityIndicatorView * myActivityView;
}
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView * myActivityView;
-(IBAction)onClickStartButton: (id)sender;
-(IBAction)onClickOpenButton: (id)sender;
@end
ToolBarViewController.m
@implementation ToolBarViewController
@synthesize myActivityView;
-(IBAction)onClickStartButton: (id)sender {
if ([myActivityView isAnimating]) {
[myActivityView stopAnimating];
} else {
[myActivityView startAnimating];
}
}
-(IBAction)onClickOpenButton: (id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示信息"
message:@"您点击了打开按钮" delegate:self
cancelButtonTitle:@"Done"
otherButtonTitles:nil];
[alert show];
[alert release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[myActivityView release];
[super dealloc];
}
@end
IB设计视图
连接动作事件
连接到打开按钮的onClickOpenButton: 动作。
连接到开始按钮的onClickStartButton: 动作。
连接到UIActivityIndicatorView输出口。
3.5 WebView
WebView可以帮助我们构建Web的iPhone应用程序。 很多网站的iPhone和iPad客户端程序都是使用WebView开
发的。 WebView能够支持HTML5,不支持Flash等。
新建项目MyWeb:
MyWebViewController.h
@interface MyWebViewController : UIViewController <UIWebViewDelegate> {
IBOutlet UITextField * myTextField;
IBOutlet UIWebView * myWebView;
}
@property(nonatomic, retain) UIWebView * myWebView;
@property(nonatomic, retain) UITextField * myTextField;
- (IBAction) changeLocation: (id) sender;
@end
MyWebViewController 需要实现协议UIWebViewDelegate,它一个委托对象。委托是一种设计模式,在iPhone中主要用于回调事件。在委托中定义了一下方法,实现了该委托的对象,要提供该方法的实现。
UIWebViewDelegate的方法是webViewDidFinishLoad: 它上在异步情况一个网址,当应答回来后回调该方法。
MyWebViewController.m
@implementation MyWebViewController
@synthesize myWebView;
@synthesize myTextField;
- (void) viewDidLoad {
myWebView.delegate = self;
}
- (void)dealloc {
myWebView.delegate = nil;
[myTextField release];
[myWebView release];
[super dealloc];
}
- (IBAction) changeLocation: (id) sender {
[myTextField resignFirstResponder];
NSURL * url = [NSURL URLWithString: myTextField.text];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[myWebView loadRequest:request];
}
#pragma mark WebView 委托
#pragma mark --
- (void)webViewDidFinishLoad: (UIWebView *) webView {
NSLog(@"%@", [webView stringByEvaluatingJavaScriptFromString:
@"document.body.innerHTML"]);
}
@end
在viewDidLoad 方法中的myWebView.delegate =self是指定为自身。
webViewDidFinishLoad方法中实现委托回调功能。
[myWebView stringByEvaluatingJavaScriptFromString:
@"document.documentElement.textContent”]
是运行一个JavaScript脚本程序,document.body.innerHTML获得页面中的HTML代码。
IOS之多视图应用程序
4.1 多视图应用程序介绍
4.2 UIViewController多视图程序
4.3 标签栏控制器
4.4 导航视图控制器应用
4.5 iOS应用程序风格类型
4.1 多视图应用程序介绍
iOS的很多应用程序都是采用多视图设计。控制器类型4种多视图程序:
" 自定义视图控制器
" 标签栏控制器
" 导航控制器
" 表视图控制器
视图和控制器类图
定义视图控制器
直接继承了UIViewController类视图控制器,或者由UIViewController作为根控制器。
标签栏控制器
由UITabBarController类作为根视图控制器。
导航视图控制器
由UINavigationController类作为根视图控制器。
表视图控制器
由UITableViewController类为根视图控制器。
模态(Modal)视图控制器
模态视图控制器非根控制器方式,可以是上面提供的几种控制器,它能够帮助展示用户信息。
4.2 UIViewController多视图程序
在iOS中我们是通过一个根视图控制器来,控制其他的视图控制器,其它的视图控制器在加载视图并显示。这里的根视图控制器是采用UIViewController类型。
1 编写根视图控制器
创建一个基于视图的应用程序(MutilViewSwitch),将自动产生的视图控制器作为,我们的根视图控制器。
IB工具设计根视图,为根视图添加工具栏和按钮。
2 IB设计根视图
3 增加BlueViewController
通过IB设计子视图,设置蓝色背景,并添加一个按钮。
BlueViewController.h
@interface BlueViewController : UIViewController {
}
-(IBAction)onClickButton:(id)sender;
@end
BlueViewController.m
@implementation BlueViewController
-(IBAction)onClickButton:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"系统消息"
message:@"蓝色视图"
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
@end
4 增加YellowViewController
@interface YellowViewController : UIViewController {
}
-(IBAction)onClickButton:(id)sender;
@end
YellowViewController.m
-(IBAction)onClickButton:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"系统消息"
message:@"黄色视图"
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
5 根视图
根视图控制h文件
#import "BlueViewController.h"
#import "YellowViewController.h"
@interface MutilViewSwitchViewController : UIViewController {
YellowViewController *yellowViewController;
BlueViewController *blueViewController;
}
@property (nonatomic, retain) YellowViewController *yellowViewController;
@property (nonatomic, retain) BlueViewController *blueViewController;
-(IBAction) switchViews:(id)sender;
@end
定义两个子视图控制器输出口,并定义一个是动作switchViews:用于切换视图时候调用。
根视图控制m文件
@implementation MutilViewSwitchViewController
@synthesize yellowViewController;
@synthesize blueViewController;
- (void)viewDidLoad {
BlueViewController *blueController = [[BlueViewController alloc]
initWithNibName:@"BlueViewController" bundle:nil];
self.blueViewController = blueController;
[self.view insertSubview:blueController.view atIndex:0];
[blueController release];
[super viewDidLoad];
}
- (void)dealloc {
[yellowViewController release];
[blueViewController release];
[super dealloc];
}
[[BlueViewController alloc] initWithNibName:@"BlueViewController" bundle:nil];
该方法可以通过nib文件创建一个视图控制器。
[self.view insertSubview:blueController.view atIndex:0];
该语句是将蓝色视图插入当前根视图中,0是在将视图放在所有视图之后。
-(IBAction) switchViews:(id)sender {
if (self.yellowViewController.view.superview == nil) {
if (self.yellowViewController.view == nil) {
YellowViewController *yellowController = [[YellowViewController alloc]
initWithNibName:@"YellowViewController" bundle:nil];
self.yellowViewController = yellowController;
[yellowController release];
}
[blueViewController.view removeFromSuperview];
[self.view insertSubview:yellowViewController.view atIndex:0];
} else {
if (self.blueViewController.view == nil) {
BlueViewController *blueController = [[BlueViewController alloc]
initWithNibName:@"BlueViewController" bundle:nil];
self.blueViewController = blueController;
[blueController release];
}
[yellowViewController.view removeFromSuperview];
[self.view insertSubview:blueViewController.view atIndex:0];
}
}
self.yellowViewController.view.superview == nil
该语句为ture的情况有两种, 一个是yellowViewController没有实例化。
二是yellowViewController已经创建,但是 yellowViewController的视图没有添加到根视图中。
接下来通过self.yellowViewController.view == nil判断 yellowViewController是否实例化,如果没有实例化可以通 过initWithNibName:方法创建。
然后通过[blueViewController.view removeFromSuperview]从根视图中移除原来有 blueViewController的视图。再通过 [self.view insertSubview:yellowViewController.view atIndex:0]方法 将yellowViewController的视图添加到根视图上。
4.3 标签栏控制器
UITabBarController是TabBar(标签栏)控件的视图控制器,可以作为根控制器。
1 标签栏控制器原理
2 创建一个TabBarApplication
3 添加视图控制器
增加两个文件:BlueViewController和YellowViewController
BlueViewCotroller.h和BlueViewCotroller.m
@interface BlueViewCotroller : UIViewController {
}
-(IBAction)onClickButton:(id)sender;
@end
-(IBAction)onClickButton:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"系统消息"
message:@"蓝色视图"
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
YellowViewController.h和YellowViewController.m
//-h
@interface YellowVieController : UIViewController {
}
-(IBAction)onClickButton:(id)sender;
@end
//-m
-(IBAction)onClickButton:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"系统消息"
message:@"黄色视图"
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
4 设计MainWindow.xib
5 设计YellowViewController.xib
6 设计BlueViewController.xib
TabBar控制器在iOS中应用很多,TabBar控制器将其它的子视图控制器,放入到一个NSArray中,根据用户所在不同的TabBarItem找到对应的子视图控制器,由该视图控制器再加载对应的视图。
4.4 导航视图控制器应用
导航栏控制器是UINavigationController作为根控制器
1 导航控制器原理
导航控制器堆栈
2 创建一个Navigation-Based Application
工程创建完成后,已经具有了一个根视图控制器RootViewController它是UITableViewController控制器,一般情况下我们nav控制器应用程序都会与UITableViewController搭配,而RootViewController可以理解为“一级”(根视图)视图控制器,它包含了很多“二级”视图控制器,“二级”视图控制器一般也是UITableViewController类型,它又包含了多个“三级”视图控制器,而“三级”视图控制器是树的末梢,则一般是UIViewController。当然,如果应用只有二级的则第“二级”就是UIViewController。
3 编写一级视图控制器
删除RootViewController.h, RootViewController.m,RootViewController.xib文件(注:这里删除上面的三个文件重新建文件可
以;如果不删除这三个文件在它们的基础上改写代码也可以。)
创建一个具有nib的ViewController—RedViewController, 然后,重复创建二个YellowViewController BlueViewController。
IB设计一级视图
4 添加UINavigationItem按钮
5 RedViewController.h
#import <UIKit/UIKit.h>
#import "YellowViewController.h"
@interface RedViewController : UIViewController {
YellowViewController * second;
}
@property (nonatomic, retain) YellowViewController * second;
- (IBAction) moveToNextView: (id) sender;
@end
RedViewController.m
@implementation RedViewController
@synthesize second;
- (IBAction) moveToNextView: (id) sender {
YellowViewController *yViewController = [[YellowViewController alloc]
initWithNibName:@"YellowViewController" bundle:nil];
self.second = yViewController;
[yViewController release];
[self.navigationController pushViewController:self.second animated: YES];
}
IB中连接动作
!在IB中把导航按钮连接到动作moveToNextView:
6 二级视图
二级视图和三级视图不能够通过IB增加导航按钮,我们要通过程序增加导航按钮
#import "YellowViewController.h"
@implementation YellowViewController
@synthesize three;
- (IBAction) moveToNextView: (id) sender {
BlueViewController *bViewController = [[BlueViewController alloc]
initWithNibName:@"BlueViewController" bundle:nil];
self.three = bViewController;
[bViewController release];
[self.navigationController pushViewController:self.three animated: YES];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"二级视图";
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:@"下一步" style:UIBarButtonItemStyleBordered
target:self
action:@selector(moveToNextView:)];
self.navigationItem.rightBarButtonItem = nextButton;
}
- (void)viewDidUnload {
[super viewDidUnload];
self.three = nil;
}
- (void)dealloc {
[three release];
[super dealloc];
}
@end
YellowViewController.h
#import <UIKit/UIKit.h>
#import "BlueViewController.h"
@interface YellowViewController : UIViewController {
BlueViewController * three;
}
@property (nonatomic, retain) BlueViewController * three;
- (IBAction) moveToNextView: (id) sender;
@end
7 三级视图
BlueViewController.h
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"三级视图";
}
4.5 iOS应用程序风格类型
根据用户的视觉、行为特性、数据模型、用户体验等方面定义了三种应用风格:
" 效率型应用程序
" 实用型应用程序
" 沉浸式应用程序
1 效率型应用程序
效率型应用程序具有组织和操作具体信息的功能。效率型应用程序通常用于完成比较重要的任务。效率型应用程序通常分层组织信息。邮件是效率型应用程序的典型例子。
2 实用型应用程序
实用型应用程序完成的简单任务对用户输入要求很低。用户打开实用型应用程序,是为了快速查看信息摘要或是在少数对象上执行简单任务 。天气程序就是一个实用型应用程序的典型例子。它在一个易读的摘要中显示了重点明确的信息。
3 沉浸式应用程序
沉浸式应用程序提供具有丰富视觉效果的全屏环境,专注于内容和用户对内容的体验。用户通常使用沉浸式应用程序进行娱乐,不论是玩游戏,观看多媒体内容还是执行简单的任务。
4 编写实用型应用程序
5 实用型AP中的模态视图控制器
模态视图控制器主要用于“中断”你当前的“工作流程”,转而去做其它的事情,模态视图控制器可以是前面讲过的任何视图控制器。换句话讲它们都可以以模态方式显示视图。
主视图控制器与被呈现的模态视图控制器之间是一中“父子”关系,MainViewController为父视图控制器,FlipsideViewController为子视图控制器。
presentModalViewController:呈现模态控制器
dismissModalViewControllerAnimated:关闭模态
6 实用型AP中的委托设计模式
委托是一种回调或通知机制,在事情发生的时候被调用的。
委托一般定义成为一个协议,实现该协议的对象就是委托对象,其中的实现了该协议的方法将被回调。
MainViewController.h
#import "FlipsideViewController.h"
@interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {
}
- (IBAction)showInfo:(id)sender;
@end
FlipsideViewController.h
#import <UIKit/UIKit.h>
@protocol FlipsideViewControllerDelegate;
@interface FlipsideViewController : UIViewController {
id <FlipsideViewControllerDelegate> delegate;
}
@property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
@end
@protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
@end
MainViewController.m实现委托关闭模式视图
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {
[self dismissModalViewControllerAnimated:YES];
}
MainViewController.m呈现模式视图
- (IBAction)showInfo:(id)sender {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@"FlipsideView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
// UIModalTransitionStyleCoverVertical = 0,
// UIModalTransitionStyleFlipHorizontal,
// UIModalTransitionStyleCrossDissolve,
// UIModalTransitionStylePartialCurl,
[self presentModalViewController:controller animated:YES];
[controller release];
}
controller.delegate = self;通过该语句,指定委托对象,就是将当前控制器传递给了模态控制器。
controller.modalTransitionStyle =
UIModalTransitionStyleFlipHorizontal;是模态控制器类型,此外还有:
" UIModalTransitionStyleCoverVertical
" UIModalTransitionStyleCrossDissolve
" UIModalTransitionStylePartialCurl
[self presentModalViewController:controller animated:YES]呈现模态控制器。
FlipsideViewController.m关闭模式视图
IOS之高级控件-拾取器
5.1 拾取器
5.2 第一视图控制器
5.3 第二视图控制器
5.4 第三视图控制器
5.5 第四视图控制器
5.1 拾取器
1 创建一个Tab Bar Application
初始化工程
由于我们要自己创建视图控制器,包括第一个试图控制器,所以我们删除:
FirstViewController.h FirstViewController.m SecondView.xib
修改MainWindow.xib文件
删除MainWindow.xib文件中自带的View
指定nib文件和视图控制器
指定Tab栏按钮图标和title
5.2 第一视图控制器
DatePickerViewController.h
@interface DatePickerViewController : UIViewController {
IBOutlet UIDatePicker *datePicker;
}
@property (nonatomic, retain) IBOutlet UIDatePicker *datePicker;
-(IBAction)onClickButton:(id)sender;
@end
日期拾取器,要想显示日期必须定义一个输出口:
@property (nonatomic, retain) UIDatePicker *datePicker;
定义点击选择按钮的动作事件: -(IBAction)onClickButton:(id)sender;
DatePickerViewController.m
@synthesize datePicker;
-(IBAction)onClickButton:(id)sender {
NSDate *selected = [datePicker date];
NSString *message = [[NSString alloc] initWithFormat:@"你选择的日期: %@", selected];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"选择日期"
message:message delegate:nil
cancelButtonTitle:@"OK" otherButtonTitles:nil];
[message release];
[alertView show];
[alertView release];
}
onClickButton:是按钮事件。
SDate *selected = [datePicker date];可以获取日期拾取器时间。
NSString *message = [[NSString alloc] initWithFormat:@"你选择的日期: %@", selected];
可以把日期格式化输出。
初始化和释放资源
- (void)viewDidLoad {
[super viewDidLoad];
NSDate *now = [NSDate date];
[datePicker setDate:now animated:YES];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
self.datePicker = nil;
}
- (void)dealloc {
[datePicker release];
[super dealloc];
}
在事件viewDidLoad:初始化日期拾取器。
NSDate *now = [NSDate date]; 获得当前的系统时间。
[datePicker setDate:now animated:YES];
设定日期拾取器的时间,animated:YES是实现动画效果,在初始化时候滚轮滚动到当前日期。
IB中链接输出口和动作
日期拾取器作为输出口,需要通过File's Owner链接到日期拾取器。
还要链接按钮点击时间,从按钮链接到File's Owner。
5.3 第二视图控制器
SinglePickerViewController.h
@interface SinglePickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
UIPickerView *pickerView;
NSArray *pickerData;
}
@property (nonatomic, retain) IBOutlet UIPickerView *pickerView;
@property (nonatomic, retain) NSArray *pickerData;
-(IBAction)onClickButton:(id)sender;
@end
拾取器,要想显示必须定义一个输出口: @property (nonatomic, retain) UIPickerView *picker;
定义点击选择按钮的动作事件: (IBAction)onClickButton:(id)sender;
pickerData保持拾取器的数据。
拾取器的委托
委托是中的方法是事件触发时候回调的方法。
拾取器的委托是实现协议UIPickerViewDelegate。
UIPickerViewDelegate回调方法:
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
是当选择了拾取器时候,根据选择的行返回拾取器的title。
拾取器的数据源
通过UIPickerViewDataSource协议为拾取器提供数据源,其中包括拾取器的行和列的数据,下面的是数据源要求的方法:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
该方法提供拾取器列的个数,本例子中是1个。
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
该方法提供了拾取器中行数,这里的数组pickerData的长度。
m文件中实现协议
#pragma mark--委托协议方法
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [pickerData objectAtIndex:row];
}
#pragma mark--数据源协议方法
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component {
return [pickerData count];
}
初始化和按钮事件处理
@synthesize pickerView;
@synthesize pickerData;
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:@"欧洲",
@"南美", @"非洲", @"北美",
@"亚洲", @"大洋洲", nil];
self.pickerData = array;
[array release];
}
-(IBAction)onClickButton:(id)sender {
NSInteger row = [pickerView selectedRowInComponent:0];
NSString *selected = [pickerData objectAtIndex:row];
NSString *title = [[NSString alloc] initWithFormat:@"你选择了 %@!", selected];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:@"谢谢你的选择."
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[title release];
[alert show];
[alert release];
}
NSInteger row = [picker selectedRowInComponent:0]; 获得第一列的选择中的行号。
NSString *selected = [pickerData objectAtIndex:row]; 通过行号获得选中的数据。
释放资源
- (void)viewDidUnload {
[super viewDidUnload];
self.pickerView = nil;
self.pickerData = nil;
}
- (void)dealloc {
[pickerView release];
[pickerData release];
[super dealloc];
}
IB中链接输出口和动作
拾取器的输出口,需要File‘s Owner与委托和数据源链接。
拾取器的委托和数据源输出口,需要File‘s Owner与委托和数据源链接。
按钮动作onClickButton需要链接到File‘s Owner。
5.4 第三视图控制器
DoublePickerViewController.h
@interface DoublePickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
UIPickerView *pickerView;
NSArray *pickerData1;
NSArray *pickerData2;
}
@property (nonatomic,retain) IBOutlet UIPickerView *pickerView;
@property (nonatomic,retain) NSArray *pickerData1;
@property (nonatomic,retain) NSArray *pickerData2;
-(IBAction)onClick:(id)sender;
@end
拾取器,要想显示必须定义一个输出口:
@property (nonatomic, retain) UIPickerView *picker;
定义点击选择按钮的动作事件:
-(IBAction)onClick:(id)sender;
@property (nonatomic, retain) NSArray *pickerData1;
@property (nonatomic, retain) NSArray *pickerData2;
保持拾取器两个列中的数据。
拾取器的委托
委托是中的方法是事件触发时候回调的方法。
拾取器的委托是实现协议UIPickerViewDelegate。
UIPickerViewDelegate回调方法:
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
是当选择了拾取器时候,根据选择的行返回拾取器的title。
拾取器的数据源
通过UIPickerViewDataSource协议为拾取器提供数据源,其中包括拾取器的行和列的数据,下面的是数据源要求的方法:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
该方法提供拾取器列的个数,本例子中是2个。
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
该方法提供了拾取器中行数,这里的数组pickerData的长度。
m文件中实现协议
#pragma mark--委托协议方法
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) { //选择了第一列
return [pickerData1 objectAtIndex:row];
} else {//选择了第二列
return [pickerData2 objectAtIndex:row];
}
}
#pragma mark--数据源协议方法
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component {
if (component == 0) { //选择了第一列
return [pickerData1 count];
} else {//选择了第二列
return [pickerData2 count];
}
}
component == 0代表选择的第一列即“洲”。如果component == 1代表选择的是第二列即“体育项目”。
这两个列直接没有关联关系,即你选择了前面和选择了后面没有关系,不会联动。
初始化处理
- (void)viewDidLoad {
NSArray *array1 = [[NSArray alloc] initWithObjects:@"欧洲",
@"南美", @"非洲", @"北美",
@"亚洲", @"大洋洲", nil];
self.pickerData1 = array1;
NSArray *array2 = [[NSArray alloc] initWithObjects:@"足球",
@"篮球", @"羽毛球", @"乒乓球", nil];
self.pickerData2 = array2;
[array1 release];
[array2 release];
}
按钮事件处理
-(IBAction)onClick:(id)sender {
NSInteger row1 = [pickerView selectedRowInComponent:0];
NSInteger row2 = [pickerView selectedRowInComponent:1];
NSString *selected1 = [pickerData1 objectAtIndex:row1];
NSString *selected2 = [pickerData2 objectAtIndex:row2];
NSString *title = [[NSString alloc] initWithFormat:@"你选择了 %@的%@项目!",
selected1,selected2];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:@"谢谢你的选择."
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[selected1 release];
[selected2 release];
[title release];
[alert show];
[alert release];
}
释放资源
- (void)viewDidUnload {
[super viewDidUnload];
self.pickerData1 = nil;
self.pickerData2 = nil;
self.pickerView = nil;
}
- (void)dealloc {
[pickerData1 release];
[pickerData2 release];
[pickerView release];
[super dealloc];
}
IB中链接输出口和动作
拾取器的输出口,需要File‘s Owner与委托和数据源链接。
拾取器的委托和数据源输出口,需要File‘s Owner与委托和数据源链接。
按钮动作onClickButton需要链接到File‘s Owner。
5.5 第四视图控制器
DependentViewController.h
@interface DependentPickerViewController : UIViewController <UIPickerViewDelegate,UIPickerViewDataSource>{
NSDictionary *data;
NSArray *pickerData1;
NSArray *pickerData2;
UIPickerView *pickerView;
}
@property (nonatomic, retain) NSDictionary *data;
@property (nonatomic, retain) NSArray *pickerData1;
@property (nonatomic, retain) NSArray *pickerData2;
@property (nonatomic, retain) IBOutlet UIPickerView *pickerView;
-(IBAction)onClick:(id)sender;
@end
拾取器,要想显示必须定义一个输出口: @property (nonatomic, retain) UIPickerView *pickerView;
定义点击选择按钮的动作事件: -(IBAction)onClickButton:(id)sender;
@property (nonatomic, retain) NSDictionary *data; 保存所有数据
@property (nonatomic, retain) NSArray *pickerData1; 保存第一列的数据
@property (nonatomic, retain) NSArray *pickerData2; 保持第二列的数据
m文件中的初始化方法
@synthesize data;
@synthesize pickerData1;
@synthesize pickerData2;
@synthesize pickerView;
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:@"足球队dictionary"
ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.data = dict;
[dict release];
NSArray *col1 = [self.data allKeys];
NSArray *sorted = [col1 sortedArrayUsingSelector:@selector(compare:)];
self.pickerData1 = sorted;
NSString *selectCol1 = [self.pickerData1 objectAtIndex:0];
NSArray *col2 = [self.data objectForKey:selectCol1];
self.pickerData2 = col2;
}
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:@"足球队dictionary" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] nitWithContentsOfFile:plistPath];
这几行代码是从statedictionary.plist属性列表文件中读取到NSDictionary对象中。
NSArray *sorted = [col1 sortedArrayUsingSelector:@selector(compare:)]; 对数据排序。
在Xcode中创建属性列表文件
编辑属性列表文件
m中的按钮点击事件
-(IBAction)onClick:(id)sender {
NSInteger row1 = [pickerView selectedRowInComponent:0];
NSInteger row2 = [pickerView selectedRowInComponent:1];
NSString *selected1 = [pickerData1 objectAtIndex:row1];
NSString *selected2 = [pickerData2 objectAtIndex:row2];
NSString *title = [[NSString alloc] initWithFormat:@"你选择了 %@的%@项目!",
selected1,selected2];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:@"谢谢你的选择."
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[selected1 release];
[selected2 release];
[title release];
[alert show];
[alert release];
}
实现委托方法
#pragma mark--委托协议方法
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) { //选择了第一列
return [pickerData1 objectAtIndex:row];
} else {//选择了第二列
return [pickerData2 objectAtIndex:row];
}
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component {
if (component == 0) {
NSString *selectCol1 = [pickerData1 objectAtIndex:row];
NSArray *col2 = [self.data objectForKey:selectCol1];
pickerData2 = col2;
//[self.pickerView selectRow:0 inComponent:1 animated:YES];
[self.pickerView reloadComponent:1];
}
}
委托方法是实现拾取器控件两个轮互动关键:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
在该方法中通过下面语句重新加载拾取器: [self.pickerView reloadComponent:1];
实现数据源方法
#pragma mark--数据源协议方法
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component {
if (component == 0) { //选择了第一列
return [pickerData1 count];
} else {//选择了第二列
return [pickerData2 count];
}
}
释放资源
- (void)viewDidUnload {
[super viewDidUnload];
self.data = nil;
self.pickerData1 = nil;
self.pickerData2 = nil;
self.pickerView = nil;
}
- (void)dealloc {
[pickerData1 release];
[pickerData2 release];
[data release];
[pickerView release];
[super dealloc];
}
IB中链接输出口和动作
拾取器的输出口,需要File‘s Owner与委托和数据源链接。
拾取器的委托和数据源输出口,需要File‘s Owner与委托和数据源链接。
按钮动作onClickButton需要链接到File‘s Owner。
6.1 关于表视图
6.2 简单表视图(无格式)
6.3 分段表视图
6.4 分组分段表视图
6.5 索引表视图
6.1 关于表视图
iOS中很多应用都使用了表视图,表视图可以分为:
无格式表视图
分段(Sections)表视图,而分段表视图又分为:
普通分段表视图
分组分段表视图
索引分段表视图
无格式表视图
分组分段表视图
索引分段表视图
6.2 简单表视图(无格式)
SimpleTableViewController.h
@interface SimpleTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *listData;
NSArray *listImage;
}
@property (nonatomic,retain) NSArray *listData;
@property (nonatomic,retain) NSArray *listImage;
@end
表视图中的数据源协议
tableView: numberOfRowsInSection: 提供表视图某个分段的行数。
tableView: cellForRowAtIndexPath: 提供表视图单元格所需要的数据。
表视图中的委托协议
tableView: didSelectRowAtIndexPath:
该委托方法是表视图单元格选择完成之后触发的方法。
m文件中的初始化加载方法
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:@"A1-南非",@"A2-墨西哥",
@"B1-阿根廷",@"B2-尼日利亚",@"C1-英格兰",@"C2-美国",
@"D1-德国",@"D2-澳大利亚",@"E1-荷兰",@"E2-丹麦",
@"G1-巴西",@"G2-朝鲜",@"H1-西班牙",@"H2-瑞士",nil];
NSArray *images = [[NSArray alloc] initWithObjects:@"SouthAfrica.png",@"Mexico.png",
@"Argentina.png",@"Nigeria.png",@"England.png",@"USA.png",
@"Germany.png",@"Australia.png",@"Holland.png",@"Denmark.png",
@"Brazil.png",@"NorthKorea.png",@"Spain.png",@"Switzerland.png",nil];
self.listData = array;
self.listImage = images;
[array release];
[images release];
}
m释放资源
- (void)viewDidUnload {
[super viewDidUnload];
self.listData = nil;
self.listImage = nil;
}
- (void)dealloc {
[listData release];
[listImage release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark -- 实现TableView数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleCellIdentifier = @"SimpleCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleCellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
UIImage *img = [UIImage imageNamed:[listImage objectAtIndex:row]];
cell.imageView.image = img;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
该方法是为表视图提供分段的个数,只有一个分段所以。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
提供表视图单元个所需要的数据。
static NSString *SimpleCellIdentifier = @"SimpleCellIdentifier";
该语句为表视图单元格提供了一个标识,当上面的单元格滚出屏幕,下面的单元格滚入屏幕时候,可以通过判断这个标识是否有可以重用的单元格,如果有则重用,如果没有则创建一个新的。
表视图单元格的单元元素:
图片 cell.imageView.image
文本标签 cell.textLabel.text
详细文本标签 cell.detailTextLabel.text
单元格样式
UITableViewCellStyle举类成员
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
实现TableView委托方法
#pragma mark -- 实现TableView委托方法
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
NSString *rowValue = [listData objectAtIndex:row];
NSString *message = [[NSString alloc] initWithFormat:@"你选择了%@队。", rowValue];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"行选择"
message:message
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[message release];
[alert show];
[alert release];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
该委托方法是选中的单元格时候触发的方法,
[indexPath row]语句可以获得选中的行号。
tableView deselectRowAtIndexPath:indexPath animated:YES];
是可以在表视图选择完成单元格后背景消失,再弹出alert对话框。
设计NIB文件
链接输出口
Table View的委托和数据源输出口,需要File‘s Owner与委托和数据源链接。
6.3 分段表视图
新建SectionTable SingleView项目:
SectionTableViewController.h
@interface SectionTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource> {
NSDictionary *teams;
NSArray *teamsname;
}
@property (nonatomic,retain) NSDictionary *teams;
@property (nonatomic,retain) NSArray *teamsname;
@end
UITableViewDataSource,表视图中的数据源:
tableView: numberOfRowsInSection: 提供表视图分段的个数。
tableView: cellForRowAtIndexPath: 提供表视图单元个所需要的数据。
UITableViewDelegate,表视图中的委托:
tableView: didSelectRowAtIndexPath: 该委托方法是表视图单元个选择之后发生触发
m文件初始化加载方法
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"足球队dictionary" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
self.teams = dict;
[dict release];
self.teamsname = [[teams allKeys] sortedArrayUsingSelector:@selector(compare:)];
}
从属性文件中加载数据到NSDictionary对象中,它的key作为表视图的分段的名字,而value部分集合作为表视图单元格。
编辑数据属性文件
m释放资源
- (void)viewDidUnload {
[super viewDidUnload];
self.teams = nil;
self.teamsname = nil;
}
- (void)dealloc {
[teams release];
[teamsname release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark -- 实现TableView数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *name = [teamsname objectAtIndex:section];
NSArray *team = [teams objectForKey:name];
return [team count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [teamsname count];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
NSString *name = [teamsname objectAtIndex:section];
return name;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
该方法是为表视图提供某个分段中单元格个数。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
提供表视图中分段的个数。
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
提供表视图中分段的Header信息。
接上实现TableView数据源方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *name = [teamsname objectAtIndex:section];
NSArray *team = [teams objectForKey:name];
static NSString *SimpleCellIdentifier = @"SimpleCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleCellIdentifier] autorelease];
}
cell.textLabel.text = [team objectAtIndex:row];
return cell;
}
实现TableView委托方法
#pragma mark --实现TableView委托方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *name = [teamsname objectAtIndex:section];
NSArray *team = [teams objectForKey:name];
NSString *selectedteam = [team objectAtIndex:row];
NSString *message = [[NSString alloc] initWithFormat:@"你选择了%@队。",
selectedteam];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"行选择"
message:message
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[message release];
[alert show];
[alert release];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
该委托方法是选中的单元格时候触发的方法, [indexPath row]语句可以获得选中的行号。
[tableView deselectRowAtIndexPath:indexPath animated:YES];
是可以在表试图选择完成单元格后背景消失,再弹出alert对话框。
设计NIB文件
链接输出口
Table View的委托和数据源输出口,需要File‘s Owner与委托和数据源链接。
6.4 分组分段表视图
由普通分段表变成分组分段表很简单,修改属性就可以了
修改分组分段
在IB中打开表视图属性设置框,在Style项目选择Grouped。
6.5 索引表视图
索引表就是在右边增加一个索引列,如果按照归类它应该属于分段表范围。
代码参考SectionIndexingTable
索引表实现
索引表实现很简单只是需要在分段表的代码基础上增加一个数据源(UITableViewDataSource)实现方法就可以了:
/* 增加索引
*/
-(NSArray *) sectionIndexTitlesForTableView: (UITableView *) tableView {
return teamsname;
}
这个方法是为表视图提供索引所需要的数据集合。
实现搜索栏
代码参考SectionSearchTable
SectionTableViewController.h
@interface SectionTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource,UISearchBarDelegate> {
NSMutableDictionary *allteams;
NSMutableDictionary *teams;
NSArray *teamsname;
//UISearchBar *search;
}
@property (nonatomic,retain) NSMutableDictionary *teams;
@property (nonatomic,retain) NSMutableDictionary *allteams;
@property (nonatomic,retain) NSArray *teamsname;
//@property (nonatomic,retain) UISearchBar *search;
-(void)resetSearch;
@end
m文件中的初始化方法
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"足球队dictionary" ofType:@"plist"];
NSMutableDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
self.allteams = dict;
[dict release];
[self resetSearch];
}
m文件中的resetSearch方法
-(void)resetSearch {
self.teams = self.allteams ;
NSMutableArray *keysArray = [[NSMutableArray alloc] init];
[keysArray addObjectsFromArray:[[teams allKeys] sortedArrayUsingSelector:@selector(compare:)]];
self.teamsname = keysArray;
[keysArray release];
}
m释放资源
- (void)viewDidUnload {
[super viewDidUnload];
self.teams = nil;
self.teamsname = nil;
}
- (void)dealloc {
[teams release];
[teamsname release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark -- 实现TableView数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *name = [teamsname objectAtIndex:section];
NSArray *team = [teams objectForKey:name];
return [team count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [teamsname count];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
NSString *name = [teamsname objectAtIndex:section];
return name;
}
接上实现TableView数据源方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *name = [teamsname objectAtIndex:section];
NSArray *team = [teams objectForKey:name];
static NSString *SimpleCellIdentifier = @"SimpleCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleCellIdentifier] autorelease];
}
cell.textLabel.text = [team objectAtIndex:row];
return cell;
}
接上实现TableView数据源方法
/* 增加索引
*/
-(NSArray *) sectionIndexTitlesForTableView: (UITableView *) tableView {
return teamsname;
}
实现TableView委托方法
#pragma mark --实现TableView委托方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *name = [teamsname objectAtIndex:section];
NSArray *team = [teams objectForKey:name];
NSString *selectedteam = [team objectAtIndex:row];
NSString *message = [[NSString alloc] initWithFormat:@"你选择了%@队。",
selectedteam];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"行选择"
message:message
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[message release];
[alert show];
[alert release];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Search Bar 委托方法
#pragma mark --实现UISearchBar委托方法
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if ([searchText length] == 0) {
[self resetSearch];
return;
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSString *key in self.allteams) {
NSMutableArray *arry = [allteams valueForKey:key];
NSMutableArray *newTeams = [[NSMutableArray alloc] init];
for (NSString *teamName in arry) {
if ([teamName rangeOfString: searchText
options:NSCaseInsensitiveSearch].location != NSNotFound) {
[newTeams addObject:teamName];
}
}
if ([newTeams count] > 0) {
[dict setObject:newTeams forKey:key];
}
[newTeams release];
}
self.teamsname = [[dict allKeys] sortedArrayUsingSelector:@selector(compare:)];
self.teams = dict;
[dict release];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[self resetSearch];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
搜索栏中的文本发生变化的时候,触发这个方法。
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
点击搜索栏的取消按钮时候调用。
视图设计
7.1 导航控制器
7.2 创建第一级控制器
7.3 第一个二级控制器
7.4 第一个三级控制器
7.5 第二个二级表控制器
7.6 第三个二级表控制器
7.7 第四个二级表控制器
7.8 第五个二级表视图控制器
7.8 第六个二级表视图控制器
7.1 导航控制器
关于导航控制器和表视图
导航控制器和表视图密不可分。严格的说,要完成导航控制器的功能并不需要表视图。然而,在实际的应用程序中使用导航控制器时,几乎总是要实现至少一个表,并且通常多个表,因为导航控制器的强大之处在于它能够处理复杂的分层数据,在iPhone的小屏幕上,连续的使用表示分层数据最理想的方式。
7.2 创建第一级控制器
一级控制器RootViewController还是一个UITableViewController,它并不是我们说的导航控制器,我们在委托Delegate中定义了导航控制器UINavigationController,事实上UINavigationController才真正意义的根控制器。
RootViewController.h
#import <UIKit/UIKit.h>
@interface RootViewController : UITableViewController {
NSArray *controllers;
}
@property (nonatomic, retain) NSArray *controllers;
@end
RootViewController.m
@implementation RootViewController
@synthesize controllers;
- (void)viewDidLoad {
self.title = @"First Level";
NSMutableArray *array = [[NSMutableArray alloc] init];
//增加控制器
//
self.controllers = array;
[array release];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[super dealloc];
}
实现TableView数据源方法
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [controllers count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
NSInteger row = [indexPath row];
SecondLevelViewController *controller = [controllers objectAtIndex:row];
cell.textLabel.text = controller.title;
cell.imageView.image = controller.rowImage;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
cell.accessoryType属性设定表视图单元格扩展图标类型。单元格扩展图标类型:
UITableViewCellAccessoryNone,没有扩展图标;
UITableViewCellAccessoryDisclosureIndicator,扩展指示器,触摸该图标将切换到下一级表视图,图标为
UITableViewCellAccessoryDetailDisclosureButton,细节展示按钮,触摸该行将显示当前行的更多详细信息视图,图标为
UITableViewCellAccessoryCheckmark,选中标志,当选中某一行时候标志该行,图标为
实现TableView委托方法
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
SecondLevelViewController *nextController = [self.controllers objectAtIndex:row];
[self.navigationController pushViewController:nextController animated:YES];
}
二级表视图控制器
由于二级控制器也是表视图控制器,而且我们需要在为每个页面指定一个图片,所以我们定义了一个父类SecondLevelViewController
SecondLevelViewController
@interface SecondLevelViewController : UITableViewController {
UIImage *rowImage;
}
@property (nonatomic, retain) UIImage *rowImage;
@end
#import "SecondLevelViewController.h"
@implementation SecondLevelViewController
@synthesize rowImage;
@end
7.3 第一个二级控制器
DisclosureButtonController.h
#import <Foundation/Foundation.h>
#import "SecondLevelViewController.h"
#import "DisclosureDetailController.h"
@interface DisclosureButtonController : SecondLevelViewController {
NSArray *listData;
DisclosureDetailController *childController;
}
@property (nonatomic,retain) NSArray *listData;
@property (nonatomic, retain) DisclosureDetailController *childController;
@end
DisclosureButtonController.m
#import "DisclosureButtonController.h"
#import "SecondLevelViewController.h"
@implementation DisclosureButtonController
@synthesize listData;
@synthesize childController;
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:@"Toy Story",
@"A Bug's Life", @"Toy Story 2", @"Monsters, Inc.",
@"Finding Nemo", @"The Incredibles", @"Cars",
@"Ratatouille", @"WALL-E", @"Up", @"Toy Story 3",
@"Cars 2", @"The Bear and the Bow", @"Newt", nil];
self.listData = array;
[array release];
[super viewDidLoad];
}
- (void)viewDidUnload {
self.listData = nil;
self.rowImage = nil;
}
- (void)dealloc {
[listData release];
[rowImage release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listData count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
NSInteger row = [indexPath row];
NSString *title = [listData objectAtIndex:row];
cell.textLabel.text = title;
//cell.imageView.image = controller.rowImage;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
return cell;
}
实现TableView委托方法
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (childController == nil) {
childController = [[DisclosureDetailController alloc]
initWithNibName:@"DisclosureDetailController"
bundle:nil];
}
//childController.title = @"DisclosureDetail Button Pressed";
NSInteger row = [indexPath row];
NSString *selectedMessage = [listData objectAtIndex:row];
NSString *message = [[NSString alloc] initWithFormat:@"你选择了 %@ 按钮。", selectedMessage];
childController.message = message;
childController.title = selectedMessage;
[message release];
[self.navigationController pushViewController:childController animated:YES];
}
上面的委托方法,是用户选中单元格后触发的方法。
[self.navigationController pushViewController:childController animated:YES];
是将详细视图控制器放置到导航控制器栈中,并以动画效果显示详细视图。
RootViewController中 viewDidLoad方法
//增加细节扩展按钮控制器
DisclosureButtonController *disclosureButtonController = [[DisclosureButtonController alloc]
initWithStyle:UITableViewStylePlain];
disclosureButtonController.title = @"Disclosure Buttons";
disclosureButtonController.rowImage = [UIImage imageNamed:@"disclosureButtonControllerIcon.png"];
[array addObject:disclosureButtonController];
[disclosureButtonController release];
7.4 第一个三级控制器
DisclosureDetailController.h
@interface DisclosureDetailController : UIViewController {
UILabel *label;
NSString *message;
}
@property (nonatomic, retain) IBOutlet UILabel *label;
@property (nonatomic, retain) NSString *message;
@end
message从上一个屏幕传递过来的消息 label显示消息的控件。
m文件中的初始化方法
@implementation DisclosureDetailController
@synthesize message;
@synthesize label;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewWillAppear:(BOOL)animated {
label.text = message;
[super viewWillAppear:animated];
}
不要使用viewDidLoad 方法,而是使用viewWillAppear:animated:方法,该方法是在屏幕出现时候调用。
m文件中释放方法
- (void)viewDidUnload {
[super viewDidUnload];
self.message = nil;
self.label = nil;
}
- (void)dealloc {
[message release];
[label release];
[super dealloc];
}
7.5 第二个二级表控制器
CheckListController.h
#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@interface CheckListController : SecondLevelViewController {
NSArray *listData;
NSIndexPath *lastIndexPath;
}
@property (nonatomic, retain) NSArray *listData;
@property (nonatomic, retain) NSIndexPath *lastIndexPath;
@end
CheckListController.m
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:@"Who Hash",
@"Bubba Gump Shrimp Étouffée", @"Who Pudding", @"Scooby Snacks",
@"Everlasting Gobstopper", @"Green Eggs and Ham", @"Soylent Green",
@"Hard Tack", @"Lembas Bread", @"Roast Beast", @"Blancmange", nil];
self.listData = array;
[array release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
self.listData = nil;
self.lastIndexPath = nil;
}
- (void)dealloc {
[listData release];
[lastIndexPath release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listData count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
NSInteger row = [indexPath row];
// NSInteger oldRow = [lastIndexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
// cell.accessoryType = (row == oldRow && lastIndexPath != nil) ?
// UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
实现TableView委托方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int newRow = [indexPath row];
int oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1;
if (newRow != oldRow) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
lastIndexPath = indexPath;
}
}
int oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1;
获得上次选择的单元格行,如果lastIndexPath为nil这设置为-1
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
设置新单元格为UITableViewCellAccessoryCheckmark oldCell.accessoryType = UITableViewCellAccessoryNone;
设置旧单元格为UITableViewCellAccessoryNone
RootViewController中 viewDidLoad方法
//增加check控制器
CheckListController *checkListController = [[CheckListController alloc]
initWithStyle:UITableViewStylePlain];
checkListController.title = @"Check One";
checkListController.rowImage = [UIImage imageNamed:@"checkmarkControllerIcon.png"];
[array addObject:checkListController];
[checkListController release];
7.6 第三个二级表控制器
RowControlsController.h
#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@interface RowControlsController : SecondLevelViewController {
NSArray *listData;
}
@property (nonatomic, retain) NSArray *listData;
-(IBAction)buttonTapped:(id)sender;
@end
RowControlsController.m
@implementation RowControlsController
@synthesize listData;
-(IBAction)buttonTapped:(id)sender {
UIButton *senderButton = (UIButton *)sender;
UITableViewCell *buttonCell = (UITableViewCell *)[senderButton superview];
NSInteger buttonRow = [[self.tableView indexPathForCell:buttonCell] row];
NSString *rowTitle = [listData objectAtIndex:buttonRow];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"点击Button"
message:[NSString stringWithFormat:@"你点击的Button是 %@",rowTitle]
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:@"R2-D2",
@"C3PO", @"Tik-Tok", @"Robby", @"Rosie", @"Uniblab",
@"Bender", @"Marvin", @"Lt. Commander Data",
@"Evil Brother Lore", @"Optimus Prime", @"Tobor", @"HAL",
@"Orgasmatron", nil];
self.listData = array;
[array release];
}
- (void)viewDidUnload {
self.listData = nil;
}
- (void)dealloc {
[listData release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listData count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
UIImage *buttonUpImage = [UIImage imageNamed:@"button_up.png"];
UIImage *buttonDownImage = [UIImage imageNamed:@"button_down.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0.0f, 0.0f, buttonUpImage.size.width, buttonUpImage.size.height);
[button setBackgroundImage:buttonUpImage forState:UIControlStateNormal];
[button setBackgroundImage:buttonDownImage forState:UIControlStateHighlighted];
[button setTitle:@"Tap" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryView = button;
}
NSInteger row = [indexPath row];
NSString *rowTitle = [listData objectAtIndex:row];
cell.textLabel.text = rowTitle;
return cell;
}
由于我们没有nib文件,所以按钮要通过代码自己写按钮, 如下:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
指定按钮的边框大小:
button.frame = CGRectMake(0.0f, 0.0f, buttonUpImage.size.width, buttonUpImage.size.height);
设定按钮正常状态时候背景图片
[button setBackgroundImage:buttonUpImage forState:UIControlStateNormal];
设定按钮高亮状态时候背景图片
[button setBackgroundImage:buttonDownImage forState:UIControlStateHighlighted];
button setTitle:@"Tap" forState:UIControlStateNormal 设置按钮正常状态时候的title内容。
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
由于没有nib文件按钮事件不能通过IB设计工具添加,要通过代码实现与按钮事件的处理。
cell.accessoryView = button;
把按钮对象赋给单元格的accessoryView(扩展视图)。
实现TableView委托方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger buttonRow = [indexPath row];
NSString *rowTitle = [listData objectAtIndex:buttonRow];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"点击Row"
message:[NSString stringWithFormat:@"你点击的Row是 %@",rowTitle]
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
RowControlsController.m
-(IBAction)buttonTapped:(id)sender {
UIButton *senderButton = (UIButton *)sender;
UITableViewCell *buttonCell = (UITableViewCell *)[senderButton superview];
NSInteger buttonRow = [[self.tableView indexPathForCell:buttonCell] row];
NSString *rowTitle = [listData objectAtIndex:buttonRow];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"点击Button"
message:[NSString stringWithFormat:@"你点击的Button是 %@",rowTitle]
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:@"R2-D2",
@"C3PO", @"Tik-Tok", @"Robby", @"Rosie", @"Uniblab",
@"Bender", @"Marvin", @"Lt. Commander Data",
@"Evil Brother Lore", @"Optimus Prime", @"Tobor", @"HAL",
@"Orgasmatron", nil];
self.listData = array;
[array release];
}
buttonTapped:方法,是点击单元格中的按钮触发事件。
UITableViewCell *buttonCell = (UITableViewCell *)[senderButton superview];
其中superview获得父控件,即表视图单元格。
NSInteger buttonRow = [[self.tableView indexPathForCell:buttonCell] row];
其中获得选择的单元格中的按钮所在的单元格行数。
RootViewController中 viewDidLoad方法
//增加Row控制器
RowControlsController *rowControlsController = [[RowControlsController alloc]
initWithStyle:UITableViewStylePlain];
rowControlsController.title = @"Row Controls";
rowControlsController.rowImage = [UIImage imageNamed:@"rowControlsIcon.png"];
[array addObject:rowControlsController];
[rowControlsController release];
7.7 第四个二级表控制器
MoveMeController.h
#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@interface MoveMeController : SecondLevelViewController {
NSMutableArray *listData;
}
@property (nonatomic, retain) NSMutableArray *listData;
-(IBAction)toggleMove;
@end
MoveMeController.m
@implementation MoveMeController
@synthesize listData;
-(IBAction)toggleMove {
[self.tableView setEditing:!self.tableView.editing animated:YES];
if (self.tableView.editing) {
[self.navigationItem.rightBarButtonItem setTitle:@"Done"];
} else {
[self.navigationItem.rightBarButtonItem setTitle:@"Move"];
}
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
self.listData = nil;
}
- (void)dealloc {
[listData release];
[super dealloc];
}
toggleMove方法,是点击导航控制器右边按钮时候触发事件,如果表单元格处于编辑状态时候,设为不可编辑,反之可以编辑单元格。
MoveMeController.m
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
if (listData == nil) {
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:
@"Eeny", @"Meeny", @"Miney", @"Moe", @"Catch", @"A",
@"Tiger", @"By", @"The", @"Toe", nil];
self.listData = array;
[array release];
}
UIBarButtonItem *moveButton = [[UIBarButtonItem alloc]
initWithTitle:@"Move"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(toggleMove)];
self.navigationItem.rightBarButtonItem = moveButton;
[moveButton release];
[super viewDidLoad];
}
实现TableView数据源方法
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [listData count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
//cell.showsReorderControl = YES;
}
NSInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath {
NSInteger fromRow = [fromIndexPath row];
NSInteger toRow = [toIndexPath row];
id object = [listData objectAtIndex:fromRow];//[[listData objectAtIndex:fromRow] retain];
[listData removeObjectAtIndex:fromRow];
[listData insertObject:object atIndex:toRow];
//[object release];
}
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
控制单元格行是否可以移动,本例中我们是可以移动所有行。
实现TableView委托方法
#pragma mark -
#pragma mark Table view delegate
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
我们希望能够对行重新排序,不过不希望用户能够删除或插入行,因此,我们实现了上面的委托方法,通过这个方法,表视图可以询问指定的行是否可以被删除,或是否可以将新行插入到指定的位置。通过为每一行返回
UITableViewCellEditingStyleNone,表示我们不支持插入或删除任何行。
RootViewController中 viewDidLoad方法
//增加Move控制器
MoveMeController *moveMeController = [[MoveMeController alloc]
initWithStyle:UITableViewStylePlain];
moveMeController.title = @"Move Me";
moveMeController.rowImage = [UIImage imageNamed:@"moveMeIcon.png"];
[array addObject:moveMeController];
[moveMeController release];
7.8 第五个二级表视图控制器
DeleteMeController.h
#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@interface DeleteMeController : SecondLevelViewController {
NSMutableArray *listData;
}
@property (nonatomic, retain) NSMutableArray *listData;
-(IBAction)toggleMove;
@end
DeleteMeController.m
@implementation DeleteMeController
@synthesize listData;
-(IBAction)toggleMove {
[self.tableView setEditing:!self.tableView.editing animated:YES];
if (self.tableView.editing) {
[self.navigationItem.rightBarButtonItem setTitle:@"Done"];
} else {
[self.navigationItem.rightBarButtonItem setTitle:@"Move"];
}
}
- (void)dealloc {
[listData release];
[super dealloc];
}
toggleMove方法,是点击导航控制器右边按钮时候触发事件,如果表单元格处于编辑状态时候,设为不可编辑,反之可以编辑单元格。
DeleteMeController.m
- (void)viewDidLoad {
if (listData == nil) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"computers" ofType:@"plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
self.listData = array;
[array release];
}
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithTitle:@"Delete"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(toggleMove)];
self.navigationItem.rightBarButtonItem = editButton;
[editButton release];
}
实现TableView数据源方法
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [listData count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
NSInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSInteger row = [indexPath row];
[self.listData removeObjectAtIndex:row];
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
该委托方法是实现删除和插入功能。
RootViewController中 viewDidLoad方法
//增加Delete控制器
DeleteMeController *deleteMeController = [[DeleteMeController alloc]
initWithStyle:UITableViewStylePlain];
deleteMeController.title = @"Delete Me";
deleteMeController.rowImage = [UIImage imageNamed:@"deleteMeIcon.png"];
[array addObject:deleteMeController];
[deleteMeController release];
7.9 第六个二级表视图控制器
TeamsViewController.h
#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@interface TeamsViewController : SecondLevelViewController {
NSArray *listData;
}
@property (nonatomic, retain) NSArray *listData;
@end
TeamsViewController.m
#import "TeamsViewController.h"
#import "TeamsViewController.h"
#import "TeamsDetailController.h"
@implementation TeamsViewController
@synthesize listData;
- (void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:@"teamdictionary" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.listData = [dict allKeys];
[dict release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
self.listData = nil;
}
- (void)dealloc {
[listData release];
[super dealloc];
}
实现TableView数据源方法
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listData count];
}
实现TableView数据源方法
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSInteger row = [indexPath row];
NSString *title = [listData objectAtIndex:row];
cell.textLabel.text = title;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
NSString *groupName = [listData objectAtIndex:row];
NSString *path = [[NSBundle mainBundle] pathForResource:@"teamdictionary" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
TeamsDetailController *detailController = [[TeamsDetailController alloc]
initWithStyle:UITableViewStyleGrouped];
detailController.listData = [dict objectForKey:groupName];
[dict release];
//[array release];
// Navigation logic may go here. Create and push another view controller.
[self.navigationController pushViewController:detailController animated:YES];
[detailController release];
}
可编辑表视图控制器
TeamsDetailController.h
#import <UIKit/UIKit.h>
#define TEAM1 1
#define TEAM2 2
#define TEAM3 3
#define TEAM4 4
#define LABLE_TAG 45678
@interface TeamsDetailController : UITableViewController <UITextFieldDelegate> {
NSArray *listData;
NSMutableArray *teamsData;
NSArray *fieldLables;
}
@property (nonatomic, retain) NSArray *listData;
@property (nonatomic, retain) NSArray *fieldLables;
@property (nonatomic, retain) NSMutableArray *teamsData;
-(IBAction)cancel:(id)sender;
-(IBAction)save:(id)sender;
-(IBAction)textFieldDone:(id)sender;
@end
TeamsDetailController.m
#import "TeamsDetailController.h"
@implementation TeamsDetailController
@synthesize listData;
@synthesize fieldLables;
@synthesize teamsData;
-(IBAction)cancel:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
-(IBAction)save:(id)sender {
for (UIView *oneView in self.tableView.subviews) {
if ([oneView isMemberOfClass:[UITableViewCell class]]) {
UITableViewCell *cell = (UITableViewCell *)oneView;
for (UIView *twoView in cell.contentView.subviews) {
if ([twoView isMemberOfClass:[UITextField class]]) {
UITextField *textField = (UITextField *)twoView;
NSLog(@"行 %i -- value %@", textField.tag ,textField.text);
}
}
}
}
[self.navigationController popViewControllerAnimated:YES];
}
-(IBAction)textFieldDone:(id)sender {
[sender resignFirstResponder];
}
- (void)viewDidUnload {
self.listData = nil;
self.teamsData = nil;
self.fieldLables = nil;
self.listData = nil;
}
- (void)dealloc {
[listData release];
[teamsData release];
[fieldLables release];
[super dealloc];
}
- (void)viewDidLoad {
teamsData = [[NSMutableArray alloc] init];
for (id name in listData) {
[teamsData addObject:name];
}
NSArray *array = [[NSArray alloc] initWithObjects:@"第一队:",@"第二队:",@"第三队:",@"第四队:", nil];
self.fieldLables = array;
[array release];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]
initWithTitle:@"Cancel"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(cancel:)];
self.navigationItem.leftBarButtonItem = cancelButton;
[cancelButton release];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
initWithTitle:@"Save"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(save:)];
self.navigationItem.rightBarButtonItem = saveButton;
[saveButton release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
实现TableView数据源方法
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [teamsData count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 75, 25)];
label.textAlignment = UITextAlignmentRight;
label.tag = LABLE_TAG;
label.font = [UIFont boldSystemFontOfSize:14];
[cell.contentView addSubview:label];
[label release];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 12, 200, 25)];
textField.clearsOnBeginEditing = NO;
[textField setDelegate:self];
textField.returnKeyType = UIReturnKeyDone;
[textField addTarget:self action:@selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit];
[cell.contentView addSubview:textField];
[textField release];
}
NSInteger row = [indexPath row];
UILabel *label = (UILabel *)[cell viewWithTag:LABLE_TAG];
UITextField *textField = nil;
for (UIView *oneView in cell.contentView.subviews) {
if ([oneView isMemberOfClass:[UITextField class]]) {
textField = (UITextField *)oneView;
}
}
label.text = [fieldLables objectAtIndex:row];
textField.text = [listData objectAtIndex:row];
textField.tag = row;
return cell;
}
实现TableView委托方法
#pragma mark 文本字段控件的委托方法
- (void)textFieldDidEndEditing:(UITextField *)textField {
NSLog(@"修改 行 %i -- value %@", textField.tag ,textField.text);
}
RootViewController中 viewDidLoad方法
//增加可编辑详细窗格控制器
TeamsViewController *teamsViewController = [[TeamsViewController alloc]
initWithStyle:UITableViewStylePlain];
teamsViewController.title = @"Detail Edit";
teamsViewController.rowImage = [UIImage imageNamed:@"detailEditIcon.png"];
[array addObject:teamsViewController];
[teamsViewController release];
IOS之应用程序设置
8.1 应用程序设置概念
8.2 创建设置束
8.3 设置项目种类
8.4 读取设置
8.1 应用程序设置概念
应用程序设置(Application Settings):在iOS和Mac OS很多软件中都有使用偏好(preferences),例如,在iOS中我们设置WiFi、运用商和壁纸等等。
8.2 创建设置束
创建工程
Settings Bundle(设置束)
一个应用程序的使用偏好,是存储在一个Root.list的XML文件中的,在这个文件中是与Settings Bundle关联起来的。可以在Root.list中指定设置项目的类型,可以有常规字段类型、划块、开关和子视图类型等等。
创建一个设置束
上图中在Resource文件夹下添加设置束文件Settings.bundle(默认名称)。展开Settings.bundle文件夹:
Root.plist图标 是设置属性的属性列表
en.lproj文件夹 是本地化应用程序时使用的
Root.plist的PreferenceSpecifiers节点
8.3 设置项目种类
生成的设置束文件设置项目
从上面的运行的应用程序看,我们没有编写任何一行代码系统就已经帮我们创建好一些设置项目了。
PSGroupSpecifier
设置项目Type为PSGroupSpecifier用于指示该项目是一个新分组的开始,其后的每个项目都将是此分组的一部分,直到一个Type值为PSGroupSpecifier的项目之前。
在本例子中我们还要设置Title为Group,有多个Group时候Title不能重复。
PSTextFieldSpecifier
设置项目Type为PSTextFieldSpecifier是指示该项目是一个特定的使用偏好字段类型。其它项目的说明:
PSToggleSwitchSpecifier
设置项目Type为PSToggleSwitchSpecifier是指示该项目是一个特定的使用偏好开关类型。其它项目的说明:
PSSliderSpecifier
设置项目Type为PSSliderSpecifier是指示该项目是一个特定的使用偏好滑块类型。其它项目的说明
为滑块添加最大最小值图片
Show Package Contents来访问束的内容。这将打开一个新的窗口。将图标文件复制到此文件夹中。这样束就能找到这两个图标文件了。
添加最大最小值图片文件结构
具体往束里设置图片:
运行结果
添加子设置视图
添加完子视图,运行程序:
8.4 读取设置
读取应用程序中设置
我们使用NSUserDefaults类读取用户设置,通常使用
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSUserDefaults获得值方法有:
arrayForKey: boolForKey: dataForKey:
dictionaryForKey: floatForKey: integerForKey:
objectForKey: stringArrayForKey: stringForKey:
MainViewController.h
MainViewController.m
FlipsideViewController.h
FlipsideViewController.m
运行
IOS之数据持久化
9.1 数据持久化概述
9.2 iOS应用程序目录结构
9.3 读写属性列表
9.4 对象归档
9.5 访问SQLite
9.1 数据持久化概述
iOS中可以有四种持久化数据的方式: 属性列表、对象归档、SQLite3和Core Data
9.2 iOS应用程序目录结构
iOS应用程序运行在Mac os模拟器时候,有一下临时目录模拟器3.1.3为例子:
/Users/tony/Library/Application Support/iPhone Simulator/3.1.3/Applications
IOS应用程序采用沙盒原理设计,ios每个应用程序都有自己的3个目录(Document,Library,tmp),互相之间不能访问。
Documents存放应用程序的数据。
Library目录下面还有Preferences和Caches目录,Preferences目录存放应用程序的使用偏好,Caches目录与Documents很相 似可以存放应用程序的数据。
tmp目录供应用程序存储临时文件。
9.3 读写属性列表
读取Documents目录下文件
可以获得应用程序的Documents文件夹。
NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* myDocPath = [myPaths objectAtIndex:0];
获取文件的完整路径。
- (NSString*)filePath:(NSString*)fileName {
NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* myDocPath = [myPaths objectAtIndex:0];
NSString* filePath = [myDocPath stringByAppendingPathComponent:fileName];
return filePath;
}
获取tmp目录
获取应用程序的tmp目录要比获取Documents目录容易的多。使用函数NSTemporaryDirectory ()可以获得tmp目录路径。
NSString* tempPath = NSTemporaryDirectory();
获取文件的完整路径。
NSString* tempFile = [tempPath stringByAppendingPathComponent:@"properties.plist"];
属性列表文件实例 :PropertesList
PropertesListViewController.h
#import "Student.h"
@interface ViewController : UIViewController
@property (retain, nonatomic) IBOutlet UITextField *studentNo;
@property (retain, nonatomic) IBOutlet UITextField *studentName;
@property (retain, nonatomic) IBOutlet UITextField *studentClass;
- (IBAction)save:(id)sender;
- (IBAction)load:(id)sender;
- (IBAction)endEditing:(id)sender;
- (IBAction)saveToArchiver:(id)sender;
- (IBAction)loadFromArchiver:(id)sender;
- (NSString*)filePath:(NSString*)fileName;
@end
PropertesListViewController.m
@synthesize studentNo;
@synthesize studentName;
@synthesize studentClass;
- (NSString*)filePath:(NSString*)fileName {
NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* myDocPath = [myPaths objectAtIndex:0];
NSString* filePath = [myDocPath stringByAppendingPathComponent:fileName];
return filePath;
}
- (IBAction)save:(id)sender {
NSString* fileName = [self filePath:@"properties.plist"];
NSLog(fileName);
NSMutableArray* data = [[NSMutableArray alloc]init];
[data addObject:studentNo.text];
[data addObject:studentName.text];
[data addObject:studentClass.text];
[data writeToFile:fileName atomically:YES];
}
- (IBAction)load:(id)sender {
NSString* fileName = [self filePath:@"properties.plist"];
if ([[NSFileManager defaultManager]fileExistsAtPath:fileName]) {
NSArray* data = [[NSArray alloc]initWithContentsOfFile:fileName];
studentNo.text = [data objectAtIndex:0];
studentName.text = [data objectAtIndex:1];
studentClass.text = [data objectAtIndex:2];
[data release];
}
}
- (IBAction)endEditing:(id)sender {
[sender resignFirstResponder];
}
9.4 对象归档
对象归档实例:Encoding
对象归档
“归档”是值另一种形式的序列化,对模型对象进行归档的技术可以轻松将复杂的对象写入文件,然后再从中读取它们,只要在类中实现的每个属性都是基本数据类型(如int或float)或都是符合NSCoding协议的某个类的实例,你就可以对你的对象进行完整归档。
实现NSCoding协议
NSCoding协议声明了两个方法: -(void)encodeWithCoder:(NSCoder *)aCoder,是将对象写入到文件中。
-(id)initWithCoder:(NSCoder *)aDecoder,是将文件中数据读入到对象中。
实现NSCopying协议
NSCopying协议声明了一个方法: -(id)copyWithZone:(NSZone *)zone ,是将对象复制方法。
Student.h
@interface Student : NSObject<NSCoding, NSCopying>
@property (retain, nonatomic) NSString* studentNo;
@property (retain, nonatomic) NSString* studentName;
@property (retain, nonatomic) NSString* studentClass;
@end
Student.m
#import "Student.h"
@implementation Student
@synthesize studentNo = _studentNo;
@synthesize studentName = _studentName;
@synthesize studentClass = _studentClass;
#pragma mark NSCopying
- (id)copyWithZone:(NSZone *)zone {
Student* copy = [[[self class]allocWithZone:zone]init];
copy.studentNo = [_studentNo copyWithZone:zone];
copy.studentName = [_studentName copyWithZone:zone];
copy.studentClass = [_studentClass copyWithZone:zone];
return copy;
}
#pragma mark NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_studentNo forKey:@"studentNo"];
[aCoder encodeObject:_studentName forKey:@"studentName"];
[aCoder encodeObject:_studentClass forKey:@"studentClass"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
_studentNo = [aDecoder decodeObjectForKey:@"studentNo"];
_studentName = [aDecoder decodeObjectForKey:@"studentName"];
_studentClass = [aDecoder decodeObjectForKey:@"studentClass"];
return self;
}
-(NSString*)description {
return [[[NSString alloc]initWithFormat:@"no:%@ name:%@ class:%@", _studentNo, _studentName, _studentClass]autorelease];
}
- (void)dealloc {
[_studentName release];
[_studentClass release];
[_studentNo release];
[super dealloc];
}
@end
EncodingViewController.h
详细见上。
EncodingViewController.m
- (IBAction)saveToArchiver:(id)sender {
NSString* fileName = [self filePath:@"student.archiver"];
NSMutableData* data = [NSMutableData data];
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
Student* student = [[Student alloc]init];
student.studentNo = studentNo.text;
student.studentName = studentName.text;
student.studentClass = studentClass.text;
[archiver encodeObject:student forKey:@"myStudent"];
[archiver finishEncoding];
[data writeToFile:fileName atomically:YES];
[archiver release];
[student release];
}
NSMutableData * theData = [NSMutableData data];用于包含编码的数据。
NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData];创建NSKeyedArchiver实例,用于将对象归档到此theData实例中。
[archiver encodeObject:student forKey:@"mystudent"]; 使用“键-值”对编码来对希望包含在归档中的对象进行归档。
[theData writeToFile:filename atomically:YES]; 写入数据到归档文件。
EncodingViewController.m
- (IBAction)loadFromArchiver:(id)sender {
NSString* fileName = [self filePath:@"student.archiver"];
NSData* data = [NSData dataWithContentsOfFile:fileName];
if ([data length] > 0) {
NSKeyedUnarchiver* unArchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
Student* student = [unArchiver decodeObjectForKey:@"myStudent"];
studentNo.text = student.studentNo;
studentName.text = student.studentName;
studentClass.text = student.studentClass;
[unArchiver finishDecoding];
[unArchiver release];
}
}
NSData * theData =[NSData dataWithContentsOfFile:filename];从归档文件中获得NSData实例。
NSKeyedUnarchiver * archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];
创建一个NSKeyedUnarchiver实例对数据进行解码。Student *student = [archiver decodeObjectForKey:@"mystudent"];
使用与归档编码使用相同的键对象进行解码。
9.5 访问SQLite
SQLite数据库
SQLite是一个开源的嵌入式关系数据库,它在2000年由D. Richard Hipp发布,它的减少应用程序管理数据的开销,SQLite可移植性好,很容易使用,很小,高效而且可靠。
SQLite嵌入到使用它的应用程序中,它们共用相同的进程空间,而不是单独的一个进程。从外部看,它并不像一个RDBMS,但在进程内部,它却是完整的,自包含的数据库引擎。 嵌入式数据库的一大好处就是在你的程序内部不需要网络配置,也不需要管理。因为客户端和服务器在同一进程空间运行。SQLite 的数据库权限只依赖于文件系统,没有用户帐户的概念。SQLite 有数据库级锁定,没有网络服务器。它需要的内存,其它开销很小,适合用于嵌入式设备。你需要做的仅仅是把它正确的编译到你的程序。
SQLite数据类型
SQLite是无类型的,这意味着你可以保存任何类型的数据到你所想要保存的任何表的任何列中, 无
论这列声明的数据类型是什么,对于SQLite来说对字段不指定类型是完全有效的,如:
Create Table ex1(a, b, c);
SQLite允许忽略数据类型,但是仍然建议在你的Create Table语句中指定数据类型, 因为数据类型对于你和其他的程序员交流, 或者你准备换掉你的数据库引擎。 SQLite支持常见的数据类型, 如:
在iOS中使用SQLite3
为了能够在iOS中使用SQLite3需要是将libsqlite3.dylib类库添加到Xcode工程中,在工程的Frameworks(框架) 文件夹右键添加存在Frameworks
或者导航到 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/ iPhoneSimulator<version>.sdk/usr/lib 目录下面找到libsqlite3.dylib.
实例:StudentSQLite3
StudentSQLite3ViewController.h
#import "sqlite3.h"
#define DATA_FILE @"data.sqlite3"
#define TABLE_NAME @"student"
#define FIELDS_NAME_SID @"studentId"
#define FIELDS_NAME_SNAME @"studentName"
#define FIELDS_NAME_SCLASS @"studentClass"
@interface ViewController : UIViewController {
sqlite3* db;
}
@property (retain, nonatomic) IBOutlet UITextField *studentId;
@property (retain, nonatomic) IBOutlet UITextField *studentName;
@property (retain, nonatomic) IBOutlet UITextField *studentClass;
- (IBAction)saveFromSqlite:(id)sender;
- (IBAction)loadFromSqlite:(id)sender;
-(NSString*)dataFile;
-(IBAction)textFieldDoneEditing:(id)sender;
@end
StudentSQLite3ViewController.m
@synthesize studentId;
@synthesize studentName;
@synthesize studentClass;
-(NSString*)dataFile {
NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* myDocPath = [myPaths objectAtIndex:0];
NSString* fileName = [myDocPath stringByAppendingFormat:DATA_FILE];
return fileName;
}
无参数SQLite3处理过程
1、打开数据库sqlite3_open。
2、创建数据库表和执行SQL语句sqlite3_exec。
3、释放资源sqlite3_close。
创建数据库
- (void)viewDidLoad {
[super viewDidLoad];
NSString* fileName = [self dataFile];
NSLog(@"%@", fileName);
if (sqlite3_open([fileName UTF8String], &db) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(NO, @"OPEN SQLITE DATABASE ERROR!");
} else {
char* error;
NSString* createSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@ TEXT PRIMARY KEY, %@ TEXT, %@% TEXT);",
TABLE_NAME, FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS];
if (sqlite3_exec(db, [createSQL UTF8String], NULL, NULL, &error)) {
sqlite3_close(db);
NSAssert1(NO, @"CREATE TABLE ERROR", error);
} else {
sqlite3_close(db);
}
}
}
sqlite3_open([[self dataFilePath] UTF8String], &db) != SQLITE_OK sqlite3_open打开数据库,注意:在sqlite3中的函数都是使用C字符串[self dataFilePath] UTF8String]是将NSString字符串转换为C字符串,&db是sqlite3指针(* db)的地址。
该函数sqlite3_open返回SQLITE_OK打开成功。
sqlite3_exec(db, [tablesql UTF8String], NULL, NULL, &err) != SQLITE_OK
sqlite3_exec是执行任何不带返回值sql语句,第2个参数是要执行的sql语句,第3个参数是要回调函数,第4个参数是要回调函数的参数,第5个参数是执行出错的字符串。
sqlite3_close(db); 是关闭数据库。
NSAssert是断言函数,当断言失败时候打印信息。
NSAssert1是带有一个参数的NSAssert函数,此外还有NSAssert2等函数。
有参数的SQLite3处理过程
1、打开数据库sqlite3_open。
2、预处理SQL语句sqlite3_prepare_v2。
3、绑定参数sqlite3_bind_text。
4、执行语句sqlite3_step(statement) 。
5、释放资源sqlite3_finalizesqlite3_close。
数据保存
- (IBAction)saveFromSqlite:(id)sender {
NSString* fileName = [self dataFile];
NSLog(@"%@", fileName);
if (sqlite3_open([fileName UTF8String], &db)) {
sqlite3_close(db);
NSAssert(NO, @"OPEN DATABASE ERROR");
} else {
NSString* sqlStr = [NSString stringWithFormat:@"INSERT OR REPLACE INTO %@(%@, %@, %@) VALUES(?, ?, ?)",TABLE_NAME, FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS];
sqlite3_stmt* statement;
//预处理过程
if (sqlite3_prepare(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) {
//绑定参数开始
sqlite3_bind_text(statement, 1, [studentId.text UTF8String], -1, NULL);
sqlite3_bind_text(statement, 2, [studentName.text UTF8String], -1, NULL);
sqlite3_bind_text(statement, 3, [studentClass.text UTF8String], -1, NULL);
//执行插入
if (sqlite3_step(statement) != SQLITE_DONE) {
NSAssert(0, @"INSERT DATABASE ERROR!");
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
}
sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, nil) == SQLITE_OK
sqlite3_prepare_v2执行sql语句,第3个参数-1代表全部sql字符串长度,第4个参数&statement是sqlite3_stmt指针(* statement)的地址,第5个参数是sql语句没有被执行的部分语句。
sqlite3_bind_text(statement, 1, [studentId.text UTF8String], -1, NULL);
是绑定参数,第2个参数为序号(从1开始),第3个参数为字符串值,第4个参数为字符串长度。 第5个参数为一个函数指针,SQLITE3执行完操作后回调此函数,通常用于释放字符串占用的内存。
sqlite3_step(statement) != SQLITE_DONE判断是否执行完成sql语句执行。
sqlite3_finalize(statement)和sqlite3_close(db)释放资源。
查询数据
- (IBAction)loadFromSqlite:(id)sender {
NSString* fileName = [self dataFile];
NSLog(@"%@", fileName);
if (sqlite3_open([fileName UTF8String], &db) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(NO, @"OPEN DATABASE ERROR!");
} else {
NSString* sqlStr = [NSString stringWithFormat:@"SELECT %@,%@,%@ FROM %@ WHERE %@=?",
FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS, TABLE_NAME, FIELDS_NAME_SID];
sqlite3_stmt* statement;
//预处理过程
if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) {
//绑定参数开始
sqlite3_bind_text(statement, 1, "1000", -1, NULL);
//执行
while (sqlite3_step(statement) == SQLITE_ROW) {
char* field1 = (char*)sqlite3_column_text(statement, 0);
NSString* field1Str = [[NSString alloc]initWithUTF8String:field1];
studentId.text = field1Str;
char* field2 = (char*)sqlite3_column_text(statement, 1);
NSString* field2Str = [[NSString alloc]initWithUTF8String:field2];
studentName.text = field2Str;
char* field3 = (char*)sqlite3_column_text(statement, 2);
NSString* field3Str = [[NSString alloc]initWithUTF8String:field3];
studentClass.text = field3Str;
[field1Str release];
[field2Str release];
[field3Str release];
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
}
while (sqlite3_step(statement) == SQLITE_ROW) sqlite3_step(statement) == SQLITE_ROW单步执行并判断sql语句执行的状态。
char *field1 = (char *) sqlite3_column_text(statement, 0); sqlite3_column_text(statement, 0);取出字段值,第2个参数是列的顺序,序号是从0开始。
NSString *field1Str = [[NSString alloc] initWithUTF8String: field1];构建NSSting字符串。
其它部分代码
-(IBAction)textFieldDoneEditing:(id)sender {
[sender resignFirstResponder];
}
- (void)viewDidUnload
{
[self setStudentId:nil];
[self setStudentName:nil];
[self setStudentClass:nil];
[super viewDidUnload];
}
- (void)dealloc {
[studentId release];
[studentName release];
[studentClass release];
[super dealloc];
}
IOS之云端应用
10.1 GET请求
10.2 XML解析
10.3 JSON解析
10.4 POST请求
10.1 GET请求
通过一个第三方提供的云服务,查询IP归属地:http://www.youdao.com/smartresult-xml/search.s?type=ip&q=218.241.121.186
它的返回格式是xml :
新建个例子:CSSimpleXML,设计原型:
编辑按钮事件:
- (IBAction)query:(id)sender {
NSString* strUrl = [NSString stringWithFormat:@"http://www.youdao.com/smartresult-xml/search.s?type=ip&q=%@", ipText.text];
NSURL* url = [NSURL URLWithString:strUrl];
NSURLRequest* request = [[NSURLRequest alloc]initWithURL:url];
NSURLConnection* connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
[connection release];
[request release];
[activityIndicator startAnimating];
}
定义NSURLConnection的委托:
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
委托(delegate)是一种事件处理机制,当满足条件时候触发。delegate:self说明是委托当前对象处理事件,我们需要实现它们回调方法。
NSURLConnection 回调方法
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 请求成功,并且接收数据。
-(void) connection:(NSURLConnection *)connection didFailWithError: (NSError *)error 请求成功,但是加载数据出现异常。
-(void) connectionDidFinishLoading: (NSURLConnection*) connection加载数据成功,在connection:didReceiveData方法之后执行。
接收数据处理
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data {
//默认对于中文的支持不好
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString* gbkNSString = [[NSString alloc]initWithData:data encoding:enc];
//如果是黑UTF-8 NSXMLParese会报错
xmlString = [[NSString alloc]initWithString:[gbkNSString stringByReplacingOccurrencesOfString:@"<?xml version=\"1.0\" encoding=\"gbk\"?>" withString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>"]];
NSLog(@"%@", xmlString);
[gbkNSString release];
}
iPhone SDK提供的XML解析类只能解析utf-8编码,如果从服务器返回的xml编码是gbk等,要转换成utf-8再开始解析。
10.2 XML解析
关于iPhone发送GET请求,就是通过NSURLRequest和NSURLConnection两个类实现的。
在众多的回调方法。解析XML是在 connectionDidFinishLoading:方法开始的。
解析XML文件也是要通过XML回调方式实现解析处理的。
NSXMLParser,是iPhone解析XML SDK工具类。
NSXMLParser采用SAX方式而不是DOM方式解析,SAX是基于事件触发的解析方式,解析器从上到下遍历xml文档,遇到开始标签、结束标签、文档开始、文档结束和字符串都会触发事件。
解析开始处理
-(void)connectionDidFinishLoading:(NSURLConnection*)connection {
[activityIndicator stopAnimating];
//开始解析XML
NSXMLParser* ipParser = [[NSXMLParser alloc]initWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding ]];;
ipParser.delegate = self;
[ipParser parse];
[ipParser release];
}
NSXMLParser回调方法
- (void)parserDidStartDocument:(NSXMLParser *)parser文档开始的时候触发
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError 文档出错的时候触发
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict 遇到一个开始标签时候触发。
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string 遇到字符串时候触发
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementNamenamespaceURI:(NSString *)
namespaceURIqualifiedName:(NSString *)qName 遇到结束标签时候出发。
- (void)parserDidEndDocument:(NSXMLParser *)parser 遇到文档结束时候触发。
文档开始的回调方法
这个方法在解析过程中只调运一次,一般在这个方法中进行有关解析的初始化处理。
//文档开始的时候触发
- (void)parserDidStartDocument:(NSXMLParser *)parser {
info = [[NSMutableDictionary alloc]initWithCapacity:1];
}
文档出错回调方法
//文档出错的时候触发
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
UIAlertView* errorAlert = [[UIAlertView alloc]initWithTitle:[parseError localizedDescription] message:[parseError localizedFailureReason] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
}
遇到开始标签回调方法
参数elementName是标签的名字,attributeDict 是属性列表,namespaceURI 是命名空间,如果有命名空间qualifiedName是指定的前缀名。
//遇到一个开始标签时候触发。
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict {
NSLog(@"value:%@\n", elementName);
currentTagName = elementName;
}
遇到字符串回调方法
//遇到字符串时候触发
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSLog(@"value:%@\n", string);
string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
if ([currentTagName isEqualToString:@"ip"]) {
if (![string isEqualToString:@""]) {
[info setValue:string forKey:currentTagName];
}
} else if([currentTagName isEqualToString:@"location"]) {
if (![string isEqualToString:@""]) {
[info setValue:string forKey:currentTagName];
}
}
}
遇到结束标签回调方法
//遇到结束标签时候出发。
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementNamenamespaceURI:(NSString *)namespaceURIqualifiedName:(NSString *)qName {
}
遇到结束文档回调方法
// 遇到文档结束时候触发。
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSMutableString* outString = [[NSMutableString alloc]initWithCapacity:1];
for (id key in info) {
[outString appendFormat:@"%@:%@\n", key, [info objectForKey:key]];
}
msgText.text = outString;
[outString release];
[xmlString release];
}
10.3 JSON解析
http://www.geonames.org/export/ws-overview.html
获得JSON:
{
status = {
message = "the daily limit of 30000 credits demo has been exceeded.Please throttle your requests or use the commercial service.";
value = 18;
};
}
10.3.1 JSON解析API
iPhone SDK没有提供JSON解析API,可以使用第三方的API类库json-framework,下载地址:
https://github.com/stig/json-framework/
把Classes/JSON/下面的类拷贝到我们的工程的Classes目录下面,右键添加存在的类文件。
实现回调方法
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void) connectionDidFinishLoading: (NSURLConnection*) connection
-(void) connection:(NSURLConnection *)connection didFailWithError: (NSError *)error
connection:didReceiveData:
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data {
outString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", outString);
}
connectionDidFinishLoading:
outString的 JSONValue消息获得NSMutableDictionary,JSON api中提供了NSString的分类(Catelog)
-(void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSMutableDictionary* jsonObj = [outString JSONValue];
NSLog(@"%@", jsonObj);
NSMutableDictionary* jsonSubObj = [jsonObj objectForKey:@"status"];
NSString* text = [[NSString alloc]initWithFormat:@"message=%@\n\nvalue=%@", [jsonSubObj objectForKey:@"message"],[jsonSubObj objectForKey:@"value"]];
mstText.text = text;
[text release];
[outString release];
[activity stopAnimating];
}
文档出错回调方法
-(void) connection:(NSURLConnection *)connection didFailWithError: (NSError *)error {
UIAlertView* errorAlert = [[UIAlertView alloc]initWithTitle:[error localizedDescription] message:[error localizedFailureReason]
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
}
点击按钮事件
-(IBAction)go:(id)sender{
NSString *strurl =@"http://api.geonames.org/findNearByWeatherJSON?lat=43&lng=-2&username=demo";
NSURL *url = [NSURL URLWithString:strurl];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
[connection release];
[request release];
[activityIndicatorView startAnimating];
}
10.4 POST请求
为了学习iPhone的POST请求,安排案例如下:
在画面中输入用户名和密码,然后以POST方式提交数据到服务器端。
NSMutableURLRequest
POST请求与GET不同,不使用的NSURLRequest,而是使用NSMutableURLRequest类,这是一个可变的NSURLRequest类。
- (IBAction)login:(id)sender {
//http://www.sunnyer.com/shop/member!login.action
//member.email:[email protected]
//member.password:dfgdfgrf
[activity startAnimating];
NSString* post = [NSString stringWithFormat:@"memberIdOrCellphoneOrEmail=%@&&password=%@", username.text, password.text];
NSData* postData = [post dataUsingEncoding:NSUTF8StringEncoding];
NSURL* postServierURL = [NSURL URLWithString:@"http://www.jinjiang.com/membercenter/member/ordinaryLogin"];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:postServierURL];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:postData];
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!connection) {
NSLog(@"Failed to submit request");
} else {
NSLog(@"Request submitted");
}
[connection release];
}
POST参数是以一个字符串方式传递: memberIdOrCellphoneOrEmail=%@&&password=%@
[request setHTTPMethod:@"POST"];
知道请求方法为POST方法,但是要注意POST必须大写。
[request setHTTPBody:postData];该语句是将要提交的数据放到请求体中。
connection:didReceiveData:
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data {
outString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", outString);
}
connectionDidFinishLoading:
-(void)connectionDidFinishLoading:(NSURLConnection*)connection{
NSLog(@"%@", outString);
[webView loadHTMLString:outString baseURL:[[NSURL alloc]initWithString:@"http://www.jinjiang.com"]];
[activity stopAnimating];
}
文档出错回调方法
-(void) connection:(NSURLConnection *)connection didFailWithError: (NSError *)error {
UIAlertView* errorAlert = [[UIAlertView alloc]initWithTitle:[error localizedDescription] message:[error localizedFailureReason]
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
}
IOS之地图和定位应用开发
11.1 iOS定位服务
11.2 iOS地图
11.3 Web地图
11.1 iOS定位服务
iOS中有三个定位服务组件:
Wifi定位,通过查询一个Wifi路由器的地理位置的信息。比较省电,iPod touch和iPad也可以采用。
蜂窝基站定位,通过移动运用商基站定位。也适合有3G版本的iPod touch和iPad。
GPS卫星定位,通过3-4颗GPS定位位置定位,最为准确,但是耗电量大,不能遮挡。
Core Location
Core Location是iPhone、iPad等开发定位服务应用程序的框架。我们要在Xcode中添加“CoreLocation.framework”存在的框架。
主要使用的类是:CLLocationManager,通过CLLocationManager实现定位服务。
CoreLocation.framework
定位服务实例
项目WhereAmI:
WhereAmIViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@interface ViewController : UIViewController<CLLocationManagerDelegate> {
CLLocationManager* locationManager;
}
@property (strong, nonatomic) CLLocationManager* locationManager;
@property (retain, nonatomic) IBOutlet UILabel *longitudeText;
@property (retain, nonatomic) IBOutlet UILabel *latituduText;
@property (retain, nonatomic) IBOutlet UIActivityIndicatorView *activity;
- (IBAction)findMe:(id)sender;
- (IBAction)webMap:(id)sender;
@end
CLLocationManagerDelegate是定位服务的委托,常用的位置变化回调方法是:
locationManager:didUpdateToLocation:fromLocation: locationManager:didFailWithError:
CLLocationManager 是定位服务管理类,通过它可以设置定位服务的参数、获取经纬度等。
m中加载方法
- (IBAction)findMe:(id)sender {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 1000.0f;
[self.locationManager startUpdatingLocation];
[activity startAnimating];
NSLog(@"start gps");
}
CLLocationManager 是的startUpdatingLocation方法启动所有定位硬件,对应的方法是stopUpdatingLocation,通过调用该方法关闭定位服务器更新,为了省电必须在不用的时候调用该方法关闭定位服务。
此外,我们还可以在这里设定定位服务的参数,包括:distanceFilter和desiredAccuracy。
distanceFilter,这个属性用来控制定位服务更新频率。单位是“米”。 desiredAccuracy,这个属性用来控制定位精度,精度
越高耗电量越大。
定位精度
desiredAccuracy精度参数可以iOS SDK通过常量实现:
kCLLocationAccuracyNearestTenMeters,10米
kCLLocationAccuracyHundredMeters ,100米
kCLLocationAccuracyKilometer ,1000米
kCLLocationAccuracyThreeKilometers,3000米
kCLLocationAccuracyBest ,最好的精度
kCLLocationAccuracyBestForNavigation,导航情况下最好精度,iOS 4 SDK新增加。一般要有外接电源时候才能使用。
委托方法用于实现位置的更新
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
latituduText.text = [NSString stringWithFormat:@"%3.5f",newLocation.coordinate.latitude];
longitudeText.text = [NSString stringWithFormat:@"%3.5f",newLocation.coordinate.longitude];
[activity stopAnimating];
[locationManager stopUpdatingLocation];
NSLog(@"location ok");
}
该委托方法不仅可以获得当前位置(newLocation),还可以获得上次的位置(oldLocation ),CLLocation 对象coordinate.latitude属性获得经度,coordinate.longitude属性获得纬度。
[NSString stringWithFormat:@"%3.5f”, newLocation.coordinate.latitude] 中的%3.5f是输出整数部分是3位,小数部分是5位的浮点数。
11.2 iOS地图
iOS应用程序中使用Map Kit API开发地图应用程序。
其核心是MKMapView类使用。
多数情况下地图会与定位服务结合使用。
地图开发一般过程
添加MapKit类库
MapKit.framework
MapMeViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "MapLocation.h"
@interface ViewController : UIViewController<CLLocationManagerDelegate, MKReverseGeocoderDelegate, MKMapViewDelegate> {
}
@property (retain, nonatomic) IBOutlet MKMapView *mapView;
@property (retain, nonatomic) IBOutlet UIActivityIndicatorView *activity;
- (IBAction)findMe:(id)sender;
@end
CLLocationManagerDelegate是定位服务委托。
MKMapViewDelegate是地图视图委托,主要方法:
-mapView:viewForAnnotation:
-mapViewDidFailLoadingMap:withError:
MKReverseGeocoderDelegate是给地理坐标获得标志点信息的委托,用于地理信息编码(即:从坐标获得地点获得信息),主要委托方法:
– reverseGeocoder:didFindPlacemark:
– reverseGeocoder:didFailWithError:
m文件中的视图加载和卸载
- (void)viewDidLoad {
[super viewDidLoad];
mapView.mapType = MKMapTypeStandard;
//mapView.mapType = MKMapTypeSatellite;
//mapView.mapType = MKMapTypeHybrid;
mapView.delegate = self;
}
mapView.mapType = MKMapTypeStandard;是指定地图的类型,iOS提供了三种风格的地图:
MKMapTypeStandard标准地图模式
MKMapTypeSatellite卫星地图模式
MKMapTypeHybrid具有街道等信息的卫星地图模式
mapView.delegate = self;是将委托对象指定为自身。
按钮事件
- (IBAction)findMe:(id)sender {
CLLocationManager *lm = [[CLLocationManager alloc] init];
lm.delegate = self;
lm.desiredAccuracy = kCLLocationAccuracyBest;
[lm startUpdatingLocation];
activity.hidden = NO;
[activity startAnimating];
}
点击按钮时候通过定位服务获取当前位置信息。
通过lm.delegate = self;是将委托对象指定为自身。
因此,点击事件发生时候将会回调CLLocationManagerDelegate委托的
-locationManager:didUpdateToLocation:fromLocation:方法。
回调位置更新方法
#pragma mark CLLocationManagerDelegate Methods
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000);
//[mapView setRegion:viewRegion animated:YES];
MKCoordinateRegion adjustedRegion = [mapView regionThatFits:viewRegion];
[mapView setRegion:adjustedRegion animated:YES];
manager.delegate = nil;
[manager stopUpdatingLocation];
MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];
geocoder.delegate = self;
[geocoder start];
}
MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000); 该函数能够创建一个MKCoordinateRegion结构体,第一个参数是一个CLLocationCoordinate2D结构指定了目标区域的中心点,第二个是目标区域南北的跨度单位是米,第三个是目标区域东西的跨度单位是米。后两个参数的调整会影响地图缩放。
[[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate]; 创建地理编码对象geocoder,通过该对象可以把坐标转换成为地理信息的描述。
geocoder.delegate = self;指定编码的处理是自身对象。
[geocoder start];开始编码处理。
MKReverseGeocoderDelegate
是地理编码委托对象,该委托的方法:
成功时候调用-reverseGeocoder:didFindPlacemark:
失败时候调用-reverseGeocoder:didFailWithError:
成功编码回调方法
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
MapLocation *annotation = [[MapLocation alloc] init];
annotation.streetAddress = placemark.thoroughfare;
annotation.city = placemark.locality;
annotation.state = placemark.administrativeArea;
annotation.zip = placemark.postalCode;
annotation.coordinate = geocoder.coordinate;
[mapView addAnnotation:annotation];
[annotation release];
geocoder.delegate = nil;
[geocoder autorelease];
[activity stopAnimating];
activity.hidden = YES;
}
成功编码后需要在该方法中创建标注对象(MapLocation)。MapLocation 是我们自定义的实现MKAnnotation协议标注对象。 该方法的placemark是MKPlacemark获得很多地理信息,详细见下表。
[mapView addAnnotation:annotation]; 为地图添加标注,该方法将会触发mapView:viewForAnnotation:方法回调。
MKPlacemark类属性
addressDictionary 地址信息的dictionary
thoroughfare 指定街道级别信息
subThoroughfare 指定街道级别的附加信息
locality 指定城市信息
subLocality 指定城市信息附加信息
administrativeArea 行政区域
subAdministrativeArea 行政区域附加信息
country 国家信息
countryCode 国家代号
postalCode 邮政编码
失败编码回调方法
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"地理解码错误息" message:@"地理代码不能识别"
delegate:nil
cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
geocoder.delegate = nil;
[geocoder autorelease];
[activity stopAnimating];
}
MKMapViewDelegate
是地图视图委托对象,本例子我们使用的方法:
- mapView:viewForAnnotation:为地图设置标注时候回调方法。
-mapViewDidFailLoadingMap:withError:地图加载错误时候回调方法。
地图标注回调方法
#pragma mark Map View Delegate Methods
- (MKAnnotationView *) mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>) annotation {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"PIN_ANNOTATION"];
if(annotationView == nil) {
annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:@"PIN_ANNOTATION"] autorelease];
}
annotationView.canShowCallout = YES;
annotationView.pinColor = MKPinAnnotationColorRed;
annotationView.animatesDrop = YES;
annotationView.highlighted = YES;
annotationView.draggable = YES;
return annotationView;
}
与表格视图单元格处理类似,地图标注对象由于会很多,因此需要重复利用,通过
dequeueReusableAnnotationViewWithIdentifier方法可以查找可重复利用的标注对象,以达到节省内存的目的。
annotationView.canShowCallout = YES;指定标注上的插图,点击图钉有气泡显示。
annotationView.pinColor 设置图钉的颜色。
annotationView.animatesDrop动画效果。
地图加载失败回调方法
- (void)mapViewDidFailLoadingMap:(MKMapView *)theMapView withError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"地图加载错误" message:[error localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
自定义地图标注对象
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface MapLocation : NSObject <MKAnnotation, NSCoding> {
NSString *streetAddress;
NSString *city;
NSString *state;
NSString *zip;
CLLocationCoordinate2D coordinate;
}
@property (nonatomic, copy) NSString *streetAddress;
@property (nonatomic, copy) NSString *city;
@property (nonatomic, copy) NSString *state;
@property (nonatomic, copy) NSString *zip;
@property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;
@end
作为地图标注对象实现MKAnnotation协议是必须的,只有实现该协议才能使该类成为标注类。实现NSCoding协议是可选的,实现该协议可以使标注对象能够复制。 里面的属性有哪些要看你自己的需要。
MapLocation.m
- (NSString *)title {
return @"您的位置!";
}
- (NSString *)subtitle {
NSMutableString *ret = [NSMutableString string];
if (streetAddress)
[ret appendString:streetAddress];
if (streetAddress && (city || state || zip))
[ret appendString:@" • "];
if (city)
[ret appendString:city];
if (city && state)
[ret appendString:@", "];
if (state)
[ret appendString:state];
if (zip)
[ret appendFormat:@", %@", zip];
return ret;
}
title 和subtitle 是MKAnnotation协议要求实现的方法。
MapLocation.m
#pragma mark -
- (void)dealloc {
[streetAddress release];
[city release];
[state release];
[zip release];
[super dealloc];
}
#pragma mark -
#pragma mark NSCoding Methods
- (void) encodeWithCoder: (NSCoder *)encoder {
[encoder encodeObject: [self streetAddress] forKey: @"streetAddress"];
[encoder encodeObject: [self city] forKey: @"city"];
[encoder encodeObject: [self state] forKey: @"state"];
[encoder encodeObject: [self zip] forKey: @"zip"];
}
- (id) initWithCoder: (NSCoder *)decoder {
if (self = [super init]) {
[self setStreetAddress: [decoder decodeObjectForKey: @"streetAddress"]];
[self setCity: [decoder decodeObjectForKey: @"city"]];
[self setState: [decoder decodeObjectForKey: @"state"]];
[self setZip: [decoder decodeObjectForKey: @"zip"]];
}
return self;
}
encodeWithCoder:和initWithCoder:是NSCoding协议要求实现方法。
11.3 Web地图
在iOS中我们还可以使用Web地图。
- (IBAction)webMap:(id)sender {
CLLocation *lastLocation = [locationManager location];
if(!lastLocation)
{
UIAlertView *alert;
alert = [[UIAlertView alloc]
initWithTitle:@"系统错误"
message:@"还没有接收到数据!"
delegate:nil cancelButtonTitle:nil
otherButtonTitles:@"OK", nil];
[alert show];
[alert release];
return;
}
NSString *urlString = [NSString stringWithFormat:
@"http://maps.google.com/maps?q=Here+I+Am!@%f,%f",
lastLocation.coordinate.latitude,
lastLocation.coordinate.longitude];
NSURL *url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
}
http://maps.google.com/maps?q=Here+I+Am!@%f,%f是请求Web地图的网站,q后面上参数。
[[UIApplication sharedApplication] openURL:url];打开iOS内置的浏览器,即在内置浏览器中打开地图。
IOS之多媒体API
12.1 播放视频
12.2 播放音频
12.3 播放和录制音频
12.1 播放视频
视频文件介绍
视频格式可以分为适合本地播放的本地影像视频和适合在网络中播放的网络流媒体影像视频两大类。尽管后者在播放的稳定性和播放画面质量上可能没有前者优秀,但网络流媒体影像视频的广泛传播性使之正被广泛应用于视频点播、网络演示、远程教育、网络视频广告等等互联网信息服务领域。
适合移动设备的视频文件
3GP,3GP是一种3G流媒体的视频编码格式,主要是为了配合3G网络的高传输速度而开发的,也是目前手机中最为常见的一种视频格式。 视频MP4格式,除了支持MP3所具有的音乐播放功能外,还具备强大的MPEG-4视频播放能力。
iPhone中还支持mov格式文件。
iOS播放视频
iOS sdk为播放视频提供了非常简便方法,提供的MPMoviePlayerViewController类作为开发使用,在iOS4以前的版本是MPMoviePlayerController。
在iPhone开发规范中禁止使用私有API播放视频,因此播放画面的控制的控件都是有iPhone提供好的,我们没有别的选择。我们能做的:
加载URL中视频
播放、暂停视频
用户控制行为和缩放模式
产生通知
视频播放案例
添加 MediaPlayer.framework
MoviePlayerViewController.h
#import <MediaPlayer/MediaPlayer.h>
@interface MoviePlayerViewController : UIViewController {
MPMoviePlayerViewController * moviePlayerView;
}
@property (nonatomic, retain) MPMoviePlayerViewController * moviePlayerView;
-(IBAction) playMovie: (id) sender;
- (void) playingDone;
@end
m文件的加载和卸载方法
- (void) viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(playingDone) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[moviePlayerView release];
[super dealloc];
}
MPMoviePlayerViewController提供了在播放过程中的状态改变和其它事件的通知。在viewDidLoad注册了一个播放完成的通知,常用的通知有:
MPMoviePlayerPlaybackDidFinishNotification通知接收者播放结束。
MPMoviePlayerScalingModeDidChangeNotification改变影片的尺寸。
MPMoviePlayerContentPreloadDidFinishNotification表示预处理以及完成,准备开始播放影片。
dealloc方法中的[[NSNotificationCenter defaultCenter]
removeObserver:self];影片播放完成要注销通知。
播放事件
- (IBAction) playMovie: (id) sender {
moviePlayerView = [[MPMoviePlayerViewController alloc]
initWithContentURL:[NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:@"short" ofType:@"3gp"]]];
moviePlayerView.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
moviePlayerView.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
// MPMovieControlStyleNone
//MPMovieControlStyleEmbedded
//MPMovieControlStyleDefault
//[movieplayer play];
//在当前view上添加视频的视图
[[[UIApplication sharedApplication] keyWindow] addSubview:moviePlayerView.view];
}
视频文件可以播放资源目录、沙箱目录和网络播放。本例中我们采用资源目录。
moviePlayerView.moviePlayer属性是MPMoviePlayerController类型,它有的controlStyle属性
可以控制播放行为,它的取值有:
MPMovieControlStyleFullscreen
MPMovieControlStyleNone没有播放控件
MPMovieControlStyleEmbedded
MPMovieControlStyleDefault
MPMoviePlayerController类还有scalingMode属性用于控制影片的尺寸,它的取值有:
MPMovieScalingModeNone原始尺寸
MPMovieScalingModeAspectFit缩放到一个填充方向
MPMovieScalingModeAspectFill填充两边可能会切除一部分
MPMovieScalingModeFill填充两边可能会改变比例
播放完成
- (void) playingDone {
NSLog(@"播放完成");
[moviePlayerView.view removeFromSuperview];
[moviePlayerView release];
moviePlayerView = nil;
}
playingDone 方法是在影片播放完成时候调用,这是因为我们在通知中心注册的方法。
播放完成需要把播放视图remove这样才可以获得上一个屏幕。
12.2 播放音频
12.2.1 音频文件介绍
有两类主要的音频文件格式:
无损格式,例如WAV,PCM,TTA,FLAC,AU,APE,TAK,WavPack(WV) ,CAF
有损格式,例如MP3,Windows Media Audio(WMA),Ogg Vorbis(OGG),AAC
移动音频文件
作为移动设备音频文件应该原则上比较小,一般的格式:
WAV、由于无损压缩效果最好。
MP3、有损压缩,文件比较小,由于去除的是人类无法感应到的声音,效果也很好。这是目前常用格式。
AAC、压缩比例更大,比MP3文件还要小。
CAF(Core Audio Format)是Apple专用的无损压缩格式。
12.2.2 Core Audio
高级API,易用
System Sound API –播放短声音、警告音等。
AVFoundation 可以播放长时间声音,简单易用。
低级API,能够对音频有更多的控制
Audio Toolbox – 录制、播放、音频流有全面的控制。
OpenAL – 播放立体声,常用于游戏。
12.2.3 System Sound API
System Sound 可以播放“短的”声音,所谓短声音就是5秒以内。 不循环、没有声音控制、立即播放。
播放格式限制:
线性PCM 和 IMA4
.caf .aif 或 .wav
播放“短声音”
播放“短声音”主要就是两个步骤:
注册声音
AudioServicesCreateSystemSoundID ((CFURLRef)fileURL, &myID);
播放声音
AudioServicesPlaySystemSound (myID);
监听完成事件方法
AudioServicesAddSystemSoundCompletion
清除播放sound ID
SystemSoundID myID;
AudioServicesDisposeSystemSoundID (myID);
震动
也可以通过System Sound API让iPhone震动,但是iPod touch不能震动。
震动可以通过指定一个特殊的system sound ID—— kSystemSoundID_Vibrate实现。
AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
实例
SystemSoundServices
添加AudioToolbox.framework框架
SystemSoundServicesViewController.h文件
#import <UIKit/UIKit.h>#include <AudioToolbox/AudioToolbox.h>
@interface SystemSoundServicesViewController : UIViewController;
- (IBAction) playSystemSound;
- (IBAction) vibrate;
@end
播放事件
- (IBAction) playSystemSound{
NSURL* system_sound_url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"BeepGMC500" ofType:@"wav"]];
SystemSoundID system_sound_id;
AudioServicesCreateSystemSoundID(
(CFURLRef)system_sound_url,
&system_sound_id
);
// Register the sound completion callback.
AudioServicesAddSystemSoundCompletion(
system_sound_id,
NULL, // uses the main run loop
NULL, // uses kCFRunLoopDefaultMode
MySoundFinishedPlayingCallback, // the name of our custom callback function
NULL // for user data, but we don't need to do that in this case, so we just pass NULL
);
// Play the System Sound
AudioServicesPlaySystemSound(system_sound_id);
}
AudioServicesAddSystemSoundCompletion方法5个参数,第一参数SystemSoundID,第二参数是是否使用循环,第三个参数是循环模式,第四个参数是回调函数,就是当播放完成时候回调的方法,第五个参数是为回调函数提供参数。
这里回调的方法是C语言风格的函数:MySoundFinishedPlayingCallback。
回调函数
void MySoundFinishedPlayingCallback(SystemSoundID sound_id, void* user_data){
AudioServicesDisposeSystemSoundID(sound_id);
}
震动方法调用
// Vibrate on action
- (IBAction) vibrate{
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
12.3 播放和录制音频
AVFoundation控件可以实现一般音频播放和录制。
AVAudioPlayer音频播放类,用于播放大于5秒钟声音,可以播放本地声音,但是不能播放网络媒体文件。能够播放、 暂停、循环和跳过等操作。
AVAudioRecorder音频录制类。
实例AVAudioPlayer
添加AVFoundation.framework框架
AvplayerViewController.h文件
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface AvplayerViewController : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer * player;
}
- (IBAction) stopSong: (id) sender;
- (IBAction) playSong: (id) sender;
@end
AvplayerViewController.m
#import "AvplayerViewController.h"
@implementation AvplayerViewController
- (IBAction) playSong: (id) sender {
NSError *error = nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:@"charleston1925_64kb" ofType:@"mp3"]] error:&error];
player.delegate = self;
if(error) {
NSLog(@"%@",[error description]);
[error release];
}
[player play];
}
- (IBAction) stopSong: (id) sender {
[player stop];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
NSLog(@"播放完成。");
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(@"播放错误发生: %@", [error localizedDescription]);
}
- (void)dealloc {
[player release];
[super dealloc];
}
@end
AVAudioPlayer委托
AVAudioPlayerDelegate委托对象提供了两个主要方法:
audioPlayerDidFinishPlaying:successfully:
audioPlayerDecodeErrorDidOccur:error:
AVAudioRecorder
新建实例:Recorder
RecorderViewController.h文件
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface RecorderViewController : UIViewController
{
AVAudioRecorder *recorder;
AVAudioPlayer *player;
UILabel *label;
}
@property (retain, nonatomic) AVAudioRecorder * recorder;
@property (retain, nonatomic) AVAudioPlayer * player;
@property (retain, nonatomic) IBOutlet UILabel *label;
-(IBAction)recordPushed:(id)sender;
-(IBAction)playPushed:(id)sender;
-(IBAction)stopPushed:(id)sender;
@end
音频录制方法
-(IBAction)recordPushed:(id)sender
{
label.text = @"recode...";
if([recorder isRecording])
return;
if([player isPlaying])
[player stop];
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord
error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];
AVAudioSession 是iOS提供音频会话类,音频会话是指定应用程序与音频系统如何交互。AVAudioSession 通过指定一个音频类别(Category)实现的,音频类别(Category)描述了应用程序使用音频的方式。下面是语句是设定音频会话类别:
[[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryRecord error:&error];
AVAudioSessionCategoryRecord代表只能输入音频,即录制音频了。其效果是停止其它音频播放。
使用类别后,音频会话要设置为“活跃的”Active,这会把后台的任何系统声音关闭。
[[AVAudioSession sharedInstance] setActive:YES error:&error];
音频录制方法
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
[settings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM]
forKey:AVFormatIDKey];
[settings setValue:[NSNumber numberWithFloat:44100.0]
forKey:AVSampleRateKey]; //采样率
[settings setValue:[NSNumber numberWithInt:1]
forKey:AVNumberOfChannelsKey];//通道的数目
[settings setValue:[NSNumber numberWithInt:16]
forKey:AVLinearPCMBitDepthKey];//采样位数 默认 16
[settings setValue:[NSNumber numberWithBool:NO]
forKey:AVLinearPCMIsBigEndianKey];//大端还是小端 是内存的组织方式
[settings setValue:[NSNumber numberWithBool:NO]
forKey:AVLinearPCMIsFloatKey];//采样信号是整数还是浮点数
NSString *filePath =
[NSString stringWithFormat:@"%@/rec_audio.caf", [self documentsDirectory]];
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
//[self setRecorder:nil];
recorder = [[AVAudioRecorder alloc]
initWithURL:fileUrl
settings:settings
error:&error];
// [recorder setMeteringEnabled:YES];
[recorder record];
}
-(NSString *)documentsDirectory{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
return [paths objectAtIndex:0];
}
音频播放方法
-(IBAction)playPushed:(id)sender{
label.text = @"play...";
if([recorder isRecording])
[recorder stop];
if([player isPlaying])
[player stop];
NSString *filePath =
[NSString stringWithFormat:@"%@/rec_audio.caf", [self documentsDirectory]];
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
NSError *error = nil;
// [self setPlayer:nil];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
// [player setMeteringEnabled:YES];
[player play];
}
音频停止方法
-(IBAction)stopPushed:(id)sender{
label.text = @"stop...";
if([recorder isRecording])
[recorder stop];
if([player isPlaying])
[player stop];
}
IOS之触摸事件和手势
13.1 事件概述
13.2 触摸事件
13.3 手势
13.1 事件概述
事件是当用户手指触击屏幕及在屏幕上移动时,系统不断发送给应用程序的对象。
系统将事件按照特定的路径传递给可以对其进行处理的对象。
在iOS中,一个UITouch对象表示一个触摸,一个UIEvent对象表示一个事件。事件对象中包含与当前多点触摸序列相对应的所有触摸对象,还可以提供与特定视图或窗口相关联的触摸对象。
响应者对象
响应者对象是可以响应事件并对其进行处理的对象。
UIResponder是所有响应者对象的基类,它不仅为事件处理,而且也为常见的响应者行为定义编程接口。
UIApplication、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
第一响应者是应用程序中当前负责接收触摸事件的响应者对象(通常是一个UIView对象)。UIWindow对象以消息的形式将事件发送给第一响应者,使其有机会首先处理事件。如果第一响应者没有进行处理,系统就将事件(通过消息)传递给响应者链中的下一个响应者,看看它是否可以进行处理。
响应者链
响应链是一个响应者对象的连接序列,事件或动作消息(或菜单编辑消息)依次传递。它允许响应者对象把事件处理的职责转交给其它更高层的对象。应用程序通过向上传递一个事件来查找合适的处理对象。因为点击检测视图也是一个响应者对象,应用程序在处理触摸事件时也可以利用响应链。响应链由一系列的下一个响应者组成。
响应者链处理原则
1. 点击检测视图或者第一响应者传递事件或动作消息给它的视图控制器(如果它有的话);如果没有一个视图控制器,就传递给它的父视图。
2. 如果一个视图或者它的视图控制器不能处理这个事件或动作消息,它将传递给该视图的父视图。
3. 在这个视图层次中的每个后续的父视图遵循上述的模式,如果它不能处理这个事件或动作消息的话。
4. 最顶层的视图如果不能处理这个事件或动作消息,就传递给UIWindow对象来处理。
5. 如果UIWindow 对象不能处理,就传给单件应用程序对象UIApplication。
如果应用程序对象也不能处理这个事件或动作消息,将抛弃它。
13.2 触摸事件
触摸信息有时间和空间两方面,时间方面的信息称为阶段(phrase),表示触摸是否刚刚开始、是否正在移动或处于静止状态,以及何时结束—也就是手指何时从屏幕抬起。触摸信息还包括当前在视图或窗口中的位置信息,以及之前的位置信息(如果有的话)。当一个手指接触屏幕时,触摸就和某个窗口或视图关联在一起,这个关联在事件的整个生命周期都会得到维护。
触摸事件的阶段
事件处理方法
在给定的触摸阶段中,如果发生新的触摸动作或已有的触摸动作发生变化,应用程序就会发送这些消息:
当一个或多个手指触碰屏幕时,发送touchesBegan:withEvent:消息。
当一个或多个手指在屏幕上移动时,发送touchesMoved:withEvent:消息。
当一个或多个手指离开屏幕时,发送touchesEnded:withEvent:消息。
当触摸序列被诸如电话呼入这样的系统事件所取消时,发送touchesCancelled:withEvent:消息。
触摸事件实例 EventInfo
#import <UIKit/UIKit.h>
@interface TouchView : UIView {
}
- (void)logTouchInfo:(UITouch *)touch;
@end
@implementation TouchView
- (void)logTouchInfo:(UITouch *)touch {
CGPoint locInSelf = [touch locationInView:self];
CGPoint locInWin = [touch locationInView:nil];
NSLog(@" touch.locationInView = {%2.3f, %2.3f}", locInSelf.x, locInSelf.y);
NSLog(@" touch.locationInWin = {%2.3f, %2.3f}", locInWin.x, locInWin.y);
NSLog(@" touch.phase = %d", touch.phase);
NSLog(@" touch.tapCount = %d", touch.tapCount);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesBegan - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
}
}
touch.phase,触摸事件的阶段。
touch.tapCount,触摸事件的轻碰次数,可以判断双击事件。
UIEvent 的allTouches方法,可以获得触摸点的集合,可以判断多点触摸事件。
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesMoved - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesEnded - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesCancelled - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
}
}
13.3 手势
手势在iPhone中很重要,手势就是手触摸屏幕的方式。
单碰击
双碰击
多点触摸(合拢和展开)
轻抚
… …
单碰击和双碰击实例:MultiTap
单碰击为红色,双碰击为蓝色
#import <UIKit/UIKit.h>
@interface MultiTapView : UIView {
}
@end
#import "MultiTapView.h"
@implementation MultiTapView
- (void)turnBlue {
self.backgroundColor = [UIColor blueColor];
}
- (void)turnRed {
self.backgroundColor = [UIColor redColor];
}
//START:code.MultiTapView.touchesBegan:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 2) {
[[self class] cancelPreviousPerformRequestsWithTarget:self
selector:@selector(turnRed)
object:nil];
}
}
//END:code.MultiTapView.touchesBegan:
//START:code.MultiTapView.touchesEnded:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 1) {
[self performSelector:@selector(turnRed) withObject:nil afterDelay:0.10f];
}
if(touch.tapCount == 2) {
[self turnBlue];
}
}
//END:code.MultiTapView.touchesEnded:
@end
[self performSelector:@selector(turnRed) withObject:nil afterDelay:0.10f]; 是在0.1秒后调用turnRed方法。
[[self class]cancelPreviousPerformRequestsWithTarget:self selector:@selector(turnRed) object:nil]; 是取消调用方法turnRed。
多点触摸(合拢和展开)PinchZoom
PinchZoomView .h文件
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
@interface PinchZoomView : UIView {
CALayer *robotLayer;
CGFloat previousDistance;
CGFloat zoomFactor;
BOOL pinchZoom;
}
@property(nonatomic, retain) CALayer *robotLayer;
@end
m文件
#import "PinchZoomView.h"
@implementation PinchZoomView
@synthesize robotLayer;
- (void)awakeFromNib {
self.robotLayer = [CALayer layer];
UIImage *image = [UIImage imageNamed:@"Robot.png"];
self.robotLayer.contents = (id)[image CGImage];
self.robotLayer.bounds = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
self.robotLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[self.layer addSublayer:self.robotLayer];
pinchZoom = NO;
previousDistance = 0.0f;
zoomFactor = 1.0f;
}
awakeFromNib当nib文件被加载的时候,加载器会发送一个awakeFromNib的消息到nib文件中的每个对象,每个对象都可以定义自己的 awakeFromNib方法来响应这个消息,执行一些必要的操作。也就是说通过nib文件创建view对象是执行awakeFromNib 。
robotLayer是 CALayer 对象,本例子中我们把图片对象添加到robotLayer对象中。使用 CALayer需要引入 <QuartzCore/QuartzCore.h>头文件和添加QuartzCore.framework框架。
//START:code.PinchZoomView.touchesBegan
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(event.allTouches.count == 2) {
pinchZoom = YES;
NSArray *touches = [event.allTouches allObjects];
CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self];
CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self];
previousDistance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) +
pow(pointOne.y - pointTwo.y, 2.0f));
} else {
pinchZoom = NO;
}
}
//END:code.PinchZoomView.touchesBegan
previousDistance 是获得两个点的距离。
pow是平方函数。
sqrt是开平方根函数。
//START:code.PinchZoomView.touchesMoved
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(YES == pinchZoom && event.allTouches.count == 2) {
NSArray *touches = [event.allTouches allObjects];
CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self];
CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self];
CGFloat distance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) +
pow(pointOne.y - pointTwo.y, 2.0f));
zoomFactor += (distance - previousDistance) / previousDistance;
zoomFactor = fabs(zoomFactor);
previousDistance = distance;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
}
}
//END:code.PinchZoomView.touchesMoved
//START:code.PinchZoomView.touchesEnded
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(event.allTouches.count != 2) {
pinchZoom = NO;
previousDistance = 0.0f;
}
if(event.allTouches.count == 1) {
// NSArray *touches = [event.allTouches allObjects];
// UITouch *touch = [touches objectAtIndex:0];
UITouch *touch = [touches anyObject];
NSInteger tapCount = [touch tapCount];
if (tapCount == 2) {
zoomFactor += 0.4;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
} else if (tapCount == 3) {
zoomFactor += 0.6;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
} else if (tapCount == 4) {
zoomFactor += 0.8;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
}
}
}
//END:code.PinchZoomView.touchesEnded
- (void)dealloc {
self.robotLayer = nil;
[robotLayer release];
[super dealloc];
}
14.1 Quartz概述
14.2 绘制基本几何图形
14.3 绘制图像和文本
14.4 坐标
14.5 变换
14.6 图像拾取器
14.1 Quartz概述
Quartz是Mac OS X的Darwin核心之上的绘图层,有时候也认为是CoreGraphics。共有两种部分组成Quartz:
Quartz Compositor,合成视窗系统,管理和合成幕后视窗影像来建立Mac OS X使用者接口。
Quartz 2D,是iOS和Mac OS X环境下的二维绘图引擎。
涉及内容包括:基于路径的绘图,透明度绘图,遮盖,阴影,透明层,颜色管理,防锯齿渲染,生成PDF,以及PDF元数据相关处理。
14.2 绘制基本几何图形
视图绘制
在iOS上,所有的绘制,无论是否采用OpenGL、Quartz、UIKit、或者 Core Animation—都发生在UIView对象的区域内。
视图定义绘制发生的屏幕区域。如果您使用系统提供的视图,绘制工作会自动得到处理。然而,如果您定义自己的定制视图,则必须自行提供绘制代码。
对于使用OpenGL进行绘制的应用程序,一旦建立了渲染表面,就必须使用OpenGL指定的绘制模型。
视图绘制周期
描绘系统会调用UIView对象的drawRect:方法,并向它传入一个包含需要重画的视图区域的矩形。触发视图更新的动作有如下几种:
对遮挡您的视图的其它视图进行移动或删除操作。
将视图的hidden属性声明设置为NO,使其从隐藏状态变为可见。
将视图滚出屏幕,然后再重新回到屏幕上。
显式调用视图的setNeedsDisplay或者setNeedsDisplayInRect:方法。
setNeedsDisplay是更新整个视图,
setNeedsDisplayInRect是更新视图的部分区域。
视图绘制实例FirstQuartz2D
自定义视图的h文件
@interface MyView : UIView {
}
@end
自定义视图的m文件
@implementation MyView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint (context, 75, 10);
CGContextAddLineToPoint (context, 10, 150);
CGContextAddLineToPoint (context, 160, 150);
// Closing the path connects the current point to the start of the current path.
CGContextClosePath(context);
// And stroke the path
[[UIColor blackColor] setStroke];
//CGContextStrokePath(context);
[[UIColor redColor] setFill];
CGContextDrawPath(context, kCGPathFillStroke);
//kCGPathFillStroke,kCGPathFill,kCGPathStroke
}
@end
CGContextRef context = UIGraphicsGetCurrentContext();可以获得图形上下文。
CGContextMoveToPoint、CGContextAddLineToPoint两个函数是构建描绘路径。
CGContextClosePath(context);函数是闭合描绘路径。
CGContextStrokePath函数是为闭合路径描边。
[[UIColor blackColor] setStroke]设置描边的颜色。
[[UIColor redColor] setFill]设置要填充颜色。
CGContextDrawPath(context, kCGPathFillStroke);设置描绘路径方式。常用的还有:
kCGPathFill和kCGPathStroke
图形上下文
在调用您提供的drawRect:方法之前,视图对象会自动配置其绘制环境,使您的代码可以立即进行绘制。作为这些
配置的一部分,UIView对象会为当前绘制环境创建一个图形上下文(对应于CGContextRef封装类型)。
该图形上下文包含绘制系统执行后续绘制命令所需要的信息,定义了各种基本的绘制属性,比如绘制使用的颜色、
裁剪区域、线的宽度及风格信息、字体信息、合成选项、以及几个其它信息。
绘制路径
路径用于描述由一序列线和Bézier曲线构成的2D几何形状。Core Graphics中也有一些用于创建简单路径(比如矩形和椭圆形)的便利函数。对于更为复杂的路径,必须用Core Graphics框架提供的函数自行创建。
Bézier曲线是法国数学家“贝塞尔”在工作中发现,任何一条曲线都可以通过与它相切的控制线两端的点的位置来定义。
Bézier曲线
Bézier曲线实例BezierCurve
- (void)drawRect:(CGRect)rect {
CGContextRef cgContext = UIGraphicsGetCurrentContext();
//CGContextBeginPath(cgContext);
CGContextMoveToPoint(cgContext, 333, 0);
CGContextAddCurveToPoint(cgContext, 333, 0, 332, 26, 330, 26);
CGContextAddCurveToPoint(cgContext, 330, 26, 299, 20, 299, 17);
CGContextAddLineToPoint(cgContext, 296, 17);
CGContextAddCurveToPoint(cgContext, 296, 17, 296, 19, 291, 19);
CGContextAddLineToPoint(cgContext, 250, 19);
CGContextAddCurveToPoint(cgContext, 250, 19, 241, 24, 238, 19);
CGContextAddCurveToPoint(cgContext, 236, 20, 234, 24, 227, 24);
CGContextAddCurveToPoint(cgContext, 220, 24, 217, 19, 216, 19);
CGContextAddCurveToPoint(cgContext, 214, 20, 211, 22, 207, 20);
CGContextAddCurveToPoint(cgContext, 207, 20, 187, 20, 182, 21);
CGContextAddLineToPoint(cgContext, 100, 45);
CGContextAddLineToPoint(cgContext, 97, 46);
CGContextAddCurveToPoint(cgContext, 97, 46, 86, 71, 64, 72);
CGContextAddCurveToPoint(cgContext, 42, 74, 26, 56, 23, 48);
CGContextAddLineToPoint(cgContext, 9, 47);
CGContextAddCurveToPoint(cgContext, 9, 47, 0, 31, 0, 0);
CGContextStrokePath(cgContext);
}
14.3 绘制图像和文本
UIImages的-drawRect:方法绘制图像:
- [UIImage drawAtPoint:(CGPoint)point]
- [UIImage drawInRect:(CGRect)rect]
- [UIImage drawAsPatternInRect:(CGRect)rect]
NSString的-drawRect:方法绘制文本:
- [NSString drawAtPoint:(CGPoint)point withFont:(UIFont *)font]
实例DrawImage
#import "MyView.h"
@implementation MyView
- (void)drawRect:(CGRect)rect {
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"dog" ofType:@"png"];
UIImage* myImageObj = [[UIImage alloc] initWithContentsOfFile:imagePath];
//[myImageObj drawAtPoint:CGPointMake(0, 0)];
[myImageObj drawInRect:CGRectMake(0, 0, 320, 480)];
NSString *s = @"我的小狗";
[s drawAtPoint:CGPointMake(100, 0) withFont:[UIFont systemFontOfSize:34.0]];
}
@end
写字实例Draw
Dot对象
//--h
@interface Dot : NSObject {
CGFloat x;
CGFloat y;
}
@property(assign) CGFloat x;
@property(assign) CGFloat y;
@end
//--m
#import "Dot.h"
@implementation Dot
@synthesize x;
@synthesize y;
@end
DrawView.h
#import <UIKit/UIKit.h>
@interface DrawView : UIView {
NSMutableArray *dots;
}
@property(nonatomic, retain) NSMutableArray *dots;
@end
DrawView.m
#import "DrawView.h"
#import "Dot.h"
@implementation DrawView
@synthesize dots;
- (NSMutableArray *)dots {
if(nil == dots) {
self.dots = [NSMutableArray array];
}
return dots;
}
//START:code.DrawView.drawRect
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [[UIColor blueColor] CGColor]);
for(Dot *dot in self.dots) {
CGContextAddArc(ctx, dot.x, dot.y, 5.0f, 0.0f, 2.0f * M_PI, YES);
CGContextFillPath(ctx);
}
}
//END:code.DrawView.drawRect
- (void)dealloc {
self.dots = nil;
[super dealloc];
}
drawRect方法中将dots集合中的Dot对象取出,一个一个在屏幕上面画出来。
CGContextAddArc(ctx, dot.x, dot.y, 5.0f, 0.0f, 2.0f * M_PI, YES);函数是绘制弧形。
CGContextFillPath(ctx);填充路径。
//START:code.DrawView.touchesBegan
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
Dot *dot = [[[Dot alloc] init] autorelease];
dot.x = location.x;
dot.y = location.y;
//[self.dots removeAllObjects];
[self.dots addObject:dot];
[self setNeedsDisplay];
}
//END:code.DrawView.touchesBegan
//START:code.DrawView.touchesMoved
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
Dot *dot = [[[Dot alloc] init] autorelease];
dot.x = location.x;
dot.y = location.y;
[self.dots addObject:dot];
[self setNeedsDisplay];
}
//END:code.DrawView.touchesMoved
14.4 坐标
Quartz坐标
Quartz技术最开始为Mac OS X系统设计的图形技术,它的坐标原点位于左下角。
UIKit坐标
UIKit坐标与Quartz不同,原点位于右上角。在iOS中的UIView等控件都是基于此坐标,由于在UIView使用了Quartz坐标有时候需要转换。
坐标变换实例
MyView.m文件
#import "MyView.h"
@implementation MyView
- (void)drawRect:(CGRect)rect {
NSString *path = [[NSBundle mainBundle] pathForResource:@"cat" ofType:@"jpg"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
CGImageRef image = img.CGImage;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGRect touchRect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, touchRect, image);
CGContextRestoreGState(context);
}
@end
CGContextSaveGState是将当前图形状态要入到图形堆栈。
CGContextDrawImage(context, touchRect, image)在上下文中绘制图形。 CGContextRestoreGState回复当前图形状态。
14.5 变换
运用变换
变换(transformation)修改了图形上下文中绘制图形的方式。可以通过移动、旋转或缩放实现变换。
Quartz提供了多种形式的变换,其中主要:CTM(当前变换矩阵)变换和仿射(affine)变换。
CTM(current transformation matrix)变换,这种变换比较简单,函数有:
CGContextRotateCTM,旋转坐标
CGContextScaleCTM,缩放坐标
CGContextTranslateCTM,移动原点
移动变换
CGContextTranslateCTM (myContext, 100, 50)
从对象角度沿着x轴正向移动100单位,沿着y轴正向移动50单位。
旋转变换
static inline double radians (double degrees) {return degrees * M_PI/180;}
CGContextRotateCTM (myContext, radians(–45.));
从对象角度:
在Quartz坐标下正数为逆时针旋转,负数为顺时针旋转。
在UIKit坐标下正数为顺时针旋转,负数为逆时针旋转。
缩放变换
CGContextScaleCTM (myContext, .5, .75);
从对象角度:所有x坐标缩小0.5,所有y坐标缩小0.75。
修改MyView.m文件
#import "MyView.h"
@implementation MyView
- (void)drawRect:(CGRect)rect {
NSString *path = [[NSBundle mainBundle] pathForResource:@"cat" ofType:@"jpg"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
CGImageRef image = img.CGImage;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -img.size.width, -img.size.height);
CGRect touchRect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, touchRect, image);
CGContextRestoreGState(context);
}
@end
仿射(affine)变换
仿射(affine)变换也是一种直角坐标变换,重
用变换,经过多次变换(多次的矩阵相乘),
每一种变换都可以用矩阵表示,通过多次矩阵
相乘得到最后结果。仿射变换函数:
CGAffineMakeRotation,创建旋转矩阵仿射对象
CGAffineMakeScale,创建缩放矩阵仿射对象
CGAffineMakeTranslation,创建移动矩阵仿射对象
CGAffineTransformRotate,旋转矩阵仿射对象
CGAffineTransformScale,缩放矩阵仿射对象
CGAffineTransformTranslate,移动矩阵仿射对象
CGContextConcatCTM,连接到CTM变换
使用仿射变换MyView.m
#import "MyView.h"
@implementation MyView
- (void)drawRect:(CGRect)rect {
NSString *path = [[NSBundle mainBundle] pathForResource:@"cat" ofType:@"jpg"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
CGImageRef image = img.CGImage;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGAffineTransform myAffine = CGAffineTransformMakeRotation(M_PI);
myAffine = CGAffineTransformTranslate(myAffine, -img.size.width, -img.size.height);
CGContextConcatCTM(context, myAffine);
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -img.size.width, -img.size.height);
CGRect touchRect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, touchRect, image);
CGContextRestoreGState(context);
}
@end
14.6 图像拾取器
图像拾取器(Image Picker)是可以帮助你从图片库中选取图片,也可以捕获照相机图片。
PhotoViewViewController.h
//START:code.PhotoViewController.h
@interface PhotoViewViewController : UIViewController
<UIImagePickerControllerDelegate> {
UIImageView *imageView;
UIImagePickerController *imagePicker;
}
@property(nonatomic, retain) IBOutlet UIImageView *imageView;
@property(nonatomic, retain) IBOutlet UIImagePickerController *imagePicker;
@end
//END:code.PhotoViewController.h
需要实现UIImagePickerControllerDelegate协议。
需要定义UIImagePickerController控制器成员变量。
PhotoViewViewController.m
#import "PhotoViewViewController.h"
@implementation PhotoViewViewController
@synthesize imageView;
@synthesize imagePicker;
//START:code.PhotoViewController.touchesEnded:withEvent:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if([[touches anyObject] tapCount] > 1) {
// bring up image grabber
if([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera]) {
self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
//self.imagePicker.allowsImageEditing = YES; //iOS 3之前
self.imagePicker.allowsEditing = YES;
[self presentModalViewController:self.imagePicker animated:YES];
}
}
//END:code.PhotoViewController.touchesEnded:withEvent:
图像选取器的sourceType属性有:
UIImagePickerControllerSourceTypePhotoLibrary,图片来源于“相簿”
UIImagePickerControllerSourceTypeCamera,来源于相机
UIImagePickerControllerSourceTypeSavedPhotosAlbum,来源于“相机胶卷”。
PhotoViewViewController.m
//START:code.PhotoViewController.didFinish
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info {
imageView.image = [info objectForKey:UIImagePickerControllerEditedImage];
[self dismissModalViewControllerAnimated:YES];
}
//END:code.PhotoViewController.didFinish
//START:code.PhotoViewController.didCancel
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self.imagePicker dismissModalViewControllerAnimated:YES];
}
//END:code.PhotoViewController.didCancel
- (void)dealloc {
self.imageView = nil;
self.imagePicker = nil;
[super dealloc];
}
imagePickerController:didFinishPickingMediaWithInfo:委托实现方法,当选择完成时候调用。
imageView.image = [info objectForKey:UIImagePickerControllerEditedImage];语句可以从图片拾取器中获得一个Image对象。
imagePickerControllerDidCancel:是委托实现方法当点击取消时候调用。
IOS之动画
15.1 动画介绍
15.2 Core Animation基础
15.3 隐式动画
15.4 显式动画
15.5 关键帧显式动画
15.6 UIView级别动画
15.1 动画介绍
在iOS中动画实现技术主要是:Core Animation。 Core Animation负责所有的滚动、旋转、缩小和放大以及所有的iOS动画效果。其中UIKit类通常都有animated:参数部分,它可以允许是否使用动画。
Core Animation还与Quartz紧密结合在一起,每个UIView都关联到一个CALayer对象,CALayer是Core Animation中的图层。
15.2 Core Animation基础
Core Animation创建动画时候会修改CALayer属性,然后让这些属性流畅地变化。Core Animation相关知识点:
图层,图层是动画发生的地方,CALayer总是与UIView关联,通过layer属性访问。
隐式动画,这是一种最简单的动画,不用设置定时器,不用考虑线程或者重画。
显式动画,是一种使用CABasicAnimation创建的动画,通过CABasicAnimation,可以更明确地定义属性如何改变动 画。
关键帧动画,这是一种更复杂的显式动画类型,这里可以定义动画的起点和终点,还可以定义某些帧之间的动画。
15.3 隐式动画
实例ianimate:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
@interface ianimateViewController : UIViewController {
IBOutlet UIImageView *plane;
}
-(IBAction)movePlane:(id)sender;
@end
//--m
-(IBAction)movePlane:(id)sender {
[UIView beginAnimations:nil context:NULL];
CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(180, 200);
plane.layer.affineTransform=moveTransform;
plane.layer.opacity = 1;
[UIView commitAnimations];
}
飞机图片的不透明度(opacity)初始为0.25,然后在动画过程中不透明度设置为1.0。这个过程是设置飞机图片对象的层属性的,plane.layer.opacity = 1;
[plane.layer setAffineTransform:moveTransform];设置飞机图片层对象的仿射移动变换。
15.4 显式动画
eanimate关键代码:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
@interface eanimateViewController : UIViewController {
IBOutlet UIImageView *plane;
}
-(IBAction)movePlane:(id)sender;
@end
//--m
-(IBAction)movePlane:(id)sender {
CABasicAnimation *opAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
opAnim.duration = 3.0;
opAnim.fromValue = [NSNumber numberWithFloat:.25];
opAnim.toValue= [NSNumber numberWithFloat:1.0];
opAnim.cumulative = YES;
opAnim.repeatCount = 2;
[plane.layer addAnimation:opAnim forKey:@"animateOpacity"];
CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(180, 200);
CABasicAnimation *moveAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
moveAnim.duration = 6.0;
moveAnim.toValue= [NSValue valueWithCATransform3D:
CATransform3DMakeAffineTransform(moveTransform)];
[plane.layer addAnimation:moveAnim forKey:@"animateTransform"];
}
显式动画时候,不必定义CALayer的变化,也不必执行它们,而是通过CABasicAnimation逐个定义动画。其中每个动画都含有各自的duration、repeatCount等属性。然后,使用addAnimation:forKey:方法分别将每个动画应用到层中。
[CABasicAnimation animationWithKeyPath:@“opacity”]获得透明度动画对象,@“transform”是指定转换动画。
opAnim.cumulative 属性是指定累计
opAnim.repeatCount 重复执行次数
CATransform3DMakeAffineTransform函数是将仿射变换矩阵变成Core Animation使用的Transform3D类型的矩阵。
15.5 关键帧显式动画
eanimate_keyFrame关键代码:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
@interface eanimateViewController : UIViewController {
IBOutlet UIImageView *plane;
}
-(IBAction)movePlane:(id)sender;
@end
//-m
-(IBAction)movePlane:(id)sender {
CAKeyframeAnimation *opAnim = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
opAnim.duration = 6.0;
opAnim.values =[NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.25],
[NSNumber numberWithFloat:0.75],
[NSNumber numberWithFloat:1.0],
nil];
opAnim.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.5],
[NSNumber numberWithFloat:1.0], nil];
[plane.layer addAnimation:opAnim forKey:@"animateOpacity"];
CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(180, 200);
CABasicAnimation *moveAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
moveAnim.duration = 6.0;
moveAnim.toValue= [NSValue valueWithCATransform3D:
CATransform3DMakeAffineTransform(moveTransform)];
[plane.layer addAnimation:moveAnim forKey:@"animateTransform"];
}
animation.values是一个值的数组。
animation.keyTimes是一个每个帧片段持续的时间比例,取值范围0.0-1.0之间。
关键帧之路径实例BallSteps
#import <UIKit/UIKit.h>
#import <QuartzCore/CoreAnimation.h>
@interface BallStepsViewController : UIViewController {
UIButton *drawButton;
UIImageView *imageView;
}
@property (nonatomic, retain) IBOutlet UIButton *drawButton;
@property (nonatomic, retain) IBOutlet UIImageView *imageView;
- (IBAction)drawStar:(id)sender;
@end
//--m
#import "BallStepsViewController.h"
@implementation BallStepsViewController
@synthesize drawButton;
@synthesize imageView;
- (IBAction)drawStar:(id)sender {
[drawButton setEnabled:NO];
CGMutablePathRef starPath = CGPathCreateMutable();
CGPathMoveToPoint(starPath,NULL,160.0f, 100.0f);
CGPathAddLineToPoint(starPath, NULL, 100.0f, 280.0f);
CGPathAddLineToPoint(starPath, NULL, 260.0, 170.0);
CGPathAddLineToPoint(starPath, NULL, 60.0, 170.0);
CGPathAddLineToPoint(starPath, NULL, 220.0, 280.0);
CGPathCloseSubpath(starPath);
CAKeyframeAnimation *animation = nil;
animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
[animation setDuration:10.0f];
[animation setDelegate:self];
[animation setPath:starPath];
CFRelease(starPath);
starPath = nil;
[[imageView layer] addAnimation:animation forKey:@"position"];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
[drawButton setEnabled:YES];
}
- (void)dealloc {
[drawButton release];
[imageView release];
[super dealloc];
}
@end
[CAKeyframeAnimation animationWithKeyPath:@“position”];创建一个position类型的关键帧动画。
关键帧动画中可以定义路径,把这些路径放入到CGMutablePathRef 中与CG中的很相似。
CGPathCloseSubpath(starPath);结束路径。
[animation setPath:starPath];设置路径。
[animation setDelegate:self];设置委托对象为本身,即回调方法 animationDidStop:finished:。
最后CFRelease(starPath);释放路径。
15.6 UIView级别动画
除了直接使用Core Animation 层实现动画,我们还有UIView直接实现隐式动画。
实例ViewAnimation:
@interface MainViewController : UIViewController
{
UIImageView *animImageView;
UIButton *button;
}
@property (assign) IBOutlet UIImageView *animImageView;
@property (assign) IBOutlet UIButton *button;
- (IBAction)action:(id)sender;
@end
//--m
#import "MainViewController.h"
@implementation MainViewController
@synthesize animImageView;
@synthesize button;
- (IBAction)action:(id)sender {
[UIView beginAnimations:@"Hide Button" context:nil];
[[self button] setAlpha:0.0];
[UIView commitAnimations];
[UIView beginAnimations:@"Slide Around" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(viewAnimationDone:)];
[UIView setAnimationRepeatCount:3];
[UIView setAnimationRepeatAutoreverses:YES];
CGPoint center = [[self animImageView] center];
center.y += 100;
[[self animImageView] setCenter:center];
[UIView commitAnimations];
}
- (void)viewAnimationDone:(NSString*)name {
[UIView beginAnimations:@"Show Button" context:nil];
[[self button] setAlpha:1.0];
[UIView commitAnimations];
}
@end
UIView中的动画是在动画块中定义的,动画块是UIView beginAnimations:context:开始, 在UIView commitAnimations结束。
首先开始将按钮设置透明度为0的,结果是开始动画时候隐藏了。
然后,又开始新的动画中设置委托事件:
[UIView setAnimationDelegate:self]
[UIView setAnimationDidStopSelector:@selector(viewAnimationDone:)];
当动画结束的时候调用viewAnimationDone:方法。
内置UIView动画
UIView具有一个UIViewAnimationTransition属性可以设定动画,这些动画是iOS提供几个常用动画有:
UIViewAnimationTransitionNone
UIViewAnimationTransitionFlipFromLeft
UIViewAnimationTransitionFlipFromRight
UIViewAnimationTransitionCurlUp
UIViewAnimationTransitionCurlDown
实例UIViewAnimation
#import <UIKit/UIKit.h>
@interface UIViewAnimationViewController : UIViewController {
}
- (IBAction)doUIViewAnimation:(id)sender;
@end
//-m
#import "UIViewAnimationViewController.h"
@implementation UIViewAnimationViewController
- (IBAction)doUIViewAnimation:(id)sender{
[UIView beginAnimations:@"animationID" context:nil];
[UIView setAnimationDuration:1.5f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationRepeatAutoreverses:NO];
UIButton *theButton = (UIButton *)sender;
switch (theButton.tag) {
case 1:
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];//oglFlip, fromLeft
break;
case 2:
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES];//oglFlip, fromRight
break;
case 3:
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
break;
case 4:
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.view cache:YES];
break;
default:
break;
}
//[self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:0];
[UIView commitAnimations];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
@end
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]设置动画曲线,动画曲线指定的是动画进入和退出的方式,它也有几个常量:
UIViewAnimationCurveEaseInOut
UIViewAnimationCurveEaseIn
UIViewAnimationCurveEaseOut
UIViewAnimationCurveLinear
setAnimationTransition: forView: cache:方法第一个参数定义动画类型,第二个参数是当前视图对象,第三个参数上使用缓冲区。
原文地址:http://www.cnblogs.com/syxchina/archive/2012/10/20/2732731.html