内容


在 iPhone 解决方案中集成 WebSphere CloudBurst 功能,第 3 部分

创建控制器来管理 Web 2.0 iPhone 应用程序的视图和流

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 在 iPhone 解决方案中集成 WebSphere CloudBurst 功能,第 3 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:在 iPhone 解决方案中集成 WebSphere CloudBurst 功能,第 3 部分

敬请期待该系列的后续内容。

先决条件

使用控制器类来管理一个使用 iPhone SDK 工具包的客户端应用程序的视图部分是将 IBM WebSphere CloudBurst Appliance 与 Apple iPhone 设备集成在一起的 Web 2.0 应用程序开发的下一步。该应用程序使用工具包来实现模型-视图-控制器框架(Model-View-Controller Framework)。在 第 2 部分 中,您创建了视图部件及其对象(如按钮、文本框和表示视图)。在控制器类中,您创建了对象来管理协作。在本文中,您将在视图控制器类和 iPhone 应用程序相应的视图之间创建连接来管理应用程序内部的流控制器。

同上两期一样,本系列文章假定您对 Web 2.0 技术,包括 Rest、ATOM、JSON 和 Objective-C 有一个大致了解。这些文章中描述的示例应用程序是通过 Objective-C 中的模型-视图-控制器 (MVC) 模式,使用 Apple iPhone SDK 3.x 实现的,并将在一台安装了 3.x 固件的 iPhone(或 iPod Touch)设备上运行。因此,这个开发假设您基本了解 C 编程语言(Object-C 继承了所有 C 的特性)。而且,理解基本的面向对象概念(如继承、类、接口等)也很重要。

此外,这个练习使用了 Cocoa 框架,它是由一组 API、程序库和组成 iPhone SDK 开发层的运行时代码所构成的。Cocoa 是用 Object-C 实现的,并使用 MVC 模式来封装视图、应用程序数据、控制器和实现管理逻辑的类。

最后,要完成本文所介绍的步骤和运行例子,您需要在计算机上安装 Apple SDK 3.0 或以上版本,运行一个 Mac OS 兼容版本。您还需要运行在您环境中的 WebSphere CloudBurst Appliance 的访问权限。

完成 OneWindow 视图

每次在您开始创建 Cocoa 项目时,您都需要选择一个模板。对于本练习,您将选择 Tab Bar Application 模板。此模板会生成两个窗口(见图 1)和一个让您在这两个窗口之间进行切换的选项卡栏。

  • First View: MainWindow.xib
  • Second View: SecondView.xib
图 1. 应用程序视图
应用程序视图
应用程序视图

如果您打开 MainWindows.xib 的 View Component 窗口,您将看到如图 2 所示的结构。它是由以下内容组成:

  • File's Owner(委托给 Test App Delegate)
  • First Responder
  • Test App Delegate
  • Windows (不使用的通用窗口)
  • Tab Bar Controller(管理两个 View Controller(First View 和 Second View)的选项卡栏控制器。)
    • Tab Bar
    • First View controller(适用于 First View)
    • View(由 FirstViewController 管理的视图)
    • Tab Bar Item(First View 的 Bar Controller 中的项)
    • Second View controller(适用于 Second View)
    • View(由 FirstViewController 管理的视图)
    • Tab Bar Item(Second View 的 Bar Controller 中的项)
图 2. MainWindow 结构
MainWindow 结构
MainWindow 结构

插入 Navigation Controller

TNavigation Controller 管理应用程序窗口流中视图之间的导航。正如在 第 2 部分 中所讨论的,您将使用三个界面之间的简单导航来设计应用程序。要做到这一点,您需要在 MainWindows 中定义 Navigation Controller。

Navigation Controller 具有以下部件:

  • Navigation Controller:这是控制器对象,处理导航事件并将其所有捆绑在一起。
  • Navigation Bar:这是位于顶部的工具栏,使用户可以看到他们正在遍历的一个导航层次结构,并使他们能够向后导航。
  • View Controller:这是与 hold 视图相关的 View Controller 类。
  • Navigation Item:导航项位于 Navigation Bar 中且具有用于导航的按钮。

在 MainWindow.xib 组件窗口中,请更改在 Tab Bar Controller 组件中定义的第一个视图控制器。

  1. MainWindow 组件窗口中选择 Tab bar controller
  2. 选择 Attribute View 并将名为 First 的 View Controller 类从 View Controller 更改到 Navigation Controller,如图 3 所示。
    图 3. Navigation Controller 类定义
    Navigation Controller 类定义
    Navigation Controller 类定义

凭借 Navigation Controller 类,管理应用程序视图之间的导航成为可能。在此项目中,您将在 OneView.xib 文件(具有 OneViewController 类)中创建第一个视图。您需要用 OneView 替换 MainWindow。

  1. 通过单击顶部窗口选择包含 First 视图的窗口。
  2. 在视图内选择 First View 面板并删除它们。注意:不要 选择标签。在图 4 中,在删除之后,您可以看到该视图。
    图 4. 主视图
    主视图
    主视图
  3. 在视图窗口中,通过单击 View 的灰框部分来确保选择空视图。
  4. 在 View Controller 属性中,使用下拉菜单插入 NIB 文件:OneView
    图 5. OneView
    OneView
    OneView
  5. 单击左侧面板中部的 One View 蓝色链接以便打开 One View 视图。
  6. 在 View Component 视图中选择 File's Owner
  7. 使用 Class 下拉菜单为选中的 One View 视图分配 OneViewController 视图控制器类。这意味着 OneViewController 类是 OneView 视图(图 6)的控制器。
    图 6. OneView 控制器类
    OneView 控制器类
    OneView 控制器类
  8. 保存所有更改。
  9. 通过单击 Build > Build and Run 可以测试应用程序。

现在您可以看到 OneView 作为应用程序中的第一个打开窗口。

连接视图组件

在您创建 OneViewController 类时,您定义了实例变量来管理视图组件。这些变量是:

  • field1:适用于第一个输入字段
  • field2:适用于第二个输入字段
  • description:适用于测试文本字段

您需要分配这些变量到每一个视图组件。现在,让我们看看如何连接 field1。(您将使用相应的实例变量对所有视图组件重复此操作。)

  1. Interface Builder 中打开 OneView
  2. 选择 File's Owner。它应该是 OneViewController 类。
  3. 使用鼠标右键,将 File's Owner 行拖到第一个文本字段上,如图 7 所示。
    图 7. 分配实例变量
    分配实例变量
    分配实例变量
  4. 从拖动操作结束时出现已分配给此组件的变量的菜单中选择。
    图 8. 变量表
    变量表
    变量表
  5. 对所有视图组件重复相同的操作。为每个视图组件选择相应的实例变量。

最后,将所有实例变量都分配给视图组件。

分配按钮操作

在 OneView 中,有以下三个按钮:

  • test:分配给 (IBAction) buttonPressedTest
  • load:分配给 (IBAction) buttonDownload
  • next:分配给 (IBAction) buttonNext

您需要将每一个操作分配给相应的按钮视图组件。现在将连接测试按钮。(使用相应的方法对所有按钮重复相同的操作。)

  1. 在 OneView 组件窗口中选择 Test 按钮。
  2. Button Connection 中,您可以看到所有按钮事件。
  3. 使用鼠标左键,拖动您想要管理的操作(如 Touch Down)到 File's Owner 行。
  4. 从在拖动操作(例如,buttonPressedtest)结束时出现的列表中选择。
  5. 对所有的按钮重复相同的操作。为每个按钮组件选择相应的方法。
    图 9. OneViewController 连接
    OneViewController 连接
    OneViewController 连接

完成 OneViewController 类

现在您可以在类中插入所有需要的代码来管理 buttonPressedTest 和 buttonNext。ButtonPressedTest 方法会在 responseString 变量(参见清单 1)中加载测试文件 (example.sql)。

清单 1. (IBAction) buttonPressedTest
<![CDATA[
(IBAction) buttonPressedTest
{
[field1 resignFirstResponder];
[field2 resignFirstResponder];
NSString *filePath = [[NSBundle mainBundle] 
	pathForResource:@"example" 
	ofType:@"sql"];
NSString *responseString = [NSString stringWithContentsOfFile:filePath];
NSLog(@"responseString:%@",responseString);

description.text = responseString;
}]]>

Example.sql 文件包含如清单 2 中所示的项。

清单 2. example.sql
[
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Stopped",
      "created": 1260941860895,
      "name": "Single Server",
      "currentstatus": "RM01011",
      "desiredstatus": null,
      "currentmessage": "RM07153",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/6",
      "updated": 1260943821497,
      "id": 51,
      "currentmessage_text": "Stopped"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Running",
      "created": 1263868560454,
      "name": "05WASadmin Cloned Lab Virtual System",
      "currentstatus": "RM01005",
      "desiredstatus": "",
      "currentmessage": "RM07054(\"05WASAdmin+Lab+Application\",\"05WASadmin+Cloned+Lab+
      Virtual+System+vm-001-016+dmgr\")",
      "pattern": "/resources/patterns/43",
      "owner": "/resources/users/15",
      "updated": 1263869039755,
      "id": 59,
      "currentmessage_text": "script package of 05WASAdmin Lab Application
      on VM 05WASadmin Cloned Lab Virtual System vm-001-016 dmgr"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Stopped",
      "created": 1263857152896,
      "name": "Financial Application Test Group Sys 05",
      "currentstatus": "RM01011",
      "desiredstatus": null,
      "currentmessage": "RM07153",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/15",
      "updated": 1263863898994,
      "id": 54,
      "currentmessage_text": "Stopped"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Run",
      "created": 1263857159858,
      "name": "John",
      "currentstatus": "RM01006",
      "desiredstatus": "",
      "currentmessage": "RM07045",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/14",
      "updated": 1263857536925,
      "id": 55,
      "currentmessage_text": "The VM is available"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Run",
      "created": 1263862881511,
      "name": "LucaV",
      "currentstatus": "RM01006",
      "desiredstatus": "",
      "currentmessage": "RM07045",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/4",
      "updated": 1263863256864,
      "id": 56,
      "currentmessage_text": "The VM is available"
   }
]

ButtonNext 方法将调用 ListElement 视图并实例化 ListElementViewController,如清单 3 所示。

Listing 3. (IBAction) buttonNext
<![CDATA[
-(IBAction) buttonNext
{
[field1 resignFirstResponder];
NSString *server = field1.text;
ListElementViewController *listElementController = 
	[[ListElementViewController alloc] 
		initWithNibName:@"ListElementView" 
		bundle:nil];
NSLog(@"func UnoViewController.buttonNext ");
[listElementController setDescpText:server];
[self.navigationController pushViewController:listElementController 
 animated:YES];
]]>
[listElementController loadJsonText:description.text];
<![CDATA[	
}
]]>

LoadJsonText 方法将接收的数据从 WCA 注入到 ListElementViewController。该接收的数据用于显示视图上的列表。

完成 ListElement 视图

ListElement 视图由惟一的视图组件(Table View 组件)组成。虽然此组件在视图控制器类内不具有已分配的实例变量,但是您需要定义类 delegate 。这是必要的,因为您需要定义许多方法来管理所有必要的表视图操作。有必要时该框架都会调用类 delegate,且该类必须执行所有需要的方法。这些方法是:

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  • (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  • (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

在 delegate 类中创建这些方法。定义 ListElement 视图的 delegate 类。为了简化,您将对视图控制器和表视图 delegate 这两种方法使用相同的类(如 ListElementViewController)。

  1. 打开 ListElementView.xib 文件并在 Identity 视图中插入 ListElementViewController 类。
  2. 在视图组件窗口中选择 Table View 组件。
  3. 选择 Table View connection
  4. 使用鼠标左键,在视图组件窗口中从 dataSource 标签拖动到 File's Owner
  5. delegate 标签使用相同的操作。

图 10 显示了最终的 Table View connections。

图 10. 表视图 dataSource 和 delegate
表视图 dataSource 和 delegate
表视图 dataSource 和 delegate

管理 JSON 文件

此视图从调用方视图控制器接收 JSON 数据文件。在此解决方案中,先前的视图管理网络连接协议。虽然此视图将代替管理数据(不知道数据会到哪里),但是它必须将文本字符串(类 JSON 格式)转换为 Objective-C 数据对象。要获取字典,您就要使用 VirtualSystems 类。正如 第 2 部分 讨论的,您将包括在此 JSON 分析程序类中。要在类声明的顶部执行此操作,您需要包括:

  • #import "JSON/JSON.h":根据 JSON 分析程序选择而不同。
  • #import "VirtualSystems.h":包括用于获取 JSON 数据对象的类。在 JSON 文本文件中发现的每个元素都将转换为 VirtualSystems 对象。

调用方类 (OneViewController) 之间的连接和 ListElementViewController 都用以下代码方法执行,如清单 4 所示。

清单 4. IBAction) loadJsonText:(NSString *)jsonString.h
<![CDATA[
- (IBAction) loadJsonText:(NSString *)jsonString{
NSLog(@"func UnoViewController.loadJsonText :%@ ",jsonString);
	
NSError *error;]]><![CDATA[
SBJSON *json = [[SBJSON new] autorelease];
NSArray *luckyNumbers = [json objectWithString:jsonString 
 error:&error];
]]>
	
<![CDATA[for (int i = 0; i < [luckyNumbers count]; i++) 
{
	NSLog(@"number:%@",[luckyNumbers objectAtIndex:i]);
	NSDictionary *tmpDict= [luckyNumbers objectAtIndex:i];
		
	VirtualSystems *elem = [[VirtualSystems alloc] 
						initWithJSON:tmpDict name:@"test"];
	[listElement addObject:elem];
	[elem release];
}
}
]]>

您会看到粗体的代码,这取决于您对 JSON 解析器的选择。根据解析器操作说明更改这几行代码。在这个示例中,该方法使用 SBJSON 对象来解析和获取数据对象数组。对于此数组中发现的每一个元素,您将创建一个 VirtualSystems 对象并在实例变量 (listElement) 中加载它。

正如您所看到的,我们使用了 NSDictionary 对象。Objective-C 中的字典是一个具有两组信息(惟一的密钥和相关的值)的对象,且使您能够使用该密钥来检索条目。如果您需要方便快捷的方式来检索与任意密钥相关的数据,那么您应该使用此种类型的对象。该密钥通常是整型或字符串的,条目是任何类型的。因为您使用了 NSDictionary,所以您创建了静态字典。由密钥和相关数据组成的每一行被称为一个条目。在字典内,密钥是惟一的。密钥或值可能是 null(如果需要字典中的 null 值,请使用 NSNull 替代)。您从 JSON 文件中提取数据并在被称为 tmpDict 的 Dictionary 对象中映射信息。根据已使用的 JSON 结构,Dictionary 对象会具有以下字段:

  • desiredstatus_text
  • currentstatus_text
  • created
  • name
  • currentstatus
  • desiredstatus
  • currentmessage
  • pattern
  • owner
  • updated
  • id
  • currentmessage_text

这些字段将包含从每项的 JSON 文件中提取的值。JSON 结构的每个节点都将有一个条目。在这种情况下,您将有 5 个条目。

创建 Table 行

在 ListElement 视图中,您将插入一个简单的 Table 视图。您将在单一的行表中创建一个具有更多标签的复杂列表和一个图标。有很多方法可以做到这一点。这里所用的方法不使用任何图形化界面,因此使用代码您将在表视图内部插入每个视图组件。

可以用 cellForRowAtIndexPath 方法来实现此功能。每次调用此方法,系统都需要了解何如构建表视图的任何单行。这样,您可以创建彼此之间不同的行。要减少使用这种方法,您将要在两种不同的块中构建方法,这两种块是:

  • cell==nil:只有在第一次时此测试才为 true。您将为行创建单元格。
  • cell!=nil:这意味着已经创建了单元格行。您需要将惟一的值分配给该单元格标签。在这种情况下,请跳过创建视图单元格表。

使用此方法,您需要在屏幕布局中使用正确的组件位置来设置编码(在单元格创建期间)。要做到这一点,您需要逐点地定义正确的 Frame 矩形点(rectangle point)(清单 5)。

清单 5. cellForRowAtIndexPath 方法
<![CDATA[
- (UITableViewCell *)tableView:(UITableView *)tableView 
   cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"ImageOnRightCell";
UILabel *mainLabel, *secondLabel;
UIImageView *photo;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
 CellIdentifier];

if (cell == nil) {
	cell = [[[UITableViewCell alloc]
		initWithStyle:UITableViewCellStyleDefault 
		reuseIdentifier:CellIdentifier] autorelease];
	cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
		
	mainLabel = [[[UILabel alloc]
		initWithFrame:CGRectMake(0.0, 0.0, 290.0, 18.0)] autorelease];
	mainLabel.tag = MAINLABEL_TAG;
	mainLabel.font = [UIFont systemFontOfSize:14.0];
	mainLabel.textAlignment = UITextAlignmentRight;	
	mainLabel.textColor = [UIColor blackColor];
	mainLabel.autoresizingMask = 
	 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
	[cell.contentView addSubview:mainLabel];

	secondLabel = [[[UILabel alloc]
	 initWithFrame:CGRectMake(0.0, 20.0, 265.0, 25.0)] autorelease];
	secondLabel.tag = SECONDLABEL_TAG;
	secondLabel.font = [UIFont systemFontOfSize:12.0];
	secondLabel.textAlignment = UITextAlignmentRight;
	secondLabel.textColor = [UIColor darkGrayColor];
	secondLabel.autoresizingMask = 
	 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
	[cell.contentView addSubview:secondLabel];

	photo = [[[UIImageView alloc]
	 initWithFrame:CGRectMake(270.0, 18.0, 25.0, 25.0)] autorelease];
	photo.tag = PHOTO_TAG;
	photo.autoresizingMask = 
	 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
	[cell.contentView addSubview:photo];
} else {
	
	mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
	secondLabel = (UILabel *)[cell.contentView viewWithTag:SECONDLABEL_TAG];
	photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}

VirtualSystems *vs = (VirtualSystems *) [listElement  
 objectAtIndex:indexPath.row];
mainLabel.text = [vs getName];
secondLabel.text =  [NSString stringWithFormat:@"%i",[vs getId]];
NSString *status=[vs getCurrentstatus];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:status 
 ofType:@"png"];
UIImage *theImage = [UIImage imageWithContentsOfFile:imagePath];
photo.image = theImage;
return cell;
}
]]>

打开 DetailSystem 视图

当用户在表中选择特定行时,您需要识别选定行中所示的虚拟系统、从 listElement 阵列检索其数据以及调用 ListElementView 和其视图控制器。

在执行选择操作时将通过视图调用方法 didSelectRowAtIndexPath。该方法接收列表中选定元素的索引,且您将使用此索引来识别 listElement 元素、提取它并使用 setVirtualSystem 行为将其推入 DetailSystemViewController,如清单 6 所示。在退出该视图以前,请取消选定行。

清单 6. didSelectRowAtIndexPath 方法
<![CDATA[
- (void)tableView:(UITableView *)tableView 
	didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	
VirtualSystems *elem = (VirtualSystems *)[listElement 
 objectAtIndex:indexPath.row];
DetailSystemViewController *dettaglioController = 
	[[DetailSystemViewController alloc] 
			initWithNibName:@"DetailSystemView" bundle:nil];
NSLog(@"func didSelectRowAtIndexPath elem.keyID:%i",indexPath.row);
[dettaglioController setVirtulaSystem: elem];
[self.navigationController pushViewController:dettaglioController 
 animated:YES];
	
[tableView deselectRowAtIndexPath:indexPath animated:YES];	
}
]]>

完成 DetailSystem 视图

此视图已经接收了单一系统的信息作为 VirtualSystems 对象的实例。此对象是由 ListElement 视图中的用户选择的元素。使用 -(void)setVirtualSystems: 行为可将该对象存储在 aSystem 实例变量中。对象中的值用于将信息分配给视图元素并将其显示给用户。您将使用图标来描述系统状态(虽然您可以在下载 部分中发现图标示例,但是您已可以使用其他图标):

  • RM01011:系统停止
  • RM01006:系统正在运行
  • RM01005:未知系统状态

将选定图标加载到项目中:

  1. 在 Mac 文件系统内加载图标。
  2. 将这些图标分别重命名为 RM01005.pngRM01006.pngRM01011.png
  3. 选择如本系列 第 1 部分 所描述的 Add > Existing Files

要简化代码,请分配通过 REST 联合检索的相同状态代码的图标。

您将从 aSystem 实例变量设置分配值到 -(void)viewDidLoad 方法中的视图组件。在清单 7 和清单 8 中,您可以看到 DetailSystemsViewController 代码。

清单 7. DetailSystemsViewController.h
<![CDATA[
//
//  DetailSystemViewController.h
//  WCA000
//
//  Created by luca amato on 9/27/10.
//

#import <UIKit/UIKit.h>
#import "VirtualSystems.h"

@interface DetailSystemViewController : UIViewController {
IBOutlet UILabel *name;
IBOutlet UILabel *currentStatusText;
IBOutlet UILabel *created;
IBOutlet UILabel *owner;
	
IBOutlet UIImageView *statusImg;

IBOutlet UITextView *currentMessageText;
	
VirtualSystems *aSystem;
}

@property (nonatomic, retain) VirtualSystems *aSystem;

- (void)setVirtulaSystem:(VirtualSystems *)system;

@end]]>
清单 8. DetailSystemsViewController.m
<![CDATA[
//
//  DetailSystemViewController.m
//  WCA000
//
//  Created by luca amato on 9/27/10.
//

#import "DetailSystemViewController.h"

@implementation DetailSystemViewController

// Implement viewDidLoad to do additional setup 
// after loading the view, typically from a nib.
- (void)viewDidLoad {
NSString *status=[aSystem getCurrentstatus];
NSString *imagePath = [[NSBundle mainBundle] 
 pathForResource:status ofType:@"png"];
statusImg.image = [UIImage imageWithContentsOfFile:imagePath];
	
self.title=[NSString stringWithFormat:@"%i",[aSystem getId]];
name.text=[aSystem getName];
created.text=[aSystem getCreated];
currentStatusText.text = [aSystem getCurrentstatusText];
currentMessageText.text = [aSystem getCurrentmessage];
NSObject *tmp=[aSystem getCreated];
	
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}

- (void)dealloc {
    [super dealloc];
}

- (void)setVirtulaSystem:(VirtualSystems *)system {
	aSystem = system;
}

@end]]>

现在您可以保存该文件。从选项菜单中选择 File > Save

保存应用程序状态

在很多用户应用程序中,保存应用程序状态很有用。在下一次启动应用程序时将检索已保存的信息,且在合适的字段中,是最近在应用程序中使用的值。

有很多方法可以做到这一点。在这里,使用一个简单文件就可以实现此功能。SDK 框架提供了一种方式以便将对象(包括标量值)编码成与体系结构无关的格式。您可以使用此类在文件中存储对象。这种键控存档文件(keyed archive)是使用 NSKeyedArchiver 实现的。要获得更多详细信息,请参见 Cocoa 库参考文档。在实例化第一个应用程序类(OneViewController)时,您将检索数据。第一次,您必须检查是否存在存档文件。如果不存在,您就必须创建一个空文件。在部署最后一个类时,将状态信息写进该文件。

状态类

对于一个良好的面向对象的方法来说,您将创建一个特定的类来管理所有必要的状态键值。您应该有 4 个值存储在状态文件中。因此,请创建 FourLines 类。此类管理 4 个值,每一个值都有特定的字段密钥。您将在 h 文件中定义该字段密钥值为常数:Field1,Field2,Field3,Field4

保存 Field1 FourLine 密钥中的服务器名(来自 field1)以及 Field2 FourLines 密钥中的 User ID(来自 field2)(清单 9 和 10)。出于安全原因,密码不会存储在应用程序状态中。

清单 9. FourLines.h
<![CDATA[
//
//  FourLines.h
//  WCA000
//
//  Created by luca amato on 9/27/10.
//

#import <Foundation/Foundation.h>
#define FIELD1KEY @"Field1"
#define FIELD2KEY @"Field2"
#define FIELD3KEY @"Field3"
#define FIELD4KEY @"Field4"

@interface FourLines : NSObject <NSCoding, NSCopying> {
	NSString *field1 ;
	NSString *field2 ;
	NSString *field3 ;
	NSString *field4 ;
}

@property (nonatomic, retain) NSString *field1;
@property (nonatomic, retain) NSString *field2;
@property (nonatomic, retain) NSString *field3;
@property (nonatomic, retain) NSString *field4;
@end
]]>
清单 10. FourLines.m
<![CDATA[
//
//  FourLines.m
//  Persistence
//
//  Created by luca amato on 9/11/10.
//

#import "FourLines.h"


@implementation FourLines

@synthesize field1;
@synthesize field2;
@synthesize field3;

@synthesize field4;

#pragma mark NSCoding
-(void)encodeWithCoder:(NSCoder *)encoder
{
	[encoder encodeObject:field1 forKey:FIELD1KEY];
	[encoder encodeObject:field2 forKey:FIELD2KEY];
	[encoder encodeObject:field3 forKey:FIELD3KEY];
	[encoder encodeObject:field4 forKey:FIELD4KEY];
}

-(id)initWithCoder:(NSCoder *)decoder
{
	if (self = [super init])
	{
		self.field1 = [decoder decodeObjectForKey:FIELD1KEY];
		self.field2 = [decoder decodeObjectForKey:FIELD2KEY];
		self.field3 = [decoder decodeObjectForKey:FIELD3KEY];
		self.field4 = [decoder decodeObjectForKey:FIELD4KEY];
	}
	return self;	
}

#pragma mark -
#pragma mark NSCopying
-(id)copyWithZone:(NSZone *)zone
{
	FourLines *copy = [[[self class] allocWithZone:zone] init];
	field1 = [self.field1 copy];
	field2 = [self.field2 copy];
	field3 = [self.field3 copy];
	field4 = [self.field4 copy];
	
	return copy;
}
@end
]]>

保存并检索数据状态

清单 11 所显示的代码部分可检索已保存的文件并从 FourLines 类提取特定字段值。

清单 11. 检索 FourLines
<![CDATA[	.
	.
NSData *data = [[NSMutableData alloc] 
 	initWithContentsOfFile:[self dataFilePathPersistence]];
NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] 
 	initForReadingWithData:data];
FourLines *fourLines = [unarch decodeObjectForKey:DATAKEY];
[unarch finishDecoding];
field1.text = fourLines.field1;
field2.text = fourLines.field2;

[unarch release];
[data release];
	.
	.]]>

清单 12 中的代码部分可保存 FourLines 类。

清单 12. 保存 FourLines
<![CDATA[	.
	.
FourLines *fourLines = [[FourLines alloc] init];
fourLines.field1 = field1.text;
fourLines.field2 = field2.text;
fourLines.field3 = @"uno";
fourLines.field4 = @"due";
	
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] 
 initForWritingWithMutableData:data];
[arch encodeObject:fourLines forKey:DATAKEY];
[arch finishEncoding];
[data writeToFile:[self dataFilePathPersistence] atomically:YES];
	.
	.]]>

您将在 OneViewController 类中插入这些代码,并在 -(void)DidLoad 方法插入检索方法代码。这将在加载 OneView 以后调用。

在删除 OneView 时调用 -(void)applicationWillTerminate 方法中保存的代码。在清单 13 中,您可以看到在 Unplugged 模式下运行的 OneViewController 类的完整代码。正如您看到的,从名为 example.sql 的静态文件中检索 JSON 数据。

清单 13. OneViewController.m
<![CDATA[
 //
//  UnoViewController.m
//  WCA000
//
//  Created by luca amato on 9/25/10.
//

#import "UnoViewController.h"
#import "ListElementViewController.h"

#import "FourLines.h"
#import "AlertPrompt.h"

@implementation UnoViewController

@synthesize inputString;
@synthesize field1 ;
@synthesize field2 ;
@synthesize description;

 // Implement viewDidLoad to do additional setup 
 // after loading the view, typically from a nib.
 - (void)viewDidLoad {
self.title = @"WCA Server";
	 
NSString *filePath = [self dataFilePathPersistence];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSData *data = [[NSMutableData alloc] 
initWithContentsOfFile:[self dataFilePathPersistence]];
NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] 
initForReadingWithData:data];
FourLines *fourLines = [unarch decodeObjectForKey:DATAKEY];
[unarch finishDecoding];
field1.text = fourLines.field1;
field2.text = fourLines.field2;

[unarch release];
[data release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] 
	addObserver:self 
	selector:@selector(applicationWillTerminate:) 
	name:UIApplicationWillTerminateNotification object:app
	];
	
[super viewDidLoad];
}
 
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
[super dealloc];
}

-(NSString *)dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains
	(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent: FILENAMEARCH];
}

-(IBAction) buttonDownloadImage
{
}	

-(IBAction) buttonPressedTest
{
[field1 resignFirstResponder];
[field2 resignFirstResponder];
NSString *filePath = [[NSBundle mainBundle] 
	pathForResource:@"example" ofType:@"sql"];
NSString *responseString = [NSString stringWithContentsOfFile:filePath];
NSLog(@"responseString:%@",responseString);
description.text = responseString;
}

-(IBAction) buttonNext
{
[field1 resignFirstResponder];
NSString *server = field1.text;
ListElementViewController *listElementController = 
	[[ListElementViewController alloc] 
			initWithNibName:@"ListElementView" bundle:nil];
NSLog(@"func UnoViewController.buttonNext ");
[listElementController setDescpText:server];
[self.navigationController pushViewController:listElementController 
 animated:YES];
[listElementController loadJsonText:description.text];
}

-(NSString *)dataFilePathPersistence
{
NSArray *path = NSSearchPathForDirectoriesInDomains
	(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent: FILENAMEARCH];
}

-(void)applicationWillTerminate:(NSNotification *)notification
{
FourLines *fourLines = [[FourLines alloc] init];
fourLines.field1 = field1.text;
fourLines.field2 = field2.text;
fourLines.field3 = @"uno";
fourLines.field4 = @"due";
	
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] 
	initForWritingWithMutableData:data];
[arch encodeObject:fourLines forKey:DATAKEY];
[arch finishEncoding];
[data writeToFile:[self dataFilePathPersistence] atomically:YES];
[fourLines release];
[arch release];
[data release];
}
@end
]]>

保存并测试

确保保存所有文件。现在您可以通过从 Go 菜单选择 Build > Build 来测试应用程序。如果您发现一些错误,请删除它们并重新编译该项目。

要在主屏幕中测试应用程序,请单击 Test 按钮。在底部,您可以看到测试文件。单击 Next 按钮。该窗口显示了一系列在示例 JSON 文件中定义的服务器。您可以选择在行中存在的系统。在下一个视图 (DetailSystemView) 中,应用程序将显示选中系统的详细信息。在每一个视图中,使用视图顶部的导航工具栏中的按钮,您可以返回。使用向后按钮,您会看到前面视图的标题。

结束语

在本文中,使用 Cocoa 框架您可以创建 MVC 框架的控制器部件。该控制器类管理 GUI、实现 Navigation Controller、连接视图组件并分配操作给 GUI 的每一个按钮。然而,应用程序不管理 HTTPS 协议。要与 WebSphere Cloudburst Appliance 进行通信,您将需要实现网络元素,包括安全管理,这在 本系列结束语 中进行了描述。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere, Web development
ArticleID=681811
ArticleTitle=在 iPhone 解决方案中集成 WebSphere CloudBurst 功能,第 3 部分: 创建控制器来管理 Web 2.0 iPhone 应用程序的视图和流
publish-date=06232011