Iphone开发基础教程》第九章 导航控制器和表视图(2009-08-11 12:06:17)

这一章比较庞大,里面的例子也比较多,我看书和实践一共花了我一周的时间,然后自己又摸索构建一个View实现了一点功能,到今天才写这一章的东西,呵呵,晚了点了!

UINavigationController是用于构建分层应用程序的主要工具,它在管理以及换入和换出多个内容视图方面与UITabBarController较为相似,但是主要不同之处在于前者是作为栈来实现的,这让它非常实用处理分层数据。

创建Window-Based Application,我创建的项目名称是IP_09Nav。创建一个新类,UIViewController subclass命名为RootViewController,该类将包含导航控制器的根视图的控制器类。

设置导航控制器。打开IP_09NavAppDelegate.h添加一个输出口。

IBOutlet UINavigationController *navController;//声明一个Navigation输出口

在IP_09NavAppDelegate.m中添加子视图:

[window addSubview:navController.view];//添加Navigation导航模板到window

接下来打开MainWindow.xib,向里面拖入一个Navigation Controller,要拖入nib窗口而不是view窗口。按下control键,并从IP_09Nav App Delegate图标拖入到新的Navigation Controller图标选中navController输出口。下面要将窗口工具栏中间的View Model按钮将nib主窗口改为列表模式。点Navigation Controller前面的小箭头,找到View Controller,然后Apple+4将基类修改为RootViewControoler。

接下来我们要创建一个类让其继承UITableViewController,这样我们做的目的是我们在继承的子类中添加一个UIImage的属性,并且让所有的Nav继承此类,这样所有的Nav也就有了UIImage这样一个属性了。

创建一个新类SecondLViewController。让其继承UITableViewController,给一个UIImage属性。这样把该项目下自己创建的类都让其继承该类。

在RootViewController.m中添加代码。

#import "SecondLevelViewController.h"//导入该类是为了使用rowImage属性
#import "IP_09NavAppDelegate.h"//导入该类是为了使用应用程序委托

-(void)viewDidLoad
{
 self.title = @"Root L";//设置该控制器的标题
 NSMutableArray *array = [[NSMutableArray alloc]init];//创建一个可变数组,用来保存每一个二级视图 
 self.controllers = array;
 [array release];
 [super viewDidLoad];
}
#pragma mark Table Date source methods
//返回数组的计数,也就是在根视图下面一共有多少二级视图
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 return [self.controllers count];
}

//返回单元格
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 static NSString *RootViewControllerCell = @"RootViewControllerCell";
 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RootViewControllerCell];
 if(cell == nil)
 {
  cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:RootViewControllerCell]autorelease];
 }
 NSUInteger row = [indexPath row];
 SecondLevelViewController *controller = [controllers objectAtIndex:row];
 cell.text = controller.title;
 cell.image = controller.rowImage;
 return cell;
}

#pragma mark table View Delegate Methods
//用来显示二级视图后面的箭头
-(UITableViewCellAccessoryType)tableView:(UITableView *)tableView
  accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
 return UITableViewCellAccessoryDisclosureIndicator;
}

-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
//用户单击每行时调用的方法
{
 NSUInteger row = [indexPath row];//获取单击时的行
 SecondLevelViewController *nextController = [self.controllers objectAtIndex:row];//从对应的行的数组中获取正确的控制器
 IP_09NavAppDelegate *delegate = [[UIApplication sharedApplication] delegate];//用应用程序委托来维护导航控制器,使用共享的UIApplication实例获取到该委托的引用
 [delegate.navController pushViewController:nextController animated:YES];//使用委托的navController输出口将下一个控制器放到导航控制器中
}
这样弄好以后build and go就可以显示一个空的试图了。

下面添加第一个自控制器:展示按钮试图

创建两个类DisclosureButtonController和DisclosureDetailController前一个类是为了显示二级试图里面的项,后一个主要是和xib文件关联显示详细信息。

打开DisclosureDetailController.h添加两个变量:

IBOutlet UILabel *label;//用户显示的label
 NSString *message;
在DisclosureDetailController.m中添加viewWillAppear方法。

//此处用viewillAppear方法,而不用viewDidLoad方法是因为viewDidLoad方法只在第一次加载其视图的时候调用。用该方法在每次单击按钮的时候都会被调用
-(void)viewWillAppear:(BOOL)animated
{
 label.text = message;
 [super viewWillAppear:animated];
}

然后创建一个xib文件,名称为DisclosureDetail.xib然后拖入一个UILabel,调整好大小,让其和DisclosureDetailController类相关联,并且连接好输出口。

下面修改DisclosureButtonController.h。

#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@class DisclosureDetailController;

@interface DisclosureButtonController : SecondLevelViewController
<UITableViewDelegate,UITableViewDataSource>
{
 NSArray *list;
 DisclosureDetailController *childCntroller;
}
@property (nonatomic, retain) NSArray *list;
@property (nonatomic, retain) DisclosureDetailController *childCntroller;
@end

接下来修改DisclosureButtonController.m

#import "IP_09NavAppDelegate.h"
#import "DisclosureDetailController.h"

//初始化一个数据源
-(void)viewDidLoad
{
 NSArray *array = [[NSArray alloc]initWithObjects:@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",nil];
 self.list = array;
 [array release];
 [super viewDidLoad];
}

#pragma mark Table dataSource methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 return [list count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 static NSString *s = @"DisclosureButtonCellIdentifier";
 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:s];
 if(cell == nil)
 {
  cell = [[[UITableViewCell alloc]initWithFrame:CGRectZero reuseIdentifier:s]autorelease];
 }
 NSInteger row = [indexPath row];
 NSString *rowString = [list objectAtIndex:row];
 cell.text = rowString;
 [rowString release];
 return cell;
}

#pragma mark Table Delegate methods  委托方法
//返回带展示按钮的行
-(UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
 return UITableViewCellAccessoryDetailDisclosureButton;
}

//选中行时调用,给出提示,而不是选中按钮给出提示
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
 NSInteger row = [indexPath row];
 NSString *message = [self.list objectAtIndex:row];
 UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Show" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
 [alert show];
 [alert release];
 [message release];
}

//点击后面的按钮展示下一级菜单
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
 if(childCntroller == nil)
 {
  childCntroller = [[DisclosureDetailController alloc]initWithNibName:@"DisclosureDetail" bundle:nil];
 } 
  childCntroller.title = @"你好!";
  NSUInteger row = [indexPath row];//得到上级的行
  
  NSString *selectedMovid = [list objectAtIndex:row];//得到上级的值
  NSString *detailMessage = [[NSString alloc] initWithFormat:@"you pressed the disclosure button for %@", selectedMovid];
  childCntroller.message = detailMessage;
  childCntroller.title = selectedMovid;
  [detailMessage release];
     IP_09NavAppDelegate *delegate = [[UIApplication sharedApplication]delegate];
  [delegate.navController pushViewController:childCntroller animated:YES];
}
最后在RootViewController中把

#import "DisclosureButtonController.h"

DisclosureButtonController *dbutton = [[DisclosureButtonController alloc]initWithStyle:UITableViewStylePlain];
 dbutton.title = @"Disclosure button";
 dbutton.rowImage = [UIImage imageNamed:@"disclosureButtonControllerIcon.png"];
 [array addObject:dbutton];
 [dbutton release];

运行一下看看。

第二个子控制器:校验表(也就是允许用户从列表中选择一个项目)

创建一个新类CheckListController,继承自SecondLevelViewController。限于篇幅有限,代码我就不在这上面添加了,具体代码可以去我CSDN上面下载,代码中有注释。

代码填写好后在RootViewController里面添加一个二级试图,方法和第一个相似。

然后运行。

第三个控制器:表行上的控件

创建一个新类RowControllersController,继承SecondLevelViewController。具体代码下载看吧。

这几个控制器和第一个差不多,步骤都大同小异,想必自己摸索一下就好了。

第四个控制器:可移动的行

这个演示主要是让其行在编辑模式下面可以随心所欲的移动。也是代码操作,步骤入第一个。

第五个控制器:可删除的行(同第四个)

第六个控制器:可便捷的详细窗格

创建数据模型对象。创建一个新类President这个选择模板为NSObject subclass,然后添加代码。导入Presidents.plist文件。

创建控制器。创建两个类PresidentsViewController和PresidentDetailController。具体代码可以去下载,太多了。

最后这个比较麻烦,一定要信息,上面的方法要一点一点搞明白。我最后就是把这个演示用xib实现了一下,用的plist文件也是自己创建的,也可以下载,但是还是有一点问题,有时候会推出,现在还在研究阶段。

最后上几个关于本章的几个图片:

《Iphone开发基础教程》第十章 应用程序设置和用户默认设置

这一章主要介绍的是如何向Iphone的Settings应用程序中添加自己的应用设置,以及如何从应用程序内部访问这些设置。

用户默认设置是应用程序首选项的一部分,由NSUserDefaults类实现,用户保存和获取首选项。与NSDictonary获取数据一样,实用键值读取和保存首选项数据。不同之处是NSUserDefaults数据被持久化到文件系统中,而没有存储在内存中的对象实例中。

Settings应用程序的优势之一是无需为首选项设计用户界面。创建属性列表来定义应用程序的可用设置后,Settings应用程序会自动创建用户界面。但是使用Settings应用程序也有一些限制,当应用程序正在运行时,用户可能需要更改的任何首选项都不应该受到Settings应用程序的限制,因为用户可能被强制退出应用程序以更改这些值。互动式应用程序,通常应该提供首选项视图,使用户更改设置时无需退出应用程序。

下面开始这一章的练习。

创建新的项目,这次使用的是Utility Application模板。我创建的项目名称是:IP_10Seting

这个模板从来没有使用过,首先先来熟悉一下该模板。该模板创建出来的应用程序有一个主视图和一个辅助试图,点击主视图中的信息按钮会进入辅助试图,点击辅助试图中的Done按钮会返回主视图。

在这个项目中,你会发现没有Classes文件夹,因为事先这种类型的应用程序需要许多文件,该模板已经将这些文件组织到一些分组中,展开文件夹Main View、Flipside View和Application Controllers,然后将Resources也打开。然后就会看到在每一个文件夹下面会自动对应几个类文件。组成主视图的所有类,包括试图控制器和一个UIView子类,都包含在Main View文件夹中。事先flipside试图所需要的源代码文件都包含在Flipside View文件夹中,最后应用程序委托和控制器类都包含在Application Controllers文件夹中。

打开MainWindow.xib,将主视图更改为列表模式,点击Root View Controller,View,light info button并在此上apple+4,在type中选择info dark这样主视图中的信息按钮就会变成黑色,这样在白色背景下面就会有好的显示效果。

更改info.plist文件,添加图标。

在根目录上面(就是点IP_10Seting)创建新项目,选择Settings Bundle,创建一个新的Bundle(用默认的名字)。

创建属性列表时要用固定的模式,在创建的Bundle中已经存在了一个Root.plist文件,我们对这个文件进行修改就好了。

打开Root.plist文件,在最上面的Title里修改你Setting的名字。SettingsTable先不用管。在PreferenceSpecifiers项下,有许都的项目,删除2,3,4项。然后展开1,修改Title,这个Title会显示在Settings里面。然后在Item1下面在添加一项,默认是string类型,修改为Dictionary,然后向里面添加5项。Type/PSTextFieldSpecifier,Title/Username,Key/username,AutocapitalizetionType/None,AutocorrectionType/No。PSTextFieldSpecifier说明我们希望在该文本字段中编辑此设置。AutocapitalizetionType指的是该文本字段不要尝试自动大写用户输入的内容。AutocorrectionType这个是告诉Settings不需要自动更正输入到该文本中的值。

到此都可以运行一下看看结果了。

下面添加安全字段设置。

添加一新项,简单的方法就是直接复制item 2然后粘贴就好了,修改item 3中的项。

Type/PSTextFieldSpecifier,Title/Pasword,Key/pasword,AutocapitalizetionType/None,AutocorrectionType/No,IsSecure/Boolean类选中,这样输入的密码就要被点所代替。

添加多值字段。还利用上面的方法,复制一个。

Type/PSMultiValueSpecifier,Title/Protocol,Key/protocol,添加两个Array类型的值。Titles和Values,其中Titles是显示在Settings中的值,而values是显示的值对应的一个值。DefaultValue/1,这个是启动的时候默认的值。

添加开关设置。继续复制、粘贴、修改。

Type/PSToggleSwitchSpecifier,Title/Warp Drive,Key/warp,Truue/Engaged,Falsue/Disabled,DefaultValue/Engaged。

添加滑块设置。在添加滑块设置前,让我们再创建一个分组。很简单就是复制item 1,然后粘贴就好了。(这个例子中的粘贴都是选中最后行然后粘贴,让粘贴的行成为最后的行)

修改键值。

Type/PSSliderSpecifier,Key/warpfactor,DefaultValue/5,MinimumValue/0,MaximumValue/10,MinimumValueImage/rabbit.png,MaximumValueImage/turtle.png,从前到后以此为类型,key值,默认值,最小值,最多值,slider左边显示的图标,slider右边显示的图标。

再添加一个分组,然后添加子视图设置。

所谓的添加子视图,其实就是再添加一个plist文件罢了。在复制,粘贴一行,修改键值。

Type/PSChildPaneSpecifier,Title/More Settings,File/More这个就是新的plist文件。

创建新的plist文件也有一个诀窍,我们可以复制一个Root.plist文件,粘贴然后修改名字,修改里面的项就Ok了。但是这里的复制和粘贴不能在Xcode里面完成,我们必须找到项目所在的文件夹,找到Setting.bundle然后点右键,让其显示文件夹里面的内容,在这里面进行复制粘贴操作才可。

到此该bundle创建完成,下面的就是我们怎么读出来了。

读取应用程序设置。

读取操作主要是应用NSUserDefaults类进行。

首先打开MainViewController.h。添加常量和变量。

//这里面定义的常量对应Setting.bundle里面Root.plist里面每一项的key值
#define kUsernameKey @"username"
#define kSwitch @"warp"
#define kSlider @"warpfactor"
#define kPasword @"pasword"
#define kProtocol @"protocol"

#define kSecondname @"secondname"
#define kUseraddress @"useraddress"
#define kUserBirth @"userbirth"
#define kUserPhone @"userphone"
#define kSecondProtocol @"secondprotocol"

IBOutlet UILabel *usernameLabel;
 IBOutlet UILabel *paswordLabel;
 IBOutlet UILabel *protocolLabel;
 IBOutlet UILabel *secondname;
 IBOutlet UILabel *useraddress;
 IBOutlet UILabel *userbirth;
 IBOutlet UILabel *userphone;
 IBOutlet UILabel *secondprotocol;
 IBOutlet UILabel *switchStats;
 IBOutlet UILabel *sliderValue;

然后打开MainViewController.m添加三个方法。

//这个新的方法仅用于抓取标准用户默认设置,并使用我们输入到属性文件中的键值,将所有标签的文本属性设置为用户默认设置中的适当对象
-(void)refreshFields
{
 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 usernameLabel.text = [defaults objectForKey:kUsernameKey];
 paswordLabel.text = [defaults objectForKey:kPasword];
 protocolLabel.text = [defaults objectForKey:kProtocol];
 
 secondname.text = [defaults objectForKey:kSecondname];
 useraddress.text = [defaults objectForKey:kUseraddress];
 userbirth.text = [defaults objectForKey:kUserBirth];
 userphone.text = [defaults objectForKey:kUserPhone];
 secondprotocol.text = [defaults objectForKey:kSecondProtocol];
 
 switchStats.text = [defaults objectForKey:kSwitch];
 sliderValue.text = [[defaults objectForKey:kSlider]stringValue];
}

//在viewDidLoad和viewDidAppear中调用refreshfields方法,使得在视图载入时显示的字段将设置为合适的首选项值,然后在视图更新
//首选项时,显示的字段将被刷新
-(void)viewDidAppear:(BOOL)animated
{
 [self refreshFields];
 [super viewDidAppear:animated];
}

- (void)viewDidLoad {
 [self refreshFields];
}

当然了,需要打开MainView.xib,向里面拖入适当的UILabel并且和刚才定义的变量进行关联。

这样主视图的读取已经结束。

现在添加辅助视图,在辅助试图我们添加一个switch和一个slider用来读取应用程序设置中的值。

打开FlipsideViewController.h,添加两个输出口。

IBOutlet UISwitch *switchshow;
IBOutlet UISlider *slidershow;

打开FlipsideViewController.m,首先引入MainViewController。

#import "MainViewController.h"

添加两个方法。

- (void)viewDidLoad
{
 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 switchshow.on = ([[defaults objectForKey:kSwitch]isEqualToString:@"Engaged"])?YES:NO;
 slidershow.value = [defaults floatForKey:kSlider];
}

//重写viewWillDisappear方法,以便在主视图再次显示之前,将控件的值填充到用户默认设置中。
- (void)viewWillDisappear:(BOOL)animated
{
 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 NSString *prefValue = (switchshow.on)? @"Engaged": @"Disabled";
 [defaults setObject:prefValue forKey:kSwitch];
 [defaults setFloat:slidershow.value forKey:kSlider];
 [super viewWillDisappear:animated];
}

好了,释放资源,build and go运行吧,看看结果。

到此结束,不是很难,这一章就半天就可以搞定。

源代码在我的csdn上面有,需要的可以去下载,下载地址http://hanyegudeng.download.csdn.net/

《Iphone开发基础教程》第十一章 基本数据持久

以前我们也用到过读取数据,但是没有一个应用程序是将其数据永久性存储,也就是在应用重启,机器重启后数据不会丢失,和上一次最后的数据一致。这章一共定义了三种保持数据的方法,第一种:实用属性列表,第二种:对象归档,第三种:使用Iphone的嵌入式数据库(SQLite3)

给予Iphone应用程序沙盒原理,我们保持的数据都是保存在相对应的应用程序的Document文件夹。既然我们把数据放在每一个应用的Document文件夹中,呢我我们怎么得到相应的路径呢,其实也不是很难。下面是检索文档目录路径的代码:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserdomainMask, YES);

NSString *documentDirectory = [paths objectAtIndex:0];常量NSDocumentDirectory表面我们正在查找的Document目录路径,常量NSUserDomainmask表明我们希望将搜索限制于我们应用程序的沙盒中。这样我们就可以得到该数组的第一值,也仅此一值,因为每一个应用程序只有一个Document文件夹。我们得到了Document的路径,然后和文件名相连接不就是一个完整的路径了吗,这用到了stringByAppendingPathComponent方法:

NSString *filepath = [documentDirectory stringbyAppendingPathComponent:@"filename.xxx"];其中filename.xxx为要命名的文件。

在每一个应用程序中还对应一个temp文件夹,我们怎么获取这个文件夹的路径呢,也比较简单:

NSString *tempPath = NSTemporaryDirectory();

NSString *filePath = [tempPath stringByAppendingPathComponent:@"filename.xxx"];

下面我们来创建该项目:

第一个实用属性列表文件,打开Xcode,创建新项目,选择View-Based Application即可,我创建项目名称是IP_11persistence。打开IP_11persistenceViewController.h,首先定义一个常量用来串联Document。

#define kFileName @"data.plist"

然后定义四个输出口,用来输入和显示数据。

IBOutlet UITextField *show1;
 IBOutlet UITextField *show2;
 IBOutlet UITextField *show3;
 IBOutlet UITextField *show4;

定义两个方法:

-(NSString *)dataFilePath;//用来返回数据文件的完整路径名
-(void)applicationWillTerminate:(NSNotification *)notification;//应用程序在退出时调用,将数据保存到数据列表

然后打开IP_11persistenceViewController.m

添加下面这三个方法:

-(NSString *)dataFilePath//用来返回数据文件的完整路径名
{
 NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 NSString *paths = [path objectAtIndex:0];
 return [paths stringByAppendingPathComponent:kFileName];


}

-(void)applicationWillTerminate:(NSNotification *)notification//应用程序在退出时调用,将数据保存到数据列表
{
 NSMutableArray *array = [[NSMutableArray alloc]init];
 [array addObject:show1.text];
 [array addObject:show2.text];
 [array addObject:show3.text];
 [array addObject:show4.text];
 [array writeToFile:[self dataFilePath] atomically:YES];
 [array release];
}

-(void)viewDidLoad
{
 NSString *filePath = [self dataFilePath];//得到文件路径
 if([[NSFileManager defaultManager] fileExistsAtPath:filePath])//如果该文件存在就加载
 {
  NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
  show1.text = [array objectAtIndex:0];
  show2.text = [array objectAtIndex:1];
  show3.text = [array objectAtIndex:2];
  show4.text = [array objectAtIndex:3];
  [array release];
 }


 UIApplication *app = [UIApplication sharedApplication];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:)
             name:UIApplicationWillTerminateNotification
              object:app];
 [super viewDidLoad];
}

代码已经搞定,下面点开Resources文件夹,打开IP_11persistenceViewController.xib,向里面拖入4个UITextField控件,勾掉每一个控件上的Clear When Editing Begin,这样就不是每次选中文本框的时候会自动消失里面的内容了。关联四个控件即可。(在实际的例子当中我添加两个方法,一个是在点击回车时,软键盘自动消失,一个是在点击背景是软键盘自动消失,这两个方法在第四章中有详细的讲解,可以去参考)。build and go运行,这是你输入内容,关闭应用程序,然后在打开,你会发现你刚才输入的内容仍然存在,下面开始第二种方法,使用对象归档的方法。

创建一个新的项目,我的项目名是:IP_11persistence2。创建好以后,在Classes文件夹上点右键创建一个新的类,选择NSObject subclass模板。名称为fourlines。打开fourlines.h,向里面添加四个常量和四个输出口:

#define kField1Key @"show1"
#define kField2Key @"show2"
#define kField3Key @"show3"
#define kField4Key @"show4"

NSString *show1;
NSString *show2;
NSString *show3;
NSString *show4;

打开fourlines.m文件,修改代码:

-(void)encodeWithCoder:(NSCoder *)encoder //对所有的属性进行编码
{
 [encoder encodeObject:show1 forKey:kField1Key];
 [encoder encodeObject:show2 forKey:kField2Key];
 [encoder encodeObject:show3 forKey:kField3Key];
 [encoder encodeObject:show4 forKey:kField4Key];
}

-(id)initWithCoder:(NSCoder *)decoder//使用相同的4格键值对这些属性进行解码
{
 if(self == [super init])
 {
  self.show1 = [decoder decodeObjectForKey:kField1Key];
  self.show2 = [decoder decodeObjectForKey:kField2Key];
  self.show3 = [decoder decodeObjectForKey:kField3Key];
  self.show4 = [decoder decodeObjectForKey:kField4Key];
 }
 return self;
}

-(id)copyWithZone:(NSZone *)zone//创建一个新的fourline对象,并将所有4格字符串复制到其中
{
 fourlines *copy = [[[self class] allocWithZone:zone] init];
 show1 = [self.show1 copy];
 show2 = [self.show2 copy];
 show3 = [self.show3 copy];
 show4 = [self.show4 copy];
 return copy;
}

然后打开IP_11persistence2ViewController.h,里面声明四个输出口,这和第一个例子一样,定义两个常量。

#define kFileName @"archive"
#define kDataKey @"Data"

打开IP_11persistence2ViewController.m导入fourlines类。

#import "fourlines.h"

修改代码

-(NSString *)dataFilePath//用来返回数据文件的完整路径名
{
 NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 NSString *paths = [path objectAtIndex:0];
 return [paths stringByAppendingPathComponent:kFileName];
}

-(void)applicationWillTerminate:(NSNotification *)notification//应用程序在退出时调用,将数据保存到数据列表
{
 fourlines *fourline = [[fourlines alloc] init];
 fourline.show1 = show1.text;
 fourline.show2 = show2.text;
 fourline.show3 = show3.text;
 fourline.show4 = show4.text;
 
 NSMutableData *data = [[NSMutableData alloc] init];
 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
 [archiver encodeObject:fourline forKey:kDataKey];
 [archiver finishEncoding];
 [data writeToFile:[self dataFilePath] atomically:YES];
 [fourline release];
 [archiver release];
 [data release];
}

-(void)viewDidLoad
{
 NSString *filePath = [self dataFilePath];//得到文件路径
 if([[NSFileManager defaultManager] fileExistsAtPath:filePath])//如果该文件存在就加载
 {
  NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
  NSKeyedUnarchiver *archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
  fourlines *fourline = [archiver decodeObjectForKey:kDataKey];
  [archiver finishDecoding];
  
  show1.text = fourline.show1;
  show2.text = fourline.show2;
  show3.text = fourline.show3;
  show4.text = fourline.show4;
  
  [archiver release];
  [data release];
 }
 UIApplication *app = [UIApplication sharedApplication];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:)
             name:UIApplicationWillTerminateNotification
              object:app];
 [super viewDidLoad];
}

对IP_11persistence2ViewController.xib的操作同第一个。

下面来创建最后一个使用SQLite来保存数据。我创建的项目名是IP_11SQLite。

首先导入libsqlite3.lib,这和导入FrameWork一样,在Framework文件夹上面右击,ADD-Existing Files然后选择文件,该文件在/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.2.sdk/usr/lib/libsqlite3.dylib下面。导入后开始修改代码。

打开IP_11SQLiteViewController.h

#import "/usr/include/sqlite3.h"
#define kFileName @"data.sqlite3"

增加一个变量:sqlite3 *database;

修改后为:

#import <UIKit/UIKit.h>
#import "/usr/include/sqlite3.h"
#define kFileName @"data.sqlite3"

@interface IP_11SQLiteViewController : UIViewController {
 IBOutlet UITextField *show1;
 IBOutlet UITextField *show2;
 IBOutlet UITextField *show3;
 IBOutlet UITextField *show4;
 
 sqlite3 *database;
}
@property (nonatomic, retain) UITextField *show1;
@property (nonatomic, retain) UITextField *show2;
@property (nonatomic, retain) UITextField *show3;
@property (nonatomic, retain) UITextField *show4;
-(NSString *)dataFilePath;
-(void)applicationWillTerminate:(NSNotification *)notification;
@end

然后打开IP_11SQLiteViewController.m修改代码如下:

-(NSString *)dataFilePath//用来返回数据文件的完整路径名
{
 NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 NSString *paths = [path objectAtIndex:0];
 return [paths stringByAppendingPathComponent:kFileName];
}

-(void)applicationWillTerminate:(NSNotification *)notification//应用程序在退出时调用,将数据保存到数据列表
{
 for(int i = 1; i<=4; i++)
 {
  NSString *fieldName = [[NSString alloc] initWithFormat:@"show%d",i];
  UITextField *field = [self valueForKey:fieldName];//根据每一i的值设定相应textfield
  [fieldName release];
  
  NSString *update = [[NSString alloc] initWithFormat:@"insert or replace into fields (row, field_data) values (%d, '%@');",i,field.text];//插入或更新表
  char *errorMsg;
  
  if(sqlite3_exec(database, [update UTF8String],NULL,NULL,&errorMsg) != SQLITE_OK)//执行插入或更新操作,如果插入或更新失败给出错误信息
  {
   NSAssert1(0,@"Error updating tables: %s",errorMsg);
   sqlite3_free(errorMsg);
  }
 }
 sqlite3_close(database);//关闭数据库连接
}

-(void)viewDidLoad
{
 if(sqlite3_open([[self dataFilePath] UTF8String], &database) != SQLITE_OK)//打开数据库,如果打开失败给出提示
 {
  sqlite3_close(database);
  NSAssert(0,@"Failed to open database");
 }
 
 char *errorMsg;
 NSString *createSQL = @"create table if not exists fields (row integer primary key, field_data text);";//创建表
 if(sqlite3_exec(database, [createSQL UTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)//是否创建成功
 {
  sqlite3_close(database);
  NSAssert1(0,@"Error creating table: %s",errorMsg);
 }
 
 NSString *query = @"select row, field_data from fields order by row";//查找表中的数据,行和每一行对应的值
 sqlite3_stmt *statement;
 if(sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK)
 {
  while(sqlite3_step(statement) == SQLITE_ROW)
  {
   int row = sqlite3_column_int(statement, 0);
   char *rowData = (char *)sqlite3_column_text(statement, 1);
   
   NSString *fieldName = [[NSString alloc] initWithFormat:@"show%d",row];
   NSString *fieldValue = [[NSString alloc] initWithUTF8String:rowData];
   UITextField *field = [self valueForKey:fieldName];
   field.text = fieldValue;
   [fieldName release];
   [fieldValue release];
  }
  sqlite3_finalize(statement);
 }
 
 UIApplication *app = [UIApplication sharedApplication];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:)
             name:UIApplicationWillTerminateNotification
              object:app];
 [super viewDidLoad];
}
最后就是IP_11SQLiteViewController.xib的修改了,和第一个一样的。

到此三种方法的操作结束了。

源代码在我的csdn上面有,需要的可以去下载,下载地址http://hanyegudeng.download.csdn.net/。

phone开发基础教程相关推荐

  1. Xamarin图表开发基础教程(13)OxyPlot框架支持的其它图表

    Xamarin图表开发基础教程(13)OxyPlot框架支持的其它图表 除了以上提到的图表外,OxyPlot组件还包含了6种类型的其它图表,分别为等高线图.箱线图.饼图.热图.散点图和散点误差图,如图 ...

  2. Xamarin图表开发基础教程(12)OxyPlot框架支持的金融图表类型

    Xamarin图表开发基础教程(12)OxyPlot框架支持的金融图表类型 OxyPlot组件中支持5种类型的金融图表,它们分别为销量图.高低图.股票K线图.股票走势图和旧式股票图,如图1.20~1. ...

  3. Xamarin图表开发基础教程(11)OxyPlot框架支持的图表类型

    Xamarin图表开发基础教程(11)OxyPlot框架支持的图表类型 OxyPlot组件中支持7种类型的条型图表,分别为普通条形图.线型条形图.矩形条形图.差值图.龙卷风图.普通柱形图和柱形误差图, ...

  4. Xamarin图表开发基础教程(10)OxyPlot框架支持的图表类型

    Xamarin图表开发基础教程(10)OxyPlot框架支持的图表类型 OxyPlot组件支持26种图表,这些图表按照功能和样式可以分为4大类,分别为线型图表.条型图表.金融图表和其它图表. 线型图表 ...

  5. Xamarin图表开发基础教程(8)OxyPlot框架

    Xamarin图表开发基础教程(8)OxyPlot框架 [示例OxyPlotFormsDemo]在Xamarin.Forms中实现线图的显示. (1)打开Xamarin.Forms项目. (2)将Ox ...

  6. Xamarin图表开发基础教程(7)OxyPlot框架

    Xamarin图表开发基础教程(7)OxyPlot框架 Xamarin.Forms中使用OxyPlot框架 在Xamarin. Forms平台上实现图表显示需要完成以下的步骤: 1.添加OxyPlot ...

  7. Xamarin图表开发基础教程(6)OxyPlot框架

    Xamarin图表开发基础教程(6)OxyPlot框架 Xamamin iOS中绘制线图OxyPlotiOSDemo [示例OxyPlotiOSDemo]下面将实现线图的显示.具体的操作步骤如下: ( ...

  8. Xamarin图表开发基础教程(5)OxyPlot框架

    Xamarin图表开发基础教程(5)OxyPlot框架 Xamarin.iOS中使用OxyPlot框架 在Xamarin.iOS平台上实现图表显示需要完成以下的步骤: 1.添加OxyPlot.Xama ...

  9. Xamarin图表开发基础教程(4)OxyPlot框架

    Xamarin图表开发基础教程(4)OxyPlot框架 XamaminAndroid中绘制线图OxyPlotAndroidDemo [示例1-1:OxyPlotAndroidDemo]下面实现线图的绘 ...

  10. Xamarin图表开发基础教程(3)OxyPlot框架

    Xamarin图表开发基础教程(3)OxyPlot框架 Xamarin.Android中使用OxyPlot框架 在Xamarin.Android平台上实现图表显示需要完成以下的步骤: 1.添加OxyP ...

最新文章

  1. TokuDB在生产环境的应用场景(zabbix也可以)
  2. 对Canvas的研究
  3. C#基于Socket的CS模式的完整例子
  4. 腾讯云 Centos 配置 JDK Tomcat Mysql
  5. (原创)让mongodb的secondary支持读操作
  6. 一般拦截器 serviceImpl部分
  7. WindowsXP 系统登陆原理及其验证机制概述
  8. Log Explorer 使用简介转
  9. 使用 CSS 模拟鼠标点击交互
  10. 超算简史:练飞天之技,登峰一战后,终有落地日 | 凌云时刻
  11. 雨棚板弹性法计算简图_钢结构雨篷图纸计算书
  12. 在哪下载公司考勤刷卡特殊情况说明Excel模板
  13. elixer学习笔记
  14. helm和operator
  15. Python SMTP 163邮箱发送邮件不成功
  16. amd显卡风扇调节_amd显卡风扇速度设置linux版本
  17. h5支付——前端需要处理什么?
  18. 业务流程管理包括什么
  19. 微信抢红包算法实现(JAVA)
  20. 学习LSM(Linux security module)之二:编写并运行一个简单的demo

热门文章

  1. 之前美国芯片说不卖,现在是中国制造说不要,美国芯片后悔莫及
  2. lrzsz快传小工具
  3. 洛谷P1080 国王游戏 贪心+高精度
  4. 会声会影使用技巧小结
  5. git commit --amend 修改最近一次提交
  6. 2021图灵奖公布,高性能计算先驱Dongarra获奖!
  7. 如何使用会声会影制作手机竖屏效果
  8. 【刷机】给小米8输入PE12
  9. 查看x86 cpu睿频命令
  10. Ceph和Swift的比较-为什么他们没有撕逼