创建一个iOS应用程序

移动开发 iOS
iPhone不是桌面计算机,有不同的预期,需要完全不同设计思路。需要从iOS的能力获得优势,还要避开那些对移动环境无关和不切实际的特性。iPhone和iPod touch的小尺寸屏幕意味着,你的应用程序用户界面应该合理组织,并总是关注于用户最需要的信息。

在表面看,创建一个iOS应用程序和创建一个Mac OS X应用程序很像。使用相同的工具和许多相同的基础库。尽管很像,还是有些显著的不同。iPhone不是桌面计算机,有不同的预期,需要完全不同设计思路。需要从iOS的能力获得优势,还要避开那些对移动环境无关和不切实际的特性。iPhone和iPod touch的小尺寸屏幕意味着,你的应用程序用户界面应该合理组织,并总是关注于用户最需要的信息。

iOS运行用户跟iPhone和iPod touch的设备,用桌面程序无法实现的交互方式来交互。多点触摸(Multi-Touch)是一种革命性的接收事件的新方法,报告每一个独立的手指对屏幕的触摸,并使处理多手指手势和其他的复杂输入变得非常简单。此外,内建的硬件特性,例如加速度传感器,虽然也在一些桌面系统使用,但是在iOS中使用的更佳广泛,可以跟踪屏幕的当前方向并相应的调整你的内容。了解如何在你的程序中使用这些特性,有助于你聚焦适于用户的设计方式。

了解iPhone程序设计的最好办法就是看例子。本文针对例程MoveMe进行介绍。下面的例子展示iOS程序的典型性为,包括:

◆初始化程序

◆显示窗口

◆描绘定制内容

◆处理触摸事件

◆进行动画

图 1 展现了应用程序的接口。触摸Welcome按钮会引发一个动画,按钮会跳动并把自己的中心移动到你的指下。手指在屏幕上移动,按钮就会跟着你的手指移动。把你的手指从屏幕上拿开,引发另一个动画,按钮跳回它的原始位置。在按钮以外的任何位置双击屏幕会改变按钮欢迎词的语言。

MoveMe程序的窗口

图1 MoveMe程序的窗口

阅读本文的其他部分前,你应该下载例子(MoveMe),这样你可以直接参照源代码学习。你应该已经阅读了iPhone开发者中心下面的指南文章,对iOS和开发需要的工具和语言有了基本的了解:

iOS概述

iOS开发工具

如果你对Objective-C编程语言不熟悉,你应该阅读学习Objective-C:入门手册让自己对Objective-C的基本语法有所了解。#p#

查看MoveMe例子工程

下载MoveMe例程,你可以得到构建和运行程序需要的源代码和支持文件。你应该使用Xcode应用程序(位于 /Developer/Applications)管理iOS项目。每个Xcode项目窗口包含一个工作空间用来组织代码和资源文件,编译和组装程序的构建规则,编辑和调试代码的工具。

图 2现实MoveMe程序的Xcode项目窗口。要打开这个项目,首先把它复制到本地硬盘,然后双击MoveMe.xcodeproj文件即可。(你可以在可以在Xcode选择菜单File > Open然后选择文件。)项目包含了多个Objective-C源代码文件(扩展名为.m),一些图像文件和其他资源,以及构建程序包的预定义目标。

MoveMe项目窗口

图2 MoveMe项目窗口

在iOS,Xcode项目的最终目标是程序包,一个特性类型的目录,放置程序的二进制执行文件以及资源文件。iOS包是一个相对平面的目录结构,包含的大多数文件都在包目录的顶层。然而,包文件也可以包含子目录去存储字符串的本地化版本以及其他的语言相关的资源文件。本文不需要你了解程序的确切结构,但是你感兴趣的话可以在iOS编程指南程序包章节找到这些信息。

构建MoveMe程序

要构建MoveMe程序并在模拟器里运行它,要找如下的步骤去做:

1. 在Xcode中打开MoveMe.xcodeproj文件

2. 在项目工具栏,确保Active SDK菜单中选中了simulator选项。(如果Active SDK菜单在工具栏中没有出现,选择菜单Project > Set Active SDK > Simulator。)

3. 从菜单选择Build > Build and Go (Run),或者简单的点击工具栏上的Build and Go按钮。

当程序构建完成,Xcode把它装入到模拟器中然后开始运行它。使用鼠标,你可以点击Welcome按钮,并把它拖到屏幕的任意位置,查看程序的行为(是否符合需求)。如果你把一个设备设置为开发所用,你可以构建程序并让它在设备上运行。#p#

简述内存管理

iOS主要是一个面向对象系统,所以你分配的大多数内存都是以Objective-C对象的形式存在的。iOS中的对象使用引用计数机制来确定是否可以安全的释放对象占用的内存。当你创建一个对象,它的引用计数从1开始。客户获得对象后可以选择保留它,这样就要把他的引用技术加1。如果客户保留一个对象,那么它也必须在不再需要的时候释放这个对象。释放一个对象就会让它的引用技术减1。当一个对象的引用计数等于0,系统就会自动的回收对象占用的内存。

注意:iOS并不支持Mac OS X v10.5以及更新版本中的垃圾回收型内存管理机制。如果你需要分配普通的内存块,没有跟一个对象想关联的内存块,你可以使用标准的malloc库调用。任何使用malloc函数分配的内存,你都有责任在用完后,调用free函数释放。系统不会帮助你释放基于malloc的内存块。

无论你想如何分配内存,在iOS中管理全部内存使用都比在Mac OS X中更加的重要。虽然iOS有一个虚拟内存系统,但是他不使用交换文件。这就是说如果需要的话,代码页可以被覆盖,但是同时应用程序的数据必须适应内存的尺寸。系统监控着空闲内存的大小,并试图给程序需要的全部内存。但是当内存使用情况太危急的时候,系统可能会终止你的应用程序。但是这个选项仅仅系统是为了确保系统有足够的内存执行最重要的操作,例如接电话,的最后一招。

更多关于在iOS下分配内存的信息,参看Cocoa基础指南。更多关于如何改善程序内存使用的信息,参看iOS编程指南的管理你的内存使用章节。

初始化MoveMe程序

就像每个基于C的程序一样,每个iPhone程序的初始入口点也是main函数。好消息是,当你使用Xcode的iPhone模板创建新项目的时候,你不需要自己写这个函数。项目模板包含了一个带有启动一个程序所有需要代码的这个函数的版本。

列表1展示了MoveMe程序的main函数。这个main函数位于项目中的main.m文件内。你创建的所有程序都会有一个跟这个一样的main函数。这个函数执行两个关键任务。首先,它为程序创建一个最高级的自动释放的内存池,这个内存池可以回收使用autorelease函数释放的 Objective-C对象占用的内存。其次,它调用UIApplicationMain函数创建MoveMe程序的关键对象,初始化这些对象,开始事件处理循环。直到程序退出这个函数才会返回。

列表1 使用提供的main函数

  1. int main(int argc, char *argv[]) 
  2.  
  3.  
  4. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
  5.  
  6. int retVal = UIApplicationMain(argc, argv, nil, nil); 
  7.  
  8. [pool release]; 
  9.  
  10. return retVal; 
  11.  

定义程序的委托对象

你的项目中最重要的架构细节之一就是定义一个程序委托对象,从你提供的类实例化。MoveMe项目中的程序委托类的接口声明在 MoveMeAppDelegate.h文件内,实现定义在MoveMeAppDelegate.m文件内。一旦你把这些文件加入到项目中,你可以使用界面构建器指明这个类作为程序的委托对象。界面构建器是一个可视化工具,用来创建和管理窗口的视图,设置视图之间的层级,配置每个视图的选项,在视图和程序的其他对象间建立联系。因为它是一个可视化工具,你可以通过在窗口上拖动组件完成这些任务。操作的结果是你的界面的交互版本,你可以快速修改,随时看到改变。界面构建器把你的用户界面保存为一个nib文件,这是你的程序对象图形的存档。

要启动界面构建器,并查看程序委托对象的定义,双击Xcode项目窗口中的文件组面板中的MainWindow.xib文件。MainWindow.xib 是一个nib文件,包含了程序的窗口,定义了程序中几个重要对象之间的关系,包括程序的委托对象。要了解程序委托关系是如何建立的,点击nib文件文档窗口(标题为MainWindow.xib)中的File's Owner图标,显示查看器窗口(选择菜单Tools > Inspector),然后点击查看器窗口的连接页。

如图3所示,查看器现实File's Owner对象(表示nib文件中的应用程序)有一个委托出口连接到MoveMeAppDelegate对象。

程序委托

图3 程序委托

程序委托对象与标准的UIApplication对象串联工作,对程序状态改变做出反应。程序对象做大多数的繁重工作,但是委托也负责几项关键的行为,如下:

◆设定程序窗口,初始化用户界面

◆执行定制数据引擎所需的格外初始化任务

◆打开与程序定制的URL模式相关联的内容

◆对设备方向的变化做出响应

◆处理内存不足的警告

◆处理系统要求程序退出的请求

启动期间,对委托对象最紧急的任务就是设定和展现程序窗口,详情在“创建程序窗口”中描述。委托对象还应该为了程序可以立即使用而执行需要的任务,例如从之前的一个状态恢复程序,或者创建需要的对象。当程序退出时,委托对象需要执行有序的程序关闭操作,并保存下次启动循环所需要的信息。#p#

创建程序窗口

每个程序都需要创建窗口覆盖整个屏幕然后在窗口内放置内容。iOS的图形程序不会和其他的程序一起运行。实际上除了核心和一些底层系统守护进程,程序启动后就独占系统了。甚至,你的程序可能最多需要一个窗口,一个 UIWindow类的实例。你希望改变用户界面的时候,你只需要改变窗口上显示的视图即可。

窗口提供了用户界面的描绘空间,但是视图对象提供实际的内容。视图对象是UIView类的实例,描绘内容,并响应对内容的交互行为。iOS定义了标准视图去表现表格,按钮,文本框以及其他类型的交互控件。你可以把这些视图都加入到窗口,或者你可以用继承UIView定义一个定制视图,实现一些定制描绘和事件响应。MoveMe程序定义了两个视图,由MoveMeView和PlacardView两个类实现,显示用户界面,处理用户交互事件。

程序启动期间,目标是创建程序窗口并尽快的显示一些初始内容。窗口从MainWindow.xib文件中展开。当程序达到启动完成状态,准备就绪可以处理事件的时候,UIApplication对象向委托对象发出applicationDidFinishLaunching:消息。这个消息暗示委托对象把内容放入窗口,并执行其他程序所需的初始化操作。

在MoveMe程序中,委托在applicationDidFinishLaunching:消息中做如下工作:

◆创建一个视图控制器对象,管理窗口中的内容视图。

◆使用MoveMeView类的实例初始化试图控制器,这个实例保存在MoveMeView.xib文件内,作为背景视图并填满整个窗口边框。

◆把视图控制器作为子视图加入窗口。

◆显示窗口。

列表2展示了MoveMe程序的applicationDidFinishLaunching:方法,该方法定义在程序委托对象的实现文件MoveMeAppDelegate.m中。这个方法创建了窗口的主内容视图,并使窗口可见。展现窗口让系统了解到,程序已经准备就绪可以处理事件了。

列表2 创建内容视图

  1. - (void)applicationDidFinishLaunching:(UIApplication *)application 
  2.  
  3.  
  4. // Set up the view controller 
  5.  
  6. UIViewController *aViewController = [[UIViewController alloc] 
  7.  
  8. initWithNibName:@"MoveMeView" bundle:[NSBundle mainBundle]]; 
  9.  
  10. self.viewController = aViewController
  11.  
  12. [aViewController release]; 
  13.  
  14. // Add the view controller's view as a subview of the window 
  15.  
  16. UIView *controllersView = [viewController view]; 
  17.  
  18. [window addSubview:controllersView]; 
  19.  
  20. [window makeKeyAndVisible]; 
  21.  

注意:你可以利用applicationDidFinishLaunching:方法执行设定程序用户界面以外的其他任务。许多程序利用它初始化需要的数据结构,读取用户参数,或者返回到上次程序退出时的状态。

虽然前面的代码创建了窗口的背景视图,并显示了窗口,但是在前面的代码中,你无法找到创建PlacardView类显示Welcome按钮的代码。这个行为由MoveMeView类的 setUpPlacardView方法来处理,这个方法在MoveMeView对象从nib文件中展开的时候会被调用。setUpPlacardView 方法在列表3中展示。这个视图的初始化部分包括了PlacardView对象的创建。因为MoveMeView类提供了整个程序的背景,所以它把 PlacardView对象加为子视图。两个视图见的关系不仅令 Welcome按钮显示在程序的背景之上,而且让MoveMeView类可以处理针对按钮的事件。

列表3 创建placard视图

  1. - (void)setUpPlacardView 
  2.  
  3.  
  4. // Create the placard view -- it calculates its own frame based on its image 
  5.  
  6. PlacardView *aPlacardView = [[PlacardView alloc] init]; 
  7.  
  8. self.placardView = aPlacardView
  9.  
  10. [aPlacardView release]; 
  11.  
  12. placardView.center = self.center; 
  13.  
  14. [self addSubview:placardView]; 
  15.  

#p#

描绘Welcome按钮

UIKit提供的标准视图无需修改就可以描绘很多类型的简单内容。例如,你可以用UIImageView类显示图像,用UILabel类显示文本信息。 MoveMe程序的MoveMeView类也得益于UIView对象的基本属性,具体是backgroundColor属性,用颜色填满视图。这个属性可以在视图对象的初始化方法中被设置。这里,这个属性被设置于MoveMeView.xib文件中,使用界面构建器的查看器窗口的属性页的颜色选择器选择颜色。当你需要动态描绘内容,你必须使用在UIKit里的一些更高级的描绘特性,或者你应该使用Quartz或者OpenGL ES。

MoveMe 程序的PlacardView类描绘Welcome按钮并管理按钮在屏幕上面的位置。虽然PlacardView类可以用嵌入UIImageView和 UILabel对象的方法描绘内容,但是这里明确的描绘内容,来显示全部的流程。所以,这个类实现了drawRect:方法,该方法是用来定制描绘的位置。

drawRect:方法被调用时,描绘环境已经配置就绪了。需要做的只是指定描绘命令描绘定制内容。在PalcardView类中,内容包括一个背景图像(保存在Placard.png资源文件内)和一个定制字符串,文本可以动态改变。要描绘内容,该类需要执行下面的步骤:

◆在视图的原点描绘背景图片。(因为视图大小已经调整到符合图片尺寸,这个步骤提供了完整的按钮北京。)

◆计算welcome字符串的位置,令其可以显示在按钮中间。(因为字符串的尺寸可以改变,所以位置每次都需要根据当前的字符串尺寸计算。)

◆设定描绘颜色为黑色。

◆用黑色描绘字符串,位置略有偏移。

◆设定描绘颜色为白色。

◆在正确的位置用白色描绘字符串。

列表4展示 PlacardView类的drawRect方法。成员变量placardImage包含一个带有按钮背景图片的UIImage对象,成员变量 currentDisplayString是一个NSString对象包含了welcome字符串。描绘图像后,这个方法计算字符串相对视图的位置。字符串的尺寸是已知的,在字符串装入的时候计算好并保存在成员变量textSize中。字符串会被描绘两次,一次用黑色一次用白色,使用NSString的drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment:方法。

列表4 描绘Welcome按钮

  1. - (void)drawRect:(CGRect)rect 
  2.  
  3.  
  4. // Draw the placard at 0, 0 
  5.  
  6. [placardImage drawAtPoint:(CGPointMake(0.0, 0.0))]; 
  7.  
  8. /* 
  9.  
  10. Draw the current display string. 
  11.  
  12. This could be done using a UILabel, but this serves to illustrate 
  13.  
  14. the UIKit extensions to NSString. The text is drawn center of the 
  15.  
  16. view twice - first slightly offset in black, then in white -- to give 
  17.  
  18. an embossed appearance. The size of the font and text are calculated 
  19.  
  20. in setupNextDisplayString. 
  21.  
  22. */ 
  23.  
  24. // Find point at which to draw the string so it will be in the center of the view 
  25.  
  26. CGFloat x = self.bounds.size.width/2 - textSize.width/2; 
  27.  
  28. CGFloat y = self.bounds.size.height/2 - textSize.height/2; 
  29.  
  30. CGPoint point; 
  31.  
  32. // Get the font of the appropriate size 
  33.  
  34. UIFont *font = [UIFont systemFontOfSize:fontSize]; 
  35.  
  36. [[UIColor blackColor] set]; 
  37.  
  38. point = CGPointMake(x, y + 0.5); 
  39.  
  40. [currentDisplayString drawAtPoint:point 
  41.  
  42. forWidth:(self.bounds.size.width-STRING_INDENT) 
  43.  
  44. withFont:font 
  45.  
  46. fontSize:fontSize 
  47.  
  48. lineBreakMode:UILineBreakModeMiddleTruncation 
  49.  
  50. baselineAdjustment:UIBaselineAdjustmentAlignBaselines]; 
  51.  
  52. [[UIColor whiteColor] set]; 
  53.  
  54. point = CGPointMake(x, y); 
  55.  
  56. [currentDisplayString drawAtPoint:point 
  57.  
  58. forWidth:(self.bounds.size.width-STRING_INDENT) 
  59.  
  60. withFont:font 
  61.  
  62. fontSize:fontSize 
  63.  
  64. lineBreakMode:UILineBreakModeMiddleTruncation 
  65.  
  66. baselineAdjustment:UIBaselineAdjustmentAlignBaselines]; 
  67.  

当你需要描绘比图像和字符串更复杂的内容的时候,你可以选择Quartz或者OpenGL ES。Quartz和UIKit协作,处理描绘矢量路径,图像,斜线,PDF和其他你想动态创建的复杂内容。因为Quartz和UIKit基于相同的描绘环境,你可以直接在视图的drawRect:方法中调用Quartz函数,甚至可以在UIKit类中混用Quartz。

OpenGL ES是Quartz和UIKit之外的选择,可以让你用类似Mac OS X中OpenGL技术的函数来渲染2D和3D内容。跟Quartz和UIKit不同的是,你不能用视图的drawRect:方法来进行描绘。仍旧是使用视图,但是主要是用视图对象作为OpenGL ES代码的描绘空间。按照什么频率更新描绘空间,使用什么对象,取决于你自己的选择。

#p#

处理触摸事件

iOS的多点触摸接口令你的程序可以识别和响应多个手指触摸设备产生的不同事件。响应多手指触摸的能力带来了强大的能力,但是也带来跟传统的基于鼠标的事件处理系统操作的明显不同。每次手指触摸设备的表面,触摸传感器产生一个触摸事件。每次手指移动,额外的触摸事件产生表明手指的新位置。一旦手指离开设备表面,系统分发另外一个触摸事件表明这一点。

因为同时可能有多个手指触摸设备,这就产生了利用这些事件来识别复杂用户手势的可能性。系统对识别一些常用手势(例如,双击)提供了帮助,但是你可以检测到更复杂的手势。当事件系统产生了一个新的触摸事件,它包含了每个手指当前的状态,包括触摸或者刚离开设备的表面。因为每个事件对象包括所有的活动触摸事件,你可以用新的事件监控每个手指的动作。你可以在事件行为之间跟踪每个手指的移动来检测手势,你可以用来跟程序中的内容交互。例如,如果事件表明用户正在实施手指分开和手指并拢手势,而且下面的视图支持放大,所以你可以用这些时间改变当前的缩放级别。

用触摸事件检测手势

图4 用触摸事件检测手势

系统把事件分发给程序的响应者对象,它是UIResponder类的实例。在iPhone程序中,程序的视图往往是你的定制响应者对象。MoveMe程序实现两个视图类,但是实际上只有MoveMeView类响应事件消息。该类通过覆盖 UIResponder的下列方法,检测在Welcome按钮内外的轻点。

◆ (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

◆ (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

◆ (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

为了简化事件处理行为,MoveMe程序仅跟踪接触设备表面的第一次手指的事件。这可以利用UIView类对此的支持,此类在默认情况下禁用多点触摸事件。对于不需要跟踪多个手指事件的程序,这个特性很方便。当多点触摸事件被禁用的时候,系统仅分发跟第一个接触设备的手指的消息。与其他的触摸相关的事件不会被分发给视图。如果希望得到其他触摸的信息,你可以利用UIView类的 setMultipleTouchEnabled:方法,打开多点触摸支持。

作为事件处理部分,MoveMeView类执行下面的操作:

◆当触摸第一次发生,检查事件发生的位置。

◆双击Welcome按钮外的位置,更新按钮显示的字符串。

◆单击按钮,把按钮的中心移到手指之下,并引发一个动画放大按钮。

◆其他的触摸都忽略。

◆如果手指在按钮之内移动,按钮的位置就会更新以匹配手指。

◆如果手指在按钮内,然后离开设备的表面,按钮以动画的形式回到他的原始位置。

列表5展示MoveMeView类的 touchesBegan:withEvent:方法。手指首次触摸设备的时候系统调用这个方法。方法获得所有的触摸,然后抽出唯一的触摸以及被触摸的物体。UITouch对象中的信息可以说明触摸发生在哪个视图(MoveMeView或者PlacardView)上,以及与之相关的触碰数量。如果触摸是按钮外的双击,touchesBegan:withEvent:方法调用setupNextDisplayString方法改变按钮的欢迎字符串。如果事件发生在Welcome按钮内,使用animateFirstTouchAtPoint:方法放大按钮,并把按钮中心移动到触摸发生的位置上。其他触摸相关的事件都被忽略了。

列表5 处理初步的触摸事件

  1. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
  2.  
  3.  
  4. // We only support single touches, so anyObject 
  5.  
  6. // retrieves just that touch from touches 
  7.  
  8. UITouch *touch = [touches anyObject]; 
  9.  
  10. // Only move the placard view if the touch was in the placard view 
  11.  
  12. if ([touch view] != placardView) 
  13.  
  14.  
  15. // In case of a double tap outside the placard view, 
  16.  
  17. // update the placard's display string 
  18.  
  19. if ([touch tapCount] == 2) 
  20.  
  21.  
  22. [placardView setupNextDisplayString]; 
  23.  
  24.  
  25. return; 
  26.  
  27.  
  28. // Animate the first touch 
  29.  
  30. CGPoint touchPoint = [touch locationInView:self]; 
  31.  
  32. [self animateFirstTouchAtPoint:touchPoint]; 
  33.  

列表6展示MoveMeView类的touchesMoved:withEvent:方法。系统在响应手指已经触摸设备,而且从原始位置移开的时候调用这个方法。MoveMe程序仅跟踪发生在Welcome按钮上的移动。这个方法检查触摸事件的位置,并用之调整Placard对象的中点。视图的系统会引发在新位置的自动重绘。

列表6 响应触摸的移动

  1. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
  2.  
  3.  
  4. UITouch *touch = [touches anyObject]; 
  5.  
  6. // If the touch was in the placardView, move the placardView 
  7.  
  8. // to its location 
  9.  
  10. if ([touch view] == placardView) 
  11.  
  12.  
  13. CGPoint location = [touch locationInView:self]; 
  14.  
  15. placardView.center = location
  16.  
  17. return; 
  18.  
  19.  

当用户的手指离开屏幕,MoveMe用动画的形式把按钮移动回它的原始位置,程序窗口的中点。列表7展示了开始动画的touchesEnded:withEvent:方法。

列表7 释放Welcome按钮

  1. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
  2.  
  3.  
  4. UITouch *touch = [touches anyObject]; 
  5.  
  6. // If the touch was in the placardView, bounce it back to the center 
  7.  
  8. if ([touch view] == placardView) 
  9.  
  10.  
  11. // Disable user interaction so subsequent touches 
  12.  
  13. // don't interfere with animation 
  14.  
  15. self.userInteractionEnabled = NO
  16.  
  17. [self animatePlacardViewToCenter]; 
  18.  
  19. return; 
  20.  
  21.  

为了简化程序的事件处理流程,touchesEnded:withEvent:方法在按钮以动画方式返回原始位置的过程中暂时禁用了新的触摸事件。如果不这么做的话,每个事件处理方法中都需要判断按钮是不是正在动画中,如果是就要终止动画。在按钮返回屏幕中心的时候短时间禁用用户交互,简化了事件处理代码,无非额外的逻辑。当按钮达到他的原始位置,MoveMeView类的 animationDidStop:finished:方法重新启用用户交互,这样事件循环又可以继续下去。#p#

按钮动画移动

在iPhone程序中,动画起了非常重要的作用。动画广泛用于给用户相关信息以及立即响应。例如,当用户在程序中浏览层级数据,iPhone不是仅仅直接用其他的屏幕代替当前的屏幕,而是让屏幕以动画的方式就位。移动的方向用户走向层级的高层还是低层,并给用户可视化的信息这里有新的信息。

因为动画很重要,所以UIKit的类中已经内建了对它的支持。MoveMe程序获益于这种支持,动画改变Welcome按钮的外观。当用户第一次触摸按钮,程序引发一个动画让按钮的尺寸变大。

当用户放开这个按钮,另外的动画产生让按钮回到原始位置。创建这样动画的步骤如下:

◆调用你想动画的视图的beginAnimations:context:方法。

◆设置动画的属性。

◆调用commitAnimations方法开始动画。

列表8展示Welcome按钮第一次被触摸的时候令它动画的代码。这个方法设置了动画的持续时间,并对按钮实施放大操作。当动画结束,动画的机制调用委托的 growAnimationDidStop:finished:context:方法,通过令按钮轻微晃动的方式结束动画,并把placard视图移动到触点。

列表8 让Welcome产生动画

  1. - (void)animateFirstTouchAtPoint:(CGPoint)touchPoint 
  2.  
  3.  
  4. #define GROW_ANIMATION_DURATION_SECONDS 0.15 
  5.  
  6. NSValue *touchPointValue = [[NSValue valueWithCGPoint:touchPoint] retain]; 
  7.  
  8. [UIView beginAnimations:nil context:touchPointValue]; 
  9.  
  10. [UIView setAnimationDuration:GROW_ANIMATION_DURATION_SECONDS]; 
  11.  
  12. [UIView setAnimationDelegate:self]; 
  13.  
  14. [UIView setAnimationDidStopSelector: @selector(growAnimationDidStop:finished:context:)]; 
  15.  
  16. CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2); 
  17.  
  18. placardView.transform = transform; 
  19.  
  20. [UIView commitAnimations]; 
  21.  
  22.  
  23. - (void)growAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 
  24.  
  25.  
  26. #define MOVE_ANIMATION_DURATION_SECONDS 0.15 
  27.  
  28. [UIView beginAnimations:nil context:NULL]; 
  29.  
  30. [UIView setAnimationDuration:MOVE_ANIMATION_DURATION_SECONDS]; 
  31.  
  32. placardView.transform = CGAffineTransformMakeScale(1.1, 1.1); 
  33.  
  34. // Move the placard view under the touch 
  35.  
  36. NSValue *touchPointValue = (NSValue *)context; 
  37.  
  38. placardView.center = [touchPointValue CGPointValue]; 
  39.  
  40. [touchPointValue release]; 
  41.  
  42. [UIView commitAnimations]; 
  43.  

#p#

完成程序

前面的章节,你已经看到MoveMe程序如何初始化,展现用户界面和响应事件。除了这些概念以外,在构建程序并把它装载到设备之前,你还需要了解一些小细节。最后需要考虑的是你的程序的信息属性列表(info.plist)。这是程序用来跟系统通信的一个XML文件。Xcode 为你的程序创建一个默认版本,并把程序的初始化配置信息放在其中。你可以拓展这个信息,为系统提供所需的关于程序的额外细节信息。例如,你使用这个文件通信,关于程序的版本,支持的定制URL模式,启动镜像,默认的视觉状态和系统状态栏的风格。

列表9展示了MoveMe程序的info.plist文件的内容。该文件指明可执行文件的名字,显示在用户主屏幕的图像文件,以及在系统中唯一确定程序的字符串。因为MoveMe程序是一个全屏程序,也就是说,他不显示状态栏,所以它包含了键UIStatusBarHidden并把值设为true。把这个键设置为true令系统得知在启动或者程序运行期间,不要显示程序状态栏。虽然MoveMe程序也可以用编程的手段配置相同的行为,但是那样的话行为只能在程序已经启动后起作用,看起来有点莫名其妙的。

列表9 Info.plist文件的内容

  1. CFBundleDevelopmentRegion 
  2.  
  3. en 
  4.  
  5. CFBundleDisplayName 
  6.  
  7. ${PRODUCT_NAME} 
  8.  
  9. CFBundleExecutable 
  10.  
  11. ${EXECUTABLE_NAME} 
  12.  
  13. CFBundleIconFile 
  14.  
  15. Icon.png 
  16.  
  17. CFBundleIdentifier 
  18.  
  19. com.yourcompany.${PRODUCT_NAME:identifier} 
  20.  
  21. CFBundleInfoDictionaryVersion 
  22.  
  23. 6.0 
  24.  
  25. CFBundleName 
  26.  
  27. ${PRODUCT_NAME} 
  28.  
  29. CFBundlePackageType 
  30.  
  31. APPL 
  32.  
  33. CFBundleSignature 
  34.  
  35. ???? 
  36.  
  37. CFBundleVersion 
  38.  
  39. 1.0 
  40.  
  41. UIStatusBarHidden 
  42.  
  43. NSMainNibFile 
  44.  
  45. MainWindow 

注意:你可以使用文本编辑器编辑info.plist文件的内容,显示为Info.plist文件内容对应的XML内容,或者使用属性列表编辑器,显示为键值对的表格。Xcode也提供了信息窗口访问程序目标的某些属性。要展现这个窗口,选择程序目标然后选择菜单File > Get Info。属性表包含一些info.plist文件中的属性。

现在你已经了解了创建自己的iPhone程序需要的全部基础信息。下面的步骤是学习更多的iOS特性。程序应该利用iOS内建的特性创建直观而且令人愉悦的用户体验。这些特性有些在“深入探讨你的程序”,但是关于完全的特性列表以及如何使用它们的信息,参看iOS编程指南。

深入谈讨你的程序

这里有一些用户可以获得的iPhone和iPod touch的特性。有些特性跟硬件相关,例如根据设备的方向自动调整视图。有些是跟软件相关的,例如所有的内建iPhone应用程序都使用同一个联系人列表。因为下面描述的这些特性跟基本的用户体验有关,所以你应该在原始设计就考虑这些特性是否适用于你的程序。

用加速度传感器跟踪方向和移动

iPhone和iPod touch的加速度传感器可以为系统和你的程序提供输入。一个加速度传感器测量在一个方向上速度的变化。iPhone和iPod touch都有三个加速度传感器用来测量3D空间的每个主要轴向上的变化,允许你检测任何方向的移动。

加速度传感器轴向

图5 加速度传感器轴向

也许你不认为测量加速度变化有啥用处,但是实际上这个信息可以有很大的用处。重力的作用总是把物体拉向地面。这个力的作用造成即使设备是静止不动的,仍旧可以测量到朝向地面的加速度。通过跟踪加速度传感器记录的加速度,以及加速度的大小,你可以精确的检测到设备在3D空间内的方向。你可以把方向作为程序的输入。

系统使用加速度传感器监控设备当前的方向,并在方向改变的时候通知程序。如果程序的用户界面支持横竖两种模式,那么你应该把视图控制器加入到你的设计中。UIViewController类提供了,旋转用户界面以及根据方向改变自动调整视图位置的机制。

如果你想直接访问原始的加速度传感器数据,你可以使用UIKit内的共享对象UIAccelerometer。加速度传感器在一个可以配置的时间间隔下定时报告当前的加速度值。你还可以使用这些数据来检测设备方向或者检测其他的瞬间移动,例如用户前后摇晃设备。你可以把这个信息作为游戏或者其他程序的输入。想要找到如何配置UIAccelerometer对象,以及接收加速度传感器事件的例子,参看iOS编程指南的访问加速度传感器事件章节。

访问用户通讯录

用户的通讯录列表是所有系统程序共享的重要资源。电话,邮件和短信息程序都用它来识别用户要联系的联系人,并使基本交互变得容易,例如打电话,发邮件,或者短信息。你自己的程序也可以为类似的目的访问用户的通讯录信息,或者其他程序需要的相关信息。

访问用户通讯录

图6 访问用户通讯录

iOS提供对用户通讯录的直接访问,以及通过一个标准的选择接口进行的间接访问。使用直接访问,你可以直接用通讯录数据库获得联系人信息。你可以用另外的方式来展现这些联系信息,或者根据程序的需求过滤它们。如果你不需要一个定制接口,iOS也提供了一系列系统接口选择和创建联系人。把这些接口加入你的程序需要一些额外的工作,但是可以令你的程序的外观看起来更像系统的一部分。

获得用户当前的位置

运行iOS的设备是为用户频繁使用而设计的。你所设计的程序需要把这一点纳入考虑之中。互联网和Web让人们可以在任何地方经营自己的事业,所以可以追踪用户当前位置就变成了必备的用户体验了。毕竟,我们不能为一个在洛杉矶口渴的人,列出纽约的咖啡店。这就是Core Location框架的用处。

Core Location框架追踪手机机站和Wi-Fi无线热点的信号,然后使用他们三角定位出用户当前的位置(此文档比较早期,现在已经包括直接获取GPS信号了)。你可以仅用该框架获取一个固定的初始位置,也可以在用户位置发生变化的时候得到通知。利用这些信息,你可以过滤要提供给用户的信息,或者做一些其他的事情。

播放音频和视频

iOS通过Core Audio和OpenAL框架支持你的程序中的音频特性,并使用Media Player框架提供视频回放的能力。你可以用它播放简单的声音效果,或者多个声道的音频,混音,把它们限制在某个音域内,甚至可以使用iPhone的振动功能。如果你是一个游戏开发者,而且你的代码中已经使用了OpenAL的特性,你可以直接在iOS中继续使用这些代码在你的游戏中定位和回放你的音频。

Media Player框架是用来回放全屏视频文件。这个框架支持多种标准的电影文件格式,你可以控制回放的环境,包括是否显示用户控制器,以及视频内容的长宽比。游戏开发者可以利用这个框架播放过场影片或者预先渲染好的内容,而基于媒体的程序也可以用这个框架回放电影文件。

播放视频

图7 播放视频

使用内置相机

iPhone的相机程序允许用户拍摄照片,然后保存在照片库中,和其他从电脑上传来的照片放在一起。虽然iPod touch没有照相机,但是它也有一个用来保存用户上传照片的照片库。iOS用UIKit框架中的UIImagePickerController类来支持这些特性。

iPhone的相机

图8 iPhone的相机

UIImagePickerController类为你的程序提供了相机和照片库的访问接口。包括照相和照片程序的很多程序,使用这些标准的系统接口。当你显示一个图片选择界面,选择控制器UIImagePickerController实现所有所需的用户交互细节,然后返回一个结果图像到你的应用程序。

【编辑推荐】

  1. Windows Phone 7 免费线下培训火热报名中
  2. 我国IPTV研究目全球领先
  3. 3G与IPTV是发展大方向
  4. 我国IPTV研究目全球领先
  5. 3G与IPTV是发展大方向
责任编辑:佚名
相关推荐

2010-08-13 13:05:30

Flex应用程序

2011-03-15 19:45:27

Windows Azu

2013-01-11 14:45:43

iOS开发移动应用iPhone

2011-06-08 10:24:38

Windows Pho 应用程序

2011-03-10 10:45:47

Azure“Hello Worl

2011-06-08 10:01:36

Windows Pho 应用程序

2022-06-07 07:21:19

Python内置库命令行

2011-06-09 09:31:40

Qt 实例

2023-05-19 08:49:58

SQLAlchemy数据库

2009-10-19 14:14:19

OSGi Web应用

2021-07-14 17:39:46

ReactRails API前端组件

2020-10-11 20:54:39

Python开发Docker

2023-09-21 08:00:00

ChatGPT编程工具

2022-02-18 08:43:19

Spring Boo应用程序RabbitMQ

2016-08-04 16:02:34

云计算

2012-12-07 10:15:53

IBMdW

2013-05-13 09:31:29

Web App开发WebApp

2022-10-21 14:21:46

JavaScript笔记技能

2011-07-21 16:19:30

iOS Twitter

2010-01-08 12:14:44

ibmdwAndroid
点赞
收藏

51CTO技术栈公众号