IOS5-精要-全-
IOS5 精要(全)
原文:
zh.annas-archive.org/md5/900fc46a7d3ff1b1bec0b3920950c269译者:飞龙
前言
建立在上一代产品巨大成功的基础上,iOS 5 包含了超过 200 项新的用户功能,以及一个包含超过 1,500 个新 API 的更新版 SDK。iOS 5 看起来将加强 iPhone 在智能手机市场的统治地位。
iOS 5 基础知识将帮助你学习如何构建简单而强大的 iOS 5 应用程序,包括 iCloud 存储、Twitter、Core Image 和 Newsstand 集成。
你将从了解 iOS 5 的新功能开始。你将查看 iCloud 存储 API、自动引用计数、Twitter 和 AirPlay 集成、如何使用 Cocoa 框架中的各种 Core Image 过滤器,以及 iOS 5 SDK 的新特性。在此之后,你将直接开始使用 Xcode 和 Interface Builder,利用新的故事板布局创建应用程序。然后,我们将学习如何使用 Xcode 工具使你的应用程序运行得更加流畅。
在这本书中,我尽力使代码简单易懂。我在每个步骤都提供了详细的步骤说明,并附有大量的截图,以便更容易跟随。你将很快掌握 iOS 5 编程的各个方面,以及创建一些令人惊叹的应用程序所需的技术和技能。如果你有任何疑问,或者只是想打个招呼,请随时通过<geniesoftstudios@gmail.com>联系我。任何关于改进这本书的建议都将受到高度重视。
本书涵盖内容
第一章,iOS5 新特性,向开发者介绍了 Xcode 开发者工具集、iOS 5 的新特性,以及 Newsstand 和 MessageUI 框架的介绍。
第二章,使用 iCloud 和存储 API,介绍了使用 iCloud 的好处,以及如何将 iCloud 功能集成到你的应用程序中,以存储和检索文件及其数据,以及通过存储 API 使用其数据。本章还将为你提供一些关于如何处理多个 iOS 设备上同一文件更新时文件版本冲突的见解。
第三章, 使用 OpenGL ES 进行调试,专注于顶点着色器和片段着色器之间的区别以及它们之间的关系。我们将熟悉 OpenGL ES 2.0 可编程管道,并探讨 OpenGL ES 的新调试功能,这些功能使我们能够在 Xcode IDE 中追踪特定于 OpenGL ES 的问题。我们将了解更多关于 OpenGL ES 帧捕获工具及其停止程序执行的能力,这将使开发者能够捕获 iOS 设备上正在渲染的当前帧内容,以便通过仔细查看程序状态信息来追踪和纠正程序问题,通过滚动调试导航器堆栈跟踪,并能够查看应用程序当前正在使用的所有纹理和着色器。
第四章, 使用 Storyboard,帮助你理解 Storyboard 是什么以及我们如何应用视图之间的各种过渡。我们将探讨如何创建和配置场景和 Storyboard 文件,以程序化地展示这些内容。我们还将了解如何将 Twitter 功能集成到我们的应用程序中,以便发布照片和标准消息。
第五章, 使用 AirPlay 和 Core Image,专注于学习 AirPlay 和 Core Image 框架,以及我们如何使用和将这些框架集成到我们的应用程序中。本章还解释了不同的图像过滤效果,如何调整图像的亮度,以及如何制作水波纹效果。它还涵盖了如何将 AirPlay 功能集成到你的应用程序中,以便你的应用程序可以在外部设备上显示,例如 Apple TV。
第六章, Xcode 工具 - 改进,专注于学习对 Xcode 开发工具所做的改进。我们将探讨自动引用计数(ARC),这是 LLVM 编译器最新添加的功能,以及它如何通过最小化应用程序的问题来提高应用程序的性能。它还涵盖了 Interface Builder、iOS 位置模拟器和 OpenGL ES 调试工具集的改进。
第七章, 使用 Instruments 使您的应用程序运行流畅,重点介绍我们如何在应用程序中有效地使用 Instruments 来追踪可能导致应用程序在用户的 iOS 设备上崩溃的内存泄漏和瓶颈。我们将探讨 Instruments 应用程序中包含的每种不同类型的内置工具,学习如何使用系统跟踪工具来监控系统调用,并追踪应用程序中的性能问题。
您需要这本书的内容
本书假设您使用的是基于 Intel 的 Macintosh 运行 Snow Leopard(Mac OS X 10.6.2 或更高版本)。您可以使用 Leopard,但我强烈建议升级到 Snow Leopard 或 Lion,因为这两个操作系统才有许多仅在 Xcode 中可用的新功能。
我们将使用 Xcode 4.2.1,这是用于创建 iOS 开发应用程序的集成开发环境。您可以通过以下链接下载 Xcode 的最新版本:developer.apple.com/xcode/.
这本书面向的对象
如果您想了解 iOS 5 的最新功能,并学习如何将 Twitter、iCloud 和 Core Image 框架效果功能集成到您的应用程序中,那么这本书适合您。您应该具备良好的 Objective-C 编程经验,并使用过 Xcode 4。iOS 编程经验不是必需的。
术语
在这本书中,您将找到许多不同风格的文本,以区分不同类型的信息。以下是一些这些风格的示例及其含义的解释。
文本中的代码单词显示如下:“从 /Developer/Applications 文件夹启动 Xcode。”
代码块设置如下:
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
@interface MyEmailAppViewController: UIViewController<MFMailComposeViewControllerDelegate> {}
@end
当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>AXEUZ3F6VR.com.geniesoftstudios</string>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>TEAMID.com.yourcompany.iCloudExample</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>TEAMID.com.yourcompany.iCloudExample</string>
<key>get-task-allow</key>
<true/>
</dict>
</plist>
新术语和重要词汇以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“当启动 Xcode 时,您应该看到欢迎使用 Xcode屏幕。”
注意
警告或重要注意事项以如下框显示。
小贴士
小贴士和技巧看起来像这样。
我们始终欢迎读者的反馈。请告诉我们您对这本书的看法,您喜欢或可能不喜欢的地方。读者的反馈对我们开发您真正能从中获得最大收益的标题非常重要。
要向我们发送一般反馈,只需发送一封电子邮件到<feedback@packtpub.com>,并在邮件的主题中提及书名。
如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在您是 Packt 书籍的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大收益。
下载示例代码
您可以从您在www.packtpub.com的账户下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support,注册以直接将文件通过电子邮件发送给您。
错误
尽管我们已经尽一切努力确保我们内容的准确性,但错误仍然可能发生。如果您在我们的书中发现错误——可能是文本或代码中的错误——如果您能向我们报告,我们将不胜感激。这样做可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何错误,请通过访问www.packtpub.com/support,选择您的书籍,点击错误提交表单链接,并输入您的错误详情来报告它们。一旦您的错误得到验证,您的提交将被接受,错误将被上传到我们的网站,或添加到该标题的错误部分现有的错误列表中。
盗版
互联网上对版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过<copyright@packtpub.com>联系我们,并提供疑似盗版材料的链接。
我们感谢您在保护我们的作者以及为我们提供有价值内容的能力方面的帮助。
询问
如果您在本书的任何方面遇到问题,可以通过<questions@packtpub.com>联系我们,我们将尽力解决。
第一章:iOS5 的新功能
欢迎来到苹果公司移动操作系统的最新版本 iOS 5 的激动人心的世界,它包含了许多新的功能和改进,改变了事物的处理方式。2010 年发布的 iPhone 4,让全世界为之疯狂。世界各地的开发者都在拥抱新功能,例如在他们的应用程序中集成 AirPlay 功能,利用视网膜显示屏在他们的应用程序和游戏中提供清晰的高清图形,以及加速度计和陀螺仪。
当苹果公司在 2011 年 6 月举办年度全球开发者大会时,他们介绍了 200 多个新功能,以及一个更新的 SDK,该 SDK 具有超过 1,500 个新的开发 API。这为许多新的应用程序以及我们目前做事的方式提供了很多新的想法。一些令人兴奋的新功能亮点包括使用新的通知中心来支持处理通知消息的方式,通过使用新的iMessage消息应用程序大大改进了消息传递,最后,通过使用新的新闻摊应用程序来组织和购买所有报纸和杂志订阅。
在本章中,您将深入了解最新 iOS 5 版本中集成的某些令人惊叹的新功能和改进。我们还将探讨如何下载和安装 Xcode 开发者工具和软件开发包(SDK)。
在本章中,我们将:
-
了解 iOS 5 带来的新功能
-
下载并安装 Xcode 开发工具
-
使用 Newsstand 框架的功能创建一个简单的应用程序
-
使用
MessageUI框架创建一个简单的发送电子邮件的应用程序 -
删除 Xcode 开发工具
我们面前有一个令人兴奋的旅程,让我们开始吧。
iOS 5 的新功能有哪些
自从 2007 年 6 月苹果公司发布 iOS 操作系统以来,他们在每个操作系统的发布版本中集成了许多新功能和改进。在 iOS 4 中,我们看到了超过 1,500 个新的 API,以及一些高质量的提升和改进。
在 iOS 5 中,苹果公司引入了 200 多个新功能和改进,以及 1,500 个新的 API 和 SDK 更新,包括与 Core Image、Twitter 集成和新闻摊套件相关的新功能。
不言而喻,Xcode 4 开发环境也进行了一些改进,以便您的应用程序可以使用支持自动引用计数(ARC)的新 LLVM 编译器 3.0 进行编译。因此,您很少需要保留或释放对象,因为 ARC 为您做了大部分工作。在某些情况下,您仍然需要使用保留/释放。Storyboard 支持也已集成到 Interface Builder 中,这使得您可以为每个视图设计多视图工作流程。
最后,调试 OpenGL ES 项目要容易得多,因为它们已经集成到 Xcode 调试界面中。
在接下来的章节中,我们将详细探讨 iOS 5 带来的新功能。
提醒事项
作为此次发布的一部分,有一个整洁的新功能叫做提醒应用。将提醒想象成待办事项列表是一个不错的选择。提醒事项可以证明是救命稻草,因为它们为您提供了组织日常任务的灵活性,并且具备添加截止日期和位置的能力。
当您设置提醒以使用位置时,您可以指定在特定日期或位置提醒,也可以选择在到达或离开该位置时提醒。它们利用您的手机 GPS,类似于您的汽车导航系统的工作方式,并设计为在您接近指定区域时立即提醒您。让我们举一个例子,比如说,您想买一台新的打印机和一些额外的墨水,您可以将提醒设置成在您驶入当地商店或停车场时自动发送警报。
最后,关于提醒事项还有一点要提:自从这些功能集成到 iOS 5 以来,它们已经被设计得能够很好地与其他应用程序协同工作。例如,Apple iCal、Microsoft Outlook 和 iCloud。这样做是为了确保您所做的任何更改都将自动更新到您所有的设备和日历上。
以下截图显示了一个待办事项列表中添加的项目列表,然后展示了您如何配置和指定何时提醒。您可以选择在到达或离开特定位置时提醒。最后一个截图显示了在指定时间到达时的提醒弹出窗口。可以通过选择突出显示的矩形中的+加号来向列表中添加更多项目。

通知中心
通知在 iPhone 用户的日常生活中扮演着重要的角色。通知以弹出窗口的形式出现,通知您新邮件已到达、新的短信文本消息、来自社交网络网站的好友请求、当您的手机余额低于一定金额时的通知等等。有了通知中心应用程序,您不必担心寻找那封电子邮件、短信文本消息或好友请求。它已经足够简单,让您能够在一个方便的位置跟踪所有这些通知形式。
您可以通过将手指放在屏幕顶部任意位置并向下滑动来访问通知中心。在此视图中,您可以选择查看当前的天气预报、股票份额、即将到来的预约的日历条目等等。随着新通知的到来,它们将被添加并出现在列表顶部,以便更容易访问,而不会打断您正在做的事情。
您还可以通过 iOS 设备的锁定屏幕对通知进行操作;它们在表格视图中分类显示,这样您可以通过简单地滑动面板解锁并进入相关应用程序来快速处理它们。例如,如果您收到一条消息,这将打开iMessage应用程序。如您所见,通知中心为您提供了更好地跟踪您生活活动的方式。

新闻亭
新闻亭是一个中心位置,iOS 用户可以在这里访问他们订阅的杂志和报纸。与 iBooks 不同,在 iBooks 中,图书出版商提供.epub文件或类似的文档,新闻亭的出版商将不得不创建一个 iOS 应用程序(或修改他们现有的应用程序)。想象一下,它就像 iBooks 应用中看到的架子与主屏幕上的应用程序文件夹之间的交叉。
为了使用新功能,出版商必须调用新添加的新闻亭框架。需要配置一些简单的设置,以便您的应用程序能够识别它是一份杂志或报纸,这样它就可以在新闻亭应用程序中运行,而不是作为一个独立的应用程序运行。
在下一节中,我们将继续下载和安装 iOS 5 SDK。如果您已经安装了它,您可以完全跳过这一节,直接进入下一节。
获取和安装 iOS 5 SDK
在我们开始构建 iOS 应用程序之前,您必须首先在developer.apple.com/programs/ios/注册为注册的 iOS 开发者。注册过程是免费的,并为您提供访问 iOS SDK 和其他对您开始开发非常有用的开发者资源的权限。
一旦您注册,您就可以下载 iOS SDK,如下面的截图所示。在下载 iOS SDK 之前,确保您的机器满足以下系统要求可能是值得的:
-
只支持 Intel Mac,所以如果您有其他处理器类型(如较老的 G4 或 G5 Mac),那么您就没有运气了
-
您已使用最新的 Mac OS X 软件更新更新了系统,无论是 OS X Lion 还是 Snow Leopard
注意
如果您想为 iPad 和 iPod Touch 开发应用程序,您仍然可以使用 iOS SDK,因为它们与 iPhone 使用相同的 操作系统(OS)。此 SDK 允许您创建适用于运行 iOS 4 及以上版本的 iPhone 和 iPad 的通用应用程序。

下载 SDK 后,您可以继续安装它。您将需要接受一些许可协议。然后,您将看到一个屏幕,用于指定安装 SDK 的目标文件夹:

如果您在安装阶段选择默认设置,各种工具(将在后面详细解释)将被安装在 /Developer/Applications 文件夹中。
安装过程将引导您通过自定义安装选项屏幕。如果您安装过其他 Mac 软件,您可能已经看到了类似的屏幕。以下截图显示了您将在这里看到的内容:

这些选项使您对安装过程有更多的控制。例如,您可以选择安装 Xcode 的文件夹位置,以及各种其他选项的设置。
iOS 5 SDK 是 Xcode 开发工具下载的一部分,您可以在 developer.apple.com/devcenter/ios/index.action 找到。
SDK 包含以下组件:
-
Xcode: 这是主要的 集成开发环境(IDE),它使您能够管理、编辑和调试您的项目
-
Dashcode: 这使您能够开发基于 Web 的 iOS 应用程序和仪表板小工具
-
iOS 模拟器: 这是一个基于 Cocoa 的应用程序,它提供了一个软件模拟器,可以在您的 Mac OS X 上模拟 iOS 设备
-
仪器: 这些是帮助您优化应用程序并实时监控内存泄漏的分析工具
以下截图显示了在安装阶段作为默认设置安装的各种工具列表。这些工具安装在 /Developer/Applications 文件夹中:

在下一节中,我们将探讨如何利用 Newsstand Kit 框架的强大功能,使开发者能够开发一个将项目添加到我们的 Newsstand 的应用程序,而不是作为一个独立的 iOS 应用程序启动。
创建 MyMagazineArticle 应用程序
在我们开始创建 MyMagazineArticle 应用程序之前,我们必须首先启动 Xcode4.2 开发环境。双击位于 /Developer/Applications 文件夹中的 Xcode 图标。
或者,您可以使用 Spotlight 搜索此内容:只需在搜索框中输入 Xcode,Xcode 应该会显示在顶部列表中。当 Xcode 启动时,您应该会看到欢迎使用 Xcode屏幕,如下面的截图所示。
可能值得将 Xcode 图标停靠到您的 Mac OS X 启动栏上以便于访问,因为我们将在这本书的整个过程中大量使用它。

在 Xcode 中创建 MyMagazineArticle 应用程序非常简单。只需按照这里列出的步骤操作:
-
选择创建一个新的 Xcode 项目,然后在左侧选择iOS 应用程序。
-
从项目模板对话框中选择基于页面的应用程序模板。
![创建 MyMagazineArticle 应用程序]()
-
然后,点击下一步按钮以进入向导的下一步,这将允许您输入产品名称和您的公司标识符。
注意
您的应用程序的公司标识符需要是唯一的。苹果建议您使用反向域名样式(例如,
com.DomainName.AppName)。![创建 MyMagazineArticle 应用程序]()
-
在产品名称中输入
MyMagazineArticle,并在公司标识符字段中输入一个唯一的标识符,确保您已从设备家族下拉框中选择iPhone。 -
然后,点击下一步按钮以进入向导的最后一步。
-
选择您想要保存项目的文件夹位置。
![创建 MyMagazineArticle 应用程序]()
-
然后,点击创建按钮以在指定位置保存您的项目。
一旦创建好项目,您将在项目导航窗口中看到 Xcode 开发界面以及模板为您创建的项目文件。
将 Newsstand Kit 框架添加到我们的项目中
现在我们已经创建了项目,我们需要将 Newsstand Kit 框架添加到我们的项目中。这是一个重要的框架,它为我们提供了将我们的应用程序显示在最新 iOS 5 版本提供的 Newsstand 应用程序中的能力。
要将此框架和其他框架添加到项目中,请选择项目导航组,然后按照这里概述的简单步骤操作:
-
在项目导航窗口中选择您的项目。
-
在TARGETS组下选择您的项目目标。
-
选择构建阶段选项卡。
-
展开链接二进制与库的展开三角形。
-
在列表中向下滚动并选择NewsstandKit.framework,然后点击添加按钮将该条目添加到我们的项目中。您可以使用+按钮添加您想要添加的库;要删除框架,从组中选择它,然后点击按钮。如果您在显示的列表中找不到它,还有搜索框架的能力。
-
如果你仍然不清楚如何添加
NewsstandKit.framework,你可以参考以下截图,其中突出显示了你需要选择的部分(由矩形突出显示)。

向我们的应用程序添加属性
现在我们已经将 NewsstandKit.framework 添加到我们的项目中,我们的下一步是开始添加一些属性,使我们的应用程序在新闻亭应用程序中显示。
与其他 iOS 应用程序不同,你创建的新闻亭应用程序将仅出现在新闻亭应用程序中,而不是像当前 iOS 应用程序那样显示在用户的首页上。而不是显示应用程序图标,应用程序将显示封面和一些新闻亭提供的附加信息。当用户点击你的应用程序封面时,它将自动启动你的应用程序并向他们展示与该文章相关的信息。
创建使用新闻亭套件的应用程序需要在你的应用程序和托管你内容的服务器之间进行通信。你的服务器负责在有任何新的更新或发布可用时通知应用程序,通常使用 推送 通知。
注意
关于推送通知的更多信息,请参阅 Apple 开发者连接文档,该文档可在以下地址找到:developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction/Introduction.html。
为了使我们的应用程序表现得像新闻亭应用程序,并出现在架子上,需要三个步骤。这些步骤是:
-
将新闻亭套件框架添加到你的项目中。
-
在你的 MyMagazineArticle-Info.plist 文件中包含 UINewsstandApp 键,以指示它支持新闻亭。这可以通过单击 MyMagazineArticle-Info.plist 文件,然后在面板中心右键单击,并从弹出列表中选择 Add Row 来实现,如下面的截图所示:
![向我们的应用程序添加属性]()
-
包含具有 newsstand-content 值的 Required background modes 键,以便在后台启动应用程序,以便它可以开始下载最新内容。这可以通过单击 MyMagazineArticle-Info.plist 文件,然后在面板中心右键单击并从弹出列表中选择 Add Row 来实现。
以下截图显示了需要分配给选项,使其出现在新闻亭文件夹中:

如果您的新闻架应用在MyMagazineArticle-Info.plist文件中的newsstand-content值内包含所需的背景模式键,您的新闻架应用将在后台启动,以便开始下载最新版本的杂志或报纸文章。
下载过程由系统自行管理,并在内容完全下载并可供使用时通知您的应用。
为了使我们的应用出现在新闻架文件夹中,您需要在CFBundlePrimaryIcons下创建一个名为CFBundleIconFiles的数组条目,以包含您的标准应用图标。
您还需要创建一个包含CFBundle的UINewsstandIcon,因为这是您设置报纸或杂志封面以及指定装订类型的地方,这会给您的应用赋予图标形状和装订边缘。
在以下屏幕截图中,展示了如何通过将UINewsstandBindingType属性更改为UINewsstandBindingTypeNewspaper来自定义我们的应用,使其显示为报纸:

一旦您在应用程序的.plist文件中创建了这些条目,并确保您已将实际的图标.png文件添加到项目中,您就可以准备编译、构建和运行您的应用了。以下屏幕截图将显示,我们的应用已成功添加到Newsstand文件夹应用中,其图标已更改为显示为报纸文章。

当将图标添加到您的项目中时,注意图标的大小非常重要。这取决于您是否为 iPhone 或 iPad 开发此应用。以下表格列出了图标的相关名称、大小和平台。
| 图像名称 | 尺寸(像素) | iOS 平台 |
|---|---|---|
Icon.png |
57x57 |
通用应用图标 |
Icon-72.png |
72x72 |
iPad |
Icon-64.png |
64x64 |
iPad |
Icon-32.png |
32x32 |
iPad/iPhone |
Icon-24.png |
24x24 |
iPad/iPhone |
Icon-16.png |
16x16 |
iPad/iPhone |
如果我们将UINewsstandBindingType属性改回UINewsstandBindingTypeMagazine,它将显示我们的图标为杂志封面。

一旦您修改了应用程序的.plist文件中的此条目,您就可以准备编译、构建和运行此应用了。以下屏幕截图将显示,我们的应用已成功添加到Newsstand文件夹应用中,其图标已更改为显示为杂志封面。

所以这就是全部内容。正如您所看到的,通过在您的应用程序的.plist文件中添加一些简单的属性,您可以自定义应用程序,使其图标显示为杂志封面,或报纸文章。
注意
需要提到的一个重要事项是,Newsstand 应用程序必须在项目的.plist文件中包含UINewsstandApp键,以表明它支持 Newsstand 功能。如果没有这样做,您的应用程序将显示为普通应用程序,将在用户的首页上显示。
创建 MyEmailApp 应用程序
在您的应用程序内发送电子邮件确保您在发送电子邮件后不需要重新启动应用程序。这可以是一件好事,因为它使您的应用程序用户友好,使用户可以在不重新启动应用程序的情况下继续使用它。
在本节中,我们将使用MessageUI框架创建一个简单的应用程序,允许在应用程序内发送电子邮件,而无需用户退出您的应用程序然后重新启动它。
我们还将探讨如何自动填写收件人、主题和消息正文字段,最后我们将看到如何访问和自定义导航栏颜色以适应您的应用程序。要了解如何实现这一点,只需按照以下简单步骤操作:
-
从
/Developer/Applications文件夹中启动 Xcode。 -
然后,从项目模板对话框中选择单视图应用程序模板:
![创建 MyEmailApp 应用程序]()
-
点击下一步按钮,继续向导中的下一个步骤。
-
通过填写产品名称和公司标识符字段为您的项目提供一个名称。
![创建 MyEmailApp 应用程序]()
-
在产品名称中输入
MyEmailApp。 -
确保从设备家族下拉框中选择iPhone。
-
点击下一步,继续向导中的最后一步。
-
选择您想要保存项目的文件夹位置。
-
然后,点击创建按钮以在指定的位置保存项目。
将 MessageUI 框架添加到我们的项目中
现在我们已经创建了项目,我们需要将MessageUI框架添加到项目中。这是一个重要的框架,将为我们提供发送电子邮件消息的能力。
要添加此框架,请按照以下简单步骤操作:
-
在项目导航窗口中选择您的项目。
-
然后,从TARGETS组下选择您的项目目标。
-
选择构建阶段选项卡。
-
使用库展开三角符号展开链接库。
-
然后,在列表中向下滚动并选择
MessageUI.framework,然后点击添加按钮将该项目添加到我们的项目中。您可以使用+按钮添加您想要添加的库;要删除框架,从组中选择它,然后点击按钮。 -
如果你仍然不清楚如何添加MessageUI.framework,可以参考以下图片,该图片突出显示了需要选择的部分(由矩形突出显示)。

构建 TheMyEmailApp 用户界面
在本节中,我们将开始构建MyEmailApp的用户界面。我们需要包含MessageUI框架的头文件信息,这是我们之前章节中添加的。
这公开了所有函数方法和参数调用。要了解如何实现这一点,请按照以下简单步骤操作:
-
现在我们已经添加了所需的框架,我们的下一步是将框架头文件导入到我们的
MyEmailAppViewController.h头文件中,如下所示:#import <MessageUI/MessageUI.h> -
接下来,在资源文件夹下,打开
MyEmailAppViewController.xib文件,然后从对象库中拖动一个UIButton控制,并通过其文本属性设置按钮标题以显示Send Email,或者您可以双击按钮并输入Send Email文本。我们需要创建一个方法动作事件,当按下此按钮时将执行。
-
在
MyEmailAppViewController.m实现文件中,添加以下代码:- (IBAction)composeEmail{ MFMailComposeViewController *controller = [[MFMailComposeViewControlleralloc] init]; [selfpresentModalViewController:controlleranimated:YES]; controller.mailComposeDelegate = self [controller release]; } -
这创建了一个
MFMailComposeViewController对象,并将我们设置为代理,以便我们可以接收回调。 -
在我们发送消息后,我们需要关闭电子邮件窗口视图。为此,我们需要实现一个代理处理程序到我们的邮件编写视图控制器
MFMailComposeViewControllerDelegate。这设置了您的应用程序视图控制器作为代理,以便在用户发送或取消电子邮件时通知您。 -
打开
MyEmailAppViewController.h接口文件,然后添加以下代码:#import <UIKit/UIKit.h> #import <MessageUI/MessageUI.h> @interface MyEmailAppViewController: UIViewController<MFMailComposeViewControllerDelegate> {} @end -
我们现在需要实现一个回调方法,当用户发送或取消电子邮件时,将用于关闭视图控制器。
-
打开
MyEmailAppViewController.m实现文件,并添加以下代码:- (void)mailComposeController:(MFMailComposeViewController*) controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { [selfdismissModalViewControllerAnimated:YES]; }
自动填充字段
到目前为止,我们已经添加了足够的程序逻辑,使我们的应用程序能够正确运行,但这不允许某些字段自动填充,因此这些需要手动填写。为此,我们首先需要添加一些额外的代码。
-
打开
MyEmailAppViewController.m实现文件,并将以下代码添加到composeEmail方法中,如下所示:[controllersetSubject:@"Program Bug"]; [controllersetToRecipients:[NSArrayarrayWithObject: [NSStringstringWithString:@"YourEmail@companyname.com"]]]; [controllersetMessageBody:@"An application error has occurred within module XcodeFunctions.m" isHTML:NO]; [controller.navigationBarsetTintColor:[UIColorredColor]]; [selfpresentModalViewController:controlleranimated:YES]; controller.mailComposeDelegate = self; [controller release]; -
还可以选择更改位于电子邮件窗口顶部的导航栏的颜色。为了实现这一点,我们需要使用
navigationBar控制的setTintColor方法。您需要将其添加到composeEmail方法中,就在读取到[self presentModalViewController:controller:]的行之前。[controller.navigationBarsetTintColor:[UIColorredColor]]; [selfpresentModalViewController:controlleranimated:YES];
在本节中,我们成功地将代码添加到我们的电子邮件草稿表中,以预填充默认项目详情,并查看我们如何设置导航栏的颜色。在下一节中,我们将探讨如何构建和运行我们的应用程序。
构建和运行 MyEmailApp 应用程序
一旦您实现了前面的代码,就是时候编译、构建和运行您的应用程序,以查看它如何工作了。下面的截图显示了在 iOS 模拟器中运行的MyEmailApp应用程序,所有字段都已填充:

因此,这就是全部内容。您已经成功使用MessageUI框架构建了一个应用程序,该应用程序可以发送新的电子邮件消息。当您按下发送电子邮件按钮时,它将在您的应用程序中直接显示新的电子邮件视图控制器窗口,所有字段都已预先填充,并且导航栏的颜色设置得当。在最后一张截图中所显示的动作表单会在您按下取消按钮时显示。
iMessage
iMessage是我们已经熟悉并喜爱的现有消息应用程序的一个集成插件。iMessage 允许您轻松地向运行 iOS 5 的 iOS 设备上的个人或一组人发送文本消息、照片、视频或其他内容,这些设备可以通过 Wi-Fi 或 3G 连接。
这些消息也会自动推送到您的所有其他 iOS 设备,这使得在所有设备上维护一个会话变得更加容易。当使用 iMessage 发送消息时,您的手机会自动检查您要发送的人是否运行 iOS 5,如果是这种情况,它会发送一个 iMessage 消息而不是标准的 SMS 文本。
到目前为止,您的通讯录将被更新,并在联系人的名字旁边出现一个小的蓝色聊天气泡,以表明他们可以接收 iMessages。如果该人没有运行 iOS 5,那么通讯录将更新为一个绿色的聊天气泡。在下面的截图中,它显示了 iMessages 功能,并以小蓝色气泡显示会话,以表明两个人都在运行 iOS 5:

下面的项目符号总结了使用 iMessage 而不是标准消息组件的一些优点。
-
iMessage 将消息应用程序扩展到所有运行 iOS 5 的 iOS 设备,包括 iPhone、iPod touch 和 iPad。消息会推送到您的所有设备,因此如果您在 iPhone 上开始一个会话,您可以在任何 iOS 设备上继续并接收到它。
-
iMessage 服务集成在消息应用中,因此用户可以免费向拥有 iOS 设备的家人和朋友发送无限量的文本、照片、视频、联系人、位置和群组消息。值得一提的是,虽然这项功能不会产生任何短信费用,但它确实会使用你的带宽分配,而且根据你的使用量,可能会让你花费更多。
-
iMessage 可以让你轻松地通过类似即时通讯应用中看到的省略号形式,了解某人是否正在回复你的消息。
-
iMessage 可选地允许你通过投递回执和已读回执跟踪你的消息。
-
你可以通过 Wi-Fi 以及 3G 发送消息。
-
通过 iMessage 发送的消息在空中进行加密。
iPhone 摄像头
在这次最新版本中,iPhone 的另一个组件也得到了更新,那就是 iPhone 摄像头。由于摄像头是捕捉那些特殊和意外时刻最广泛使用的应用程序,苹果公司使这一功能更加易于访问。
此应用程序现在可以直接从你的 iPhone 锁屏访问,并具有以下改进功能:
-
网格线:这些线非常有帮助,可以确定相机是否水平,以确保你每次都能拍出完美的照片,通过使用地平线或建筑物的边缘等物体。
-
捏合缩放手势:此功能允许你直接在摄像头应用程序中手动放大和缩小,而不是使用屏幕底部的滑块。
-
单指点击对焦:此功能允许你将焦点和曝光锁定在屏幕上的一个区域。只需在屏幕上的任何位置轻触你的手指即可。
-
曝光锁定以即时构图图片:此功能允许你通过在屏幕上放置并保持手指,简单地锁定你的图像的焦点和曝光。
在 照片 应用程序中也增加了新的照片编辑改进,以便你可以直接在照片应用程序中操作你的图像,无论是裁剪或旋转图像,还是提供照片增强,例如从你的照片中移除红眼,所有这些操作都在照片应用程序内完成。
如果你正在使用 iCloud(我们将在 第二章 中介绍,使用 iCloud 和存储 API),你也可以自动将新照片加载到你的电脑桌面上,如果你更喜欢在那里使用你喜欢的照片编辑工具进行编辑。

PC 无需
随着 iOS 5 中新推出的 PC 无需 功能,用户可以在无需电脑的情况下设置和激活他们的设备。任何新的 iOS 软件更新都将直接部署到你的 iOS 设备上,以及你在设备上通过 iTunes 或 App Store 进行购买的任何内容。这些内容将通过 Wi-Fi 使用 安全套接字层(SSL)安全地传输回你的 iTunes 库。
一旦您正确设置和配置了 iCloud,您的备份和恢复将自动为您完成,并存储在云端,这使得您更容易将任何 iOS 更新部署到使用相同 Apple ID 的每个 iOS 设备或计算机。
以下截图显示了如何设置您的手机、从 iCloud 备份或从您的 iTunes 库中恢复设备选项。完成此操作后,您将收到一个最终的感谢屏幕,您可以在其中开始使用您的 iOS 设备。

Wi-Fi 同步
在 iOS 5 中,苹果为用户提供了更简单的方式,通过共享 Wi-Fi 连接无线同步所有 iOS 设备直接回您的 Mac 或 PC,无需像以前那样直接连接到您的计算机。
发生的情况是,每次您决定为 iOS 设备充电时,它将自动搜索任何新购买的项目或添加到设备中的项目,然后自动将其同步回您的 iTunes 库。
以这种方式,您将始终有一个备份副本,包括所有电影、珍贵的家庭视频和照片专辑,您可以在需要时随时访问。
多任务手势
很遗憾,这些功能从未在 iOS 4 版本中实现;它们仅包含在 iOS 4 SDK 开发者版本中,但最终在 iPad 上运行得非常好。在 iOS 5 中,这得到了极大的改进,并增加了一些新功能,使访问内容变得更加容易。苹果的工程师使其更加简单,尽可能少地移动就能轻松导航。他们还添加了快捷菜单,以帮助您在 iPad 上更快地导航。
这些手势是通过使用四个或五个手指,向上滑动以显示多任务栏,并使用捏合动作返回主页屏幕来实现的。甚至增加了左右滑动以在应用之间切换的支持。

卸载 Xcode 开发者工具
如果您想卸载 Xcode(在安装过程中出现错误或您只想卸载 Xcode 开发者工具的情况下),这是一个非常简单的过程。打开一个终端窗口实例并运行 uninstall-devtools 脚本:
sudo <Xcode>/Library/uninstall-devtools --mode=all
<Xcode> 是安装工具的目录。对于典型安装,完整路径是 /Developer/Library/uninstall-devtools。
<sudo> 是一个系统管理员命令,在继续之前需要您输入管理员密码。
注意
在您继续之前,请确保这是您真正想要做的,因为一旦删除,它将永久删除。无论如何,您总是可以选择重新安装 Xcode 开发者工具。值得检查 /Developer/Library/Xcode/ 文件夹是否也已删除。如果没有,只需将其移动到垃圾桶。
摘要
在本章中,我们了解了 iOS 5 的新特性,以及如何下载和安装 iOS 5 SDK,以及熟悉一些 Xcode 开发工具。
然后,我们继续前进,探讨了如何使用 Newsstand Kit 框架构建一个简单的 Newsstand 应用程序,以展示我们如何将报纸和杂志添加到 Newsstand 应用程序文件夹中。接下来,我们探讨了如何使用MessageUI框架构建一个简单的电子邮件应用程序,并学习了如何自定义导航栏 UI 以设置背景颜色。
为了结束本章,我们回顾了如何使用命令行卸载 Xcode 开发者工具的步骤。
在下一章中,我们将学习我们经常听说的iCloud究竟是什么,并且我们将关注作为 iCloud 一部分的存储 API。我们将探讨如何创建一个 iCloud 应用程序来在云中存储文档和键值数据,如何将备份到云中,最后看看我们如何处理文件版本冲突。
第二章:使用 iCloud 和存储 API
在本章中,我们将介绍 iCloud 的功能及其存储 API。这些 API 允许您开发应用程序,以便在公共和中央位置编写和存储用户文档,并能够从所有电脑和 iOS 设备访问这些项目。
通过 iCloud 使用户的文档可用意味着用户可以直接从任何设备查看或编辑这些文档,无需同步或传输这些文件。在用户的 iCloud 账户中存储文档为用户提供了一个额外的安全层。即使用户丢失了设备,只要文档包含在 iCloud 存储中,就可以轻松检索。
通过使用 iCloud 存储 API,您可以使您的应用程序足够强大,能够存储用户文档和键值数据,允许这些信息及其更改同时推送到所有 iOS 设备。通过使用 iCloud,您可以创建一些优秀的应用程序,添加一些令人信服的功能。
在本章中,我们将:
-
充分利用 iCloud 存储
-
使用 iCloud 备份备份我们的数据
-
在 iCloud 存储中存储文档
-
在云端搜索文档
-
处理文件版本冲突
-
将文档移动和存储到 iCloud 存储
-
配置和设置准备就绪的 iCloud 存储配置文件。
让我们开始吧。
比较苹果 iCloud 和 Google Docs
当苹果公司宣布他们新的基于云的文件管理系统 iCloud 时,它允许您将文件备份到云端,并在多台设备之间同步数据。
例如 iPad 和 iPhone 等设备可以自动备份文件,如照片、音乐和文档到 iCloud,并使这些文件与您的其他苹果设备同步。
您会在 iCloud 和 Google Docs 之间注意到的一个显著差异是,iCloud 仅适用于苹果设备,如 iPhone、iPod Touch 和 iPad。
iCloud 通过存储您所有的音乐、照片、文档、书籍等,并自动将它们无线推送到您的其他设备。
任何存储在 iCloud 中的文档都可以从任何连接到互联网的设备访问和查看。目前,iCloud 还没有提供与其他用户共享文档的方式。
另一方面,Google Docs 是谷歌提供的免费文档管理服务,允许您在云端创建、编辑和管理各种类型的文档。所有这些都在易于使用的界面中处理,以管理您的文档,每个文档都组织在相当于文件夹的标签下。与 iCloud 不同,您可以从任何电脑、平板电脑,甚至使用 iPhone 和 iPad 访问这些文档在云端的副本。
Google Docs 目前支持并存储以下文件类型在云端。这些文件可以在网络上的任何地方访问。
-
文档
-
电子表格
-
演示文稿
-
绘图:这是 Google Docs 家族的新增功能。
iCloud 和 Google Docs 都提供免费存储空间,但也有一些限制。iCloud 每个账户的总存储限制为 5GB;如果需要,可以购买额外的空间。
Google Docs 也是免费的,但有一些限制和限制,这些限制基于你可以存储的文档总数以及每个文档的长度/内容。
与 iCloud 不同,Google Docs 为你提供了一种与其他用户共享文档的方式。
你可以灵活地共享和设置文档的用户权限。你有能力使一个文档在互联网上公开可用,仅提供查看权限,或者允许选定的人进行编辑。
在 iCloud 中存储和使用文档
在云中存储文档为你提供了一个共同的中心位置,便于访问这些文档。对文档所做的任何更新都可以发送到每个使用相同 Apple ID 上传这些文档的 iOS 设备或计算机。
当文档上传到 iCloud 时,它不会立即移动到那里。文档必须首先从应用程序沙盒移动到本地系统管理的目录中,这样 iCloud 服务就可以对其进行监控。
一旦这个过程完成,文件就会传输到 iCloud,然后尽快分发到用户的其他 iOS 设备上。当文件存储在 iCloud 存储中时,任何在一个设备上所做的更改最初都会在本地存储,然后立即通过本地守护进程服务推送到 iCloud。
这是为了防止文件冲突同时发生;这由文件协调器处理,它协调应用程序和负责将文档传输到 iCloud 服务之间的本地守护进程服务所做的更改。
文件协调器在文档中起到类似锁定机制的作用,从而防止你的应用程序和守护进程服务同时应用修改到文档上。
注意
每当你的应用程序将文档存储到 iCloud 时,它必须指定一个或多个容器,这些文档和内容将存储在这些容器中,通过在你的应用程序权限文件中包含键值条目com.apple.developer.ubiquity-container-identifiers来实现。这在本节请求 iCloud 存储权限中有详细说明。
以下截图显示了在一个设备上做出更改的过程,以及在这些更改在通过本地守护进程过程推回到 iCloud 服务之前先在本地存储。

从实现的角度来看,为您的应用程序提供管理存储在 iCloud 中的文档的能力,最简单的方法是使用UIDocument类。这个类处理了读取和写入存储在 iCloud 中的文件所需的所有操作。它可以:
-
处理文件协调器的创建和使用来修改文档
-
无缝检测从其他设备接收到的更改
-
处理当两个设备以冲突的方式更新同一文件时可能出现的任何潜在冲突
-
防止大量冲突的更改同时发生
当我们在创建 iCloudExample 应用程序部分开始创建示例应用程序时,我们将探讨在 iCloud 中存储文档。
在 iCloud 中存储键值数据
在 iCloud 中存储数据为您提供了使您的应用程序能够在其他计算机和其他 iOS 设备上运行的同数据副本之间共享数据的方法。
允许您执行此操作的类称为NSUbiquitousKeyValueStore。这个类为您提供了在您的设备之间共享少量数据的能力。
NSUserDefaults类提供了一个程序接口,用于与系统默认值交互,允许应用程序根据用户的偏好定制其行为。例如,您可以设置应用程序以指定文档自动保存的频率。这个类允许您在稍后使用数据之前,将详细信息保存到各种数据类型中,即数字、字符串、日期、数组等等。
NSUserDefaults类与NSUbiquitousKeyValueStore类的主要区别在于,NSUserDefaults类将数据写入用户的 iCloud 存储,这样就可以被运行在不同 iOS 设备或计算机上的应用程序检索。
以下代码片段展示了如何设置云存储,以便您能够将数据写入用户的 iCloud 存储:
// TeamID + Bundle Identifier
NSFileManager *fileManager = [NSFileManagerdefaultManager];
NSURL *CloudURL = [fileManager URLForUbiquityContainerIdentifier: @"TEAMID.com.yourcompany.iCloudExample"];
// Log our iCloud URL to the console window
NSLog(@"iCloudURL: %@", [CloudURLabsoluteString]);
// Get a reference to the user's cloud store.
NSUbiquitousKeyValueStore *cloudStore = [NSUbiquitousKeyValueStoredefaultStore];
// Store our Cloud URL to the iCloudURL key.
[cloudStoresetString:[CloudURLabsoluteString] forKey:@"CloudURL"];
// This is important to include as it stores the
// values you set earlier on iCloud.
[cloudStore synchronize];
注意
当使用NSUbiquitousKeyValueStore类时,您必须确保将com.apple.developer.ubiquity-kvstore-identifier权限添加到您的项目权限文件中。这在本节请求 iCloud 存储权限中有详细说明。
单个键值存储可用的空间限制为 64KB;写入容器中单个键值的数据大小不得超过 4KB。这样,您可以存储有关应用程序的小量数据,但建议不要用它来存储用户文档或大量数据。
例如,您可能有一个应用程序,可能会存储当前版本号和用户正在查看的屏幕或文档的名称。这样,如果用户在另一台设备上打开应用程序,该设备上的应用程序版本可以打开与之前设备相同的屏幕或文档。
请求 iCloud 存储权限
为了保护应用程序创建的数据,需要在构建时创建一些特定的权限,以便使用 iCloud 存储。您需要确保已选择启用应用程序 App ID 的 iCloud 选项。
您需要从位于developer.apple.com/devcenter/ios/index.action#的 iOS 配置文件门户内创建一个新的 App ID。或者,如果您正在使用现有的 ID,则该 ID 不能是通配符 ID,即com.yourcompany.*。
要为您的 App ID 启用 iCloud 服务,请按照以下简单步骤操作:
-
首先,通过在配置 App ID屏幕中勾选启用 iCloud复选框来设置用于 iCloud 的配置文件。
![请求 iCloud 存储权限]()
-
接下来,您将看到一个弹出对话框,解释您使用所选 App ID 创建的任何新配置文件都将启用 iCloud 服务。
![请求 iCloud 存储权限]()
-
一旦您点击了确定按钮,弹出对话框将消失,您将返回到配置 App ID屏幕,并且启用 iCloud按钮将变为绿色,如图所示:
![请求 iCloud 存储权限]()
-
点击完成按钮以关闭此屏幕。
-
接下来,从配置选项卡,下载您的开发和分发配置文件。
-
接下来,从项目导航器窗口,点击您的项目,然后点击目标部分,然后点击摘要页面。
-
滚动到权限部分,勾选启用权限复选框。这将向您的项目添加一个名为iCloudExample.entitlements的文件。

当您向项目添加权限时,它们会直接绑定到用于将您的应用程序文档和数据存储库与其他应用程序区分开来的应用程序配置文件。根据应用程序需要使用的 iCloud 功能,应用程序可以请求两种权限。这些权限在以下表格中解释。
| 权限 | 描述 |
|---|---|
com.apple.developer.ubiquity-container-identifiers |
使用此权限请求 iCloud 文档存储权限。此键的值是一个容器标识符字符串数组(数组中的第一个字符串不得包含任何通配符字符)。 |
com.apple.developer.ubiquity-kvstore-identifier |
使用此权限请求 iCloud 键值数据存储权限。此键的值是一个单个容器标识符字符串。 |
当你指定容器标识符字符串时,它必须采用<TEAMID>.<CUSTOM_STRING>的形式,其中<TEAMID>是与你的开发团队关联的唯一十位字符标识符。<CUSTOM_STRING>标识符是一个反向 DNS 字符串,用于标识存储你的应用程序文档的容器。
此字符串不一定需要是你的应用程序的捆绑标识符,但可以是对你或你的开发团队有意义的任何内容。
注意
要找到与你的开发团队关联的唯一标识符,请登录到苹果开发者连接网站,然后转到会员中心页面(developer.apple.com/membercenter)。从会员中心主页,选择您的账户选项卡,然后从该选项卡的左侧列中选择组织概要。你的团队标识符位于公司/组织 ID字段中。
使用 iCloud 文档存储的应用程序可以指定多个容器来存储文档和数据。com.apple.developer.ubiquity-container-identifiers键的值是一个字符串数组。此数组中的第一个字符串必须是与你的应用程序关联的主要容器标识符。
以下代码片段显示了iCloudExample权限文件的 XML,该文件请求 iPhone 应用程序的密钥。它可以读取和写入其自己的文档,这些文档存储在容器目录中,如高亮代码部分所示。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>AXEUZ3F6VR.com.geniesoftstudios</string> <key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>TEAMID.com.yourcompany.iCloudExample</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>TEAMID.com.yourcompany.iCloudExample</string>
<key>get-task-allow</key>
<true/>
</dict>
</plist>
以下截图显示了iCloudExample.Entitlements权限文件在项目导航器中的属性列表视图。

TEAMID值(如前一张截图所示),可以从你的开发者账户的账户摘要页面和个人 ID获取,如下一个截图所示:

注意
你在 entitlements 属性列表文件中指定的字符串也是你传递给URLForUbiquityContainerIdentifier:方法以请求用户 iCloud 存储中目录位置的字符串。
iCloud 备份
当使用 iCloud 备份时,用户可以选择将他们的应用程序和数据直接备份到他们的 iCloud 账户。这使得在以后的时间恢复应用程序到最近的状态变得更容易。选择将数据备份到 iCloud,将使用户更容易将数据重新安装到任何新的或现有的 iOS 设备上。
iCloud 会确定哪些文件需要备份,这基于文件存储的位置,通常在应用程序或主目录内。其他需要备份的区域包括用户文档目录内的所有内容,以及应用程序库的内容。在开发 iCloud 应用程序时,为了最小化存储在用户 iCloud 账户中的数据量,建议将更多文件放在Library/Caches目录中,尤其是那些可以轻松重新创建或以其他方式获取的文件。
为了将您的数据备份到 iCloud,您需要在所有 iOS 设备上激活此功能。这可以通过以下简单步骤实现:
-
从设备中的设置面板选择iCloud。这如图所示:
![iCloud 备份]()
-
接下来,使用您的AppleID和密码登录,然后点击如图所示的登录按钮。
-
您需要同意 iCloud 的条款和条件,然后点击同意按钮关闭弹出对话框。
-
在下一个屏幕截图中,您可以选择您想要备份到 iCloud 的项。
-
接下来,点击存储与备份选项进入下一屏幕:
![iCloud 备份]()
-
接下来,将备份到 iCloud选项从备份部分设置为开启。这将自动将所有相机照片、文档和设置备份到 iCloud。
注意
将此选项设置为开启将防止 iTunes 备份您的详细信息,因为您的 iOS 设备将处理这项工作。

当在应用程序中使用 iCloud 存储 API 时,任何应用程序明确存储在 iCloud 中的文档都不会与应用程序一起备份;这是因为这些文件已经存储在您的 iCloud 账户中,因此不需要单独备份。
注意
关于如何在 iCloud 中存储文档的信息,请参阅在 iCloud 中存储和使用文档部分。要确定哪些目录被备份,请查看iOS 应用程序编程指南。
创建 iCloudExample 应用程序
在我们创建iCloudExample应用程序之前,我们必须首先启动 Xcode 开发环境。
-
从
/Developer/Applications文件夹启动 Xcode。 -
从项目模板对话框中选择单视图应用程序模板来使用。
![创建 iCloudExample 应用程序]()
-
然后,点击下一步按钮继续到向导的下一个步骤。这将允许你输入产品名称和你的公司标识符。
![创建 iCloudExample 应用程序]()
-
将产品名称输入为
iCloudExample,并确保你已经从设备家族下拉框中选择了iPhone。 -
点击下一步按钮继续到向导的最后一步。
-
选择你想要保存项目的文件夹位置。
-
点击创建按钮,将你的项目保存到步骤 6中指定的位置。
一旦你的项目创建完成,你将在项目导航器窗口中看到 Xcode 开发界面,以及模板为你创建的项目文件。
我们接下来的步骤是开始构建我们的用户界面以获取云 URL,在云中存储密钥和文档,并了解如何从云中检索信息:
-
在项目导航器窗口中,从
iCloudExample文件夹中选择iCloudExampleViewController.xib文件。 -
从对象库中选择并拖动一个(
UIButton)按钮控制,到视图中。 -
修改按钮控制的对象属性,并将标题设置为获取 iCloud URL。
-
选择并拖动一个(
UILabel)标签控制到视图中,并将其放置在获取 iCloud URL按钮下方。 -
修改标签控制的对象属性,并将文本属性设置为iCloud URL:。
重复之前的步骤添加存储到 iCloud、DocPath、从 iCloud 读取和项目值的按钮和标签。
如果你一切操作正确,你的视图应该看起来像下一个截图。请随意根据需要进行调整。

现在我们已经创建了一个用户界面,你需要将控件绑定起来以创建必要的事件,然后按照以下步骤操作:
-
打开位于你项目
iCloudExample文件夹中的iCloudExampleViewController.h接口文件,并添加以下代码:#import<UIKit/UIKit.h> @interfaceiCloudExampleViewController : UIViewController { UILabel *iCloudURL; UILabel *documentPath; UILabel *authorName; } - (IBAction)getCloudURL:(id)sender; - (IBAction)storeDocument:(id)sender; - (IBAction)readDocument:(id)sender; @property (strong, nonatomic) IBOutletUILabel *iCloudURL; @property (strong, nonatomic) IBOutletUILabel *documentPath; @property (strong, nonatomic) IBOutletUILabel *authorName; @end -
打开位于你项目
iCloudExample文件夹中的iCloudExampleViewController.m实现文件,并添加以下高亮代码:#import "iCloudExampleViewController.h" #import<Foundation/Foundation.h> #import<Foundation/NSObject.h> @implementationiCloudExampleViewController; @synthesizeiCloudURL; @synthesizedocumentPath; @synthesizeauthorName;注意
如果我们不声明这些,我们将收到编译器警告信息,这可能导致你的应用程序出现意外的结果,甚至可能导致应用程序意外终止。
-
接下来,我们需要声明一个将连接到 iCloud 服务的方法,使用我们的
TEAMID和捆绑标识符,并检索 iCloud URL。输入以下代码片段。// Function to obtain the iCloud URL based on the TEAMID // and the Bundle Identifier - (IBAction)getCloudURL:(id)sender { // TeamID + Bundle Identifier NSFileManager *fileManager = [NSFileManagerdefaultManager]; NSURL *CloudURL = [fileManager URLForUbiquityContainerIdentifier :@"TEAMID.com.yourcompany.iCloudExample"]; iCloudURL.text = [@"iCloudURL = " stringByAppendingFormat:@"%@", [CloudURLabsoluteString ]]; iCloudURL.numberOfLines = 4; iCloudURL.textColor = [UIColorblueColor]; } -
当这个按钮被执行时,它将显示基于你的 TEAMID 和捆绑标识符的 iCloud 服务 URL。
file://localhost/private/var/mobile/Library/Mobile%20Documents/TEAMID~com~yourcompany~iCloudExample/ -
接下来,我们需要实现一个方法,该方法将用于将文档存储到我们的 iCloud 沙盒中。输入以下代码片段:
- (IBAction)storeDocument:(id)sender { // Store document in the Cloud NSArray *searchPath = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *docPath = [searchPath objectAtIndex:0]; NSString *fileName = [NSStringstringWithFormat: @"%@/iCloudExample.doc",docPath]; NSString *fileContent = @"Welcome to storing documents using icloud. iCloud Rocks!!!"; // Now Save the content to the documents directory [fileContentwriteToFile:fileNameatomically: NOencoding:NSStringEncodingConversionAllowLossyerror:nil]; NSURL *fileURL = [NSURL URLWithString:fileName]; documentPath.text = [@"DocPath = " stringByAppendingFormat: @"%@", [fileURLabsoluteString ]]; documentPath.textColor = [UIColorblueColor]; documentPath.lineBreakMode = UILineBreakModeWordWrap; } -
当此按钮执行时,它将显示位于 iCloud 应用程序沙盒中的文档文件夹的路径:
/var/mobile/Applications/6BF2CE1F-C184-43FA-8D00-E4D476F8A538/Documents/iCloudExample.doc。 -
接下来,如果您通过选择窗口|组织者打开组织者窗口,您会注意到我们的
iCloudExample.doc已经被添加到我们的应用程序沙盒中。![创建 iCloudExample 应用程序]()
-
接下来,我们需要实现一个方法,该方法将用于在我们的 iCloud 存储库中添加和检索键值。
- (IBAction)readAuthor:(id)sender { NSUbiquitousKeyValueStore *cloudStore = [NSUbiquitousKeyValueStoredefaultStore]; [cloudStoresetString:@"John Grisham" forKey:@"FavoriteAuthor"]; // Important, as it first stores your in memory key values // to disk based storage, prior to eventually storing this //within iCloud [cloudStore synchronize]; // Get the latest values from iCloud authorName.text = [@"Favorite Author = " stringByAppendingFormat :@"%@", [cloudStorestringForKey:@"FavoriteAuthor"]]; authorName.textColor = [UIColorredColor]; authorName.lineBreakMode = UILineBreakModeWordWrap; } -
当此按钮执行时,它将显示位于 iCloud 应用程序沙盒中的Favorite Author键值数据的键入值:Favorite Author = John Grisham。
-
我们现在可以构建和编译我们的
iCloudExample应用程序。以下截图显示了按下每个按钮时的输出:

因此,我们已经成功创建了一个简单而强大的应用程序,它可以与 iCloud 通信来存储文档和键值数据,并从云中检索数据。
将文档移动到 iCloud 存储
当将文档移动到 iCloud 时,您可以在容器目录内创建额外的子目录来管理您的文件。
从开发的角度来看,建议在将文档添加到 iCloud 时,您应该创建一个Documents子目录,并使用该目录来存储用户文档。在 iCloud 中,Documents目录的内容对用户可见,因此可以删除单个文档,而Documents目录之外的所有内容则被组合在一起,作为一个用户可以保留或删除的单个实体。
以下代码片段首先在您的应用程序沙盒中创建并保存文件,然后再将文件移动到 iCloud 中指定的目标位置。
// TeamID + Bundle Identifier
NSFileManager *fileManager = [NSFileManagerdefaultManager];
NSURL *CloudURL = [fileManager URLForUbiquityContainerIdentifier: @"TEAMID.com.yourcompany.iCloudExample"];
NSString *docString = @"Documents";
NSURL *tempURL = [NSURL URLWithString:docString];
BOOL myVal = [fileManagersetUbiquitous:YESitemAtURL: fileURLdestinationURL:CloudURLerror:NULL];
在此代码片段中,我们创建了一个NSURL对象,该对象指定了文件在用户 iCloud 存储中的目标位置。然后我们调用NSFileManager类的URLForUbiquityContainerIdentifier:方法来获取目录的根 URL,然后向该 URL 追加任何额外的目录和文件名。最后,我们调用NSFileManager的setUbiquitous:itemAtURL:destinationURL:error:方法,将文件移动到 iCloud 中指定的目标位置。
iCloud 存储 API
iCloud 存储 API 允许您的应用程序将用户文档和数据写入中央位置,并从用户的全部计算机和 iOS 设备访问这些项目。
小贴士
使用 iCloud 使用户的文档无处不在,这意味着用户可以从任何设备查看或编辑这些文档,而无需显式同步或传输文件。
在用户的 iCloud 账户中存储文档为用户提供了一层安全保障。如果用户不幸丢失了设备,如果这些文档包含在 iCloud 存储中,它们可以很容易地恢复。有两种方式可以利用 iCloud 存储,每种方式都有其重要的用途。以下表格中解释了这些用途:
| 存储类型 | 描述 |
|---|---|
| iCloud 文档存储 | 使用此功能将用户文档和数据存储在用户的 iCloud 账户中。请参阅本章中位于“在 iCloud 中存储和使用文档”部分的章节。 |
| iCloud 键值数据存储 | 使用此功能在应用程序的实例之间共享少量数据。请参阅本章中位于“在 iCloud 中存储键值数据”部分的章节。 |
大多数你创建的应用程序都将使用 iCloud 文档存储来共享用户 iCloud 账户中的文档。这将提供跨多设备共享文档和管理特定设备上文档的能力。
当使用 iCloud 键值数据存储时,用户不会看到这一点,因为这由你的应用程序处理,并且只共享少量信息;这些信息仅由应用程序使用。例如,你可以存储用户登录你的应用程序的时间,或者他们当前正在查看的屏幕。
以下截图显示了在应用程序沙盒中创建本地 iCloud 存储信息的过程。

注意
关于如何在 iCloud 中存储和使用文档的更多信息,请参阅本章中位于“在 iCloud 中存储和使用文档”部分的章节。
在 iCloud 中搜索文档
有时候,在修改文档之前,你可能需要检查云中某个位置的文档是否存在。例如,如果你想在应用程序中打开文档之前检查该文档是否存在,如果不进行检查而尝试打开,你将收到一个错误。
另一种情况可能是你需要从你的 iCloud 存储库中删除一个文件;在尝试删除此文件之前,你仍然需要执行检查以确保该文档确实存在,否则你将收到一个处理不当的错误,导致你的应用程序崩溃。
要实现任何这些案例场景,你需要使用NSMetadataQuery对象搜索 iCloud 存储库。通过 iCloud 存储库进行搜索是确保在用户的 iCloud 存储和你的应用程序沙盒中找到文档的可靠方法。
你应该始终使用查询对象而不是持久保存 URL,因为当你的应用程序未运行时,用户可以删除 iCloud 存储中的文件。使用查询进行搜索是确保准确文档列表的唯一方法。
NSMetadataQuery *mdQuery = [[NSMetadataQueryalloc] init];
[mdQuerysetPredicate:[NSPredicatepredicateWithFormat:@"(kMDItemFSName LIKE 'iCloudDoc *')"]];
[[NSNotificationCenterdefaultCenter] addObserver:selfselector: @selector(processQuery:) name:nilobject:mdQuery];
[mdQuerystartQuery];
以下代码片段显示了NSNotification类的关联processQuery方法,并展示了我们如何为每个不同的NSMetadataQuery通知方法执行和处理比较。
-(void)processQuery:(NSNotification *)notification {
NSMetadataQuery *mdQuery = [notification object];
if ([[notification name] isEqualToString: NSMetadataQueryDidStartGatheringNotification]) {
NSLog(@"%@ %@ Query started", [self class], NSStringFromSelector(_cmd));
}
else if ([[notification name] isEqualToString: NSMetadataQueryGatheringProgressNotification]) {
NSLog(@"%@ %@ %ld", [self class], NSStringFromSelector(_cmd), (long)[mdQueryresultCount]);
}
else if ([[notification name] isEqualToString: NSMetadataQueryDidFinishGatheringNotification]) {
NSUIntegertheResultCount = [mdQueryresultCount];
theResultCount = 20;
for (NSUIntegeri; i<theResultCount; i++) {
NSLog(@"%@ %@ %ld %@", [self class], NSStringFromSelector(_cmd), (long)i, [mdQueryresultAtIndex:i]);
}
}
else {
NSLog(@"%@ %@ NSMetadataQueryDidUpdateNotification: %@", [self class], NSStringFromSelector(_cmd), notification);
}
}
在 iOS 5.0 中,NSMetadataQuery类支持几个用于文档的搜索作用域。如下表所示。
| NSMetadataQuery 类方法 | 描述 |
|---|---|
NSMetadataQueryUbiquitousDocumentsScope |
使用此功能在 iCloud 中搜索位于文档目录内的文档(对于任何给定的容器目录,将用户允许访问的文档放在文档子目录中)。 |
NSMetadataQueryUbiquitousDataScope |
使用此功能在 iCloud 中搜索位于除文档目录之外的任何位置的文档(对于任何给定的容器目录,使用此作用域来存储您的应用程序需要共享但不想让用户直接操作的用户相关数据文件)。 |
在 iCloud 中处理文档
当您的应用程序需要读取或写入 iCloud 中的文档时,它必须以协调的方式进行。您的应用程序可能不是唯一试图在任何给定时刻操作本地文件的应用程序。将文档传输到和从 iCloud 的守护进程也需要定期监控文件。为了防止您的应用程序干扰守护进程(反之亦然),iOS 提供了一个与您针对 iCloud 存储目标文件和目录协同工作的锁定机制。
iCloud 锁定机制的核心是文件协调器和文件展示者。
文件协调器
每次您需要读取和写入文件时,您都应使用文件协调器,它是NSFileCoordinator类的一个实例。文件协调器的任务是协调应用程序和同步守护进程在相同文档上执行的读取和写入操作。例如,您的应用程序和守护进程可能同时读取文档,但任何时候只能有一个写入文件。
此外,如果一个进程正在读取文档,则另一个进程将被阻止向文档写入,直到先前的进程完成读取文档。
文件展示者
除了协调操作外,文件协调器还与文件展示者合作,通知应用程序何时将要发生更改。文件展示者是指任何遵循NSFilePresenter协议的对象,并负责管理应用程序中特定文件(或文件目录)。
文件展示者的工作是保护其自身数据结构的完整性。它是通过监听来自其他文件协调器的消息并使用这些消息来更新其内部数据结构来做到这一点的。在大多数情况下,文件展示者可能不需要做任何事情。然而,如果一个文件协调器声明它即将将文件移动到新的 URL,文件展示者就需要用文件协调器提供给它的新 URL 替换其旧的 URL。
处理文件版本冲突
处理文件版本冲突是软件开发中常见的问题。使用 iCloud 时,我们需要能够在多个设备上运行多个应用程序实例时处理这种情况,并且它们都尝试修改同一文档。当两个设备同时尝试上传对文件的更改时,这将导致冲突。
到这一点,iCloud 将会有同一文件的两种不同版本,并必须决定如何处理它们。它的解决方案是将最近修改的文件作为当前文件,并将任何其他版本的文件标记为冲突版本。
为了避免丢失对那些文档所做的更改,您的应用程序将需要提示用户选择适当的操作步骤。例如,您可能让用户选择保留文件的哪个版本,或者您可能提供将旧版本保存为新名称的选项。
您需要确定当前文件和版本,使用currentVersionOfItemAtURL:类方法,然后通过调用unresolvedConflictVersionsOfItemAtURL:类方法来获取冲突版本的数组。
对于每个冲突的文件版本,您需要执行适当的操作来解决冲突,可以通过以下列出的任何操作来实现:
-
自动将冲突版本与当前文件合并,如果这样做是可行的。
-
选择忽略冲突版本,这将导致那些文件中的数据丢失。
-
提示用户选择适当的操作步骤,并决定他们确实应该保留哪个版本。这应该是您的最后一步行动。
负责任地使用 iCloud 存储
利用 iCloud 存储功能的程序在存储数据时应负责任。每个用户账户中可用的空间是有限的,并且由所有应用程序共享。此外,用户可以看到特定应用程序消耗了多少空间,并可以选择删除与该特定应用程序关联的文档和数据。出于这些原因,您的应用程序在管理文件时应负责任。以下是一些帮助您适当管理文档的建议:
-
而不是存储所有文档,让用户选择他们想要存储在 iCloud 账户中的文档。如果用户创建了大量的文档,将所有这些文档存储在 iCloud 中可能会耗尽用户的可用空间。为用户提供一种指定要存储在 iCloud 中的文档的方式,可以让用户在决定如何最好地使用可用空间时拥有更多的灵活性。
-
记住,删除文档会将其从用户的 iCloud 账户以及所有该用户的计算机和设备中删除。确保用户知道这一点,并确认任何删除操作。为了使你的应用程序删除文档的本地副本,然后从 iCloud 下载一个新的副本,请使用
NSFileManager的evictUbiquitousItemAtURL:error:方法。 -
当在 iCloud 中存储文档时,尽可能将它们放在文档目录中。用户可以单独删除文档目录中的文档以释放空间。然而,该目录之外的所有内容都被视为数据,必须一次性删除。
-
永远不要在用户的 iCloud 存储中存储你的应用程序专有的缓存或其他文件。用户的 iCloud 账户应仅用于存储用户数据和内容。
-
以对待你应用程序沙盒中所有其他文件的方式对待 iCloud 中的文件。保存文件的时间应由你应用程序的需求以及保留用户数据的需求来驱动。你不应该改变你的应用程序以更频繁或更少地保存文件到 iCloud。iCloud 会自动优化其到服务器的传输,以确保最佳性能。
当内容通过互联网发送时,iCloud 通过使用 SSL 对其进行加密来保护你的内容。这导致你的内容以加密格式存储,并使用安全令牌进行身份验证。
注意
关于如何在 iCloud 中存储和使用文档的更多信息,请参阅本章中位于“在 iCloud 中存储和使用文档”部分的说明。
摘要
在本章中,我们学习了使用 iCloud 的好处,以及如何通过它们的存储 API 来访问它们。我们探讨了如何在我们的代码中集成 iCloud 功能,如何存储和检索键值数据,以及如何在应用程序沙盒内的文件夹中存储文档。
我们还学习了如何在 iCloud 存储库中搜索和定位文档的过程,以及如何处理和避免在多个设备上更新同一文件的多个副本时提交到 iCloud 存储库所遇到的文件版本冲突。
在下一章中,我们将学习 OpenGL ES 的新调试功能,以及这个新的 Xcode 调试器如何允许你直接在 IDE 中跟踪 OpenGL ES 代码中的特定问题。
第三章:使用 OpenGL ES 调试
开放图形库(OpenGL)可以简单地定义为“图形硬件的软件接口”。它是一个高度可移植且非常快速的 3D 图形和建模库。使用 OpenGL 图形 API,你可以创建一些能够表示 2D 和 3D 数据的出色图形。
OpenGL 库是一个多用途的开源图形库,支持 2D 和 3D 数字内容创作、机械和建筑设计、虚拟原型设计、飞行模拟和视频游戏等应用,并允许应用程序开发者配置 3D 图形管线,并向其提交数据。
对象由连接的顶点定义。然后对象的顶点被变换、光照,并组装成原语,然后光栅化以创建一个可以直接发送到底层图形硬件进行渲染的 2D 图像,这通常非常快,因为硬件是专门用于处理图形命令的。
嵌入式系统 OpenGL(OpenGL ES)是流行的 OpenGL 框架的简化版本,它被开发得更容易学习和实现,消除了 iOS 图形硬件中冗余功能的需求。这个框架已经优化,以充分利用硬件加速的数学运算,从而使开发者能够获得最佳性能。
在本章中,我们将重点关注 OpenGL ES 调试器带来的新调试特性,使开发者能够追踪代码中与 OpenGL ES 相关的具体问题。
在本章中,我们将:
-
了解 Xcode 4 中的新工作流程特性
-
创建一个简单的项目来调试 OpenGL ES 应用程序
-
熟悉 OpenGL ES 2.0 可编程管线
-
编译和链接着色器到 OpenGL ES 程序
-
使用统一变量和属性从应用程序传递数据到着色器
-
检测 OpenGL ES 状态信息(视图纹理和着色器)
-
设置和使用断点来捕获 OpenGL ES 错误
-
设置条件 OpenGL ES 入口点断点
-
在帧边界处断点
本章我们将探讨一些精彩的内容,让我们开始吧。
理解 Xcode 中的新工作流程特性
在本节中,我们将探讨 Xcode 4 开发环境所做的改进,以及这如何使我们比之前版本的 Xcode 更容易地调试 OpenGL ES 应用程序。
我们将探讨如何使用调试器的帧捕获功能来捕获 OpenGL ES 应用程序中包含的所有帧对象。这个工具使你能够在特定时间点列出应用程序当前使用的所有帧对象。
我们将熟悉 Xcode 中的新 OpenGL ES 调试器,以便我们能够追踪代码中与 OpenGL ES 相关的具体问题。
创建一个简单的项目以调试 OpenGL ES 应用程序
在我们继续之前,我们首先需要创建我们的OpenGLESExample项目。为了唤起您的记忆,您可以参考我们在第二章中涵盖的部分,在创建 iCloudExample 应用程序部分:
-
从
/Developer/Applications文件夹中启动 Xcode。 -
从项目模板对话框中选择OpenGL 游戏模板。
![创建一个简单的项目以调试 OpenGL ES 应用程序]()
-
然后,点击下一步按钮以继续向导的下一步。这将允许您输入产品名称和您的公司标识符。
![创建一个简单的项目以调试 OpenGL ES 应用程序]()
-
在产品名称中输入
OpenGLESExample,并确保您已从设备家族下拉框中选择了iPhone。 -
接下来,点击下一步按钮以继续向导的最终步骤。
-
6 选择您想要保存项目的文件夹位置。
-
然后,点击创建按钮以在指定位置保存您的项目。
一旦创建了项目,您将看到 Xcode 开发界面,以及模板在项目导航器窗口中为您创建的项目文件。
现在我们已经创建了项目,我们需要配置项目以使我们能够调试对象的状态。
检测 OpenGL ES 状态信息和对象
为了使我们能够检测和监控应用程序中对象的状态,我们需要通过项目中的编辑方案…部分启用此功能,如图下所示:

在编辑方案部分,如图下所示,选择运行 OpenGLESExampleDebug操作,然后点击选项选项卡,然后选择OpenGL ES 启用帧捕获复选框。
注意
为了使此功能正常工作,您必须在 iOS 设备上运行应用程序,并且设备必须运行 iOS 5.0 或更高版本。此功能在 iOS 模拟器中不会工作。您需要确保在连接设备后,您需要重新启动 Xcode 以使此选项可用。

当您正确配置了项目后,点击确定按钮以接受所做的更改,并关闭对话框。接下来,构建并运行您的 OpenGL ES 应用程序。当您运行应用程序时,您将看到两个三维彩色立方体盒子。

当您在 iOS 设备上运行应用程序时,您会注意到帧捕获出现在 Xcode 4 调试栏中,如图下所示:

当使用 Xcode 4.2 的 OpenGL ES 功能时,这些调试功能使您能够执行以下操作:
-
检查 OpenGL ES 状态信息。
-
反思 OpenGL ES 对象,如视图纹理和着色器。
-
步进绘制调用并观察每次调用时的变化。
-
步进每个绘制调用之前的每个状态调用,以确切了解图像是如何构建的。
以下截图显示了我们的示例应用程序捕获的帧。调试导航器包含与该特定帧相关联的每个绘制调用和状态调用的列表。
与帧关联的缓冲区在编辑器面板中显示,状态信息在调试窗口面板中显示。当启动 OpenGL ES 帧捕获时,默认视图显示在自动视图中。此视图显示颜色部分,即Renderbuffer #1,以及图像的灰度等效部分,即Renderbuffer #2。

您还可以在红色、绿色和蓝色以及 alpha 通道的每个通道之间切换可见性,然后使用范围滚动条调整颜色范围。这可以通过选择前一个截图所示的每个齿轮按钮轻松完成。

您还可以通过调试导航器中的每个绘制调用或使用调试栏中的双箭头和滑块来步进每个绘制调用。

当使用绘制调用箭头或滑块时,您可以让 Xcode 从调试导航器中选择步进的绘制调用。这可以通过Control + 点击捕获的帧下方,并从快捷菜单中选择在调试导航器中显示来实现。

您还可以使用快捷菜单在绘制图像的标准视图和显示对象的线框视图之间切换,通过从弹出菜单中选择显示线框选项,如前一个截图所示。
当使用对象的线框视图时,它突出显示由所选绘制调用绘制的元素。要关闭线框功能并将图像返回到正常状态,请从弹出菜单中选择隐藏线框选项,如以下截图所示:

现在您已经对通过 OpenGL ES 应用程序及其绘制调用进行调试有了合理的理解,让我们看看我们如何查看与 OpenGL ES 应用程序关联的纹理。
查看纹理
当在 OpenGL ES 中引用纹理时,这基本上是一个可以被图形引擎管道采样的图像,并用于将彩色图像映射到映射表面上。要查看通过帧捕获按钮捕获的对象,请按照以下简单步骤操作:
-
打开辅助编辑器以查看与捕获帧关联的对象。在此视图中,你可以选择查看所有对象、仅绑定对象或堆栈。这可以通过以下截图所示的视图 | 辅助编辑器 | 显示辅助编辑器菜单访问:
![查看纹理]()
-
打开一个辅助编辑器面板,这样你就可以同时看到对象和堆栈帧。这可以通过之前显示的视图 | 辅助编辑器 | 添加辅助编辑器菜单访问,或者通过点击以下截图所示的+符号:

要查看 OpenGL ES 辅助编辑器中任何对象的详细信息,请双击该对象,或从弹出列表中选择该项,如图下所示截图:
值得注意的是,从该视图中,你可以更改已捕获并渲染到视图中的任何对象的朝向。要更改朝向,找到屏幕右下角显示的方向选项。根据需要,对象可以被更改以在一个或多个视图中显示,如下所示:
-
顺时针旋转
-
逆时针旋转
-
垂直翻转方向
-
水平翻转方向

例如,如果你想查看关于顶点数组对象(VAO)的信息,你可以双击它以更详细地查看,如图下所示截图。

这显示了构建我们每个对象所需的全部X, Y和Z轴。接下来,我们将探讨着色器的构建方式。
着色器
你可以为 OpenGL ES 编写两种类型的着色器;这些是顶点着色器和片段着色器。
这两个着色器构成了 OpenGL ES 2.0 可编程管道中所谓的可编程部分,并且使用类似于 C 语言的语法编写,称为OpenGL ES 着色语言(GLSL)。
以下截图概述了 OpenGL ES 2.0 可编程管道,并结合了一个适用于 iOS 设备的嵌入式平台的 OpenGL 着色语言版本,用于编程顶点着色器和片段着色器:

着色器并不新鲜,这些已经在各种使用 OpenGL 的游戏中得到了应用。例如,Doom 3 和 Quake 4,或者几个飞行模拟器,如微软的 Flight Simulator X。
小贴士
关于着色器有一点需要注意,那就是它们在应用程序构建时不会被编译。着色器的源代码以文本文件的形式存储在您的应用程序包中,或者作为字符串字面量定义在您的代码中,即vertShaderPathname = [[NSBundlemainBundle] pathForResource:@"Shader" ofType:@"vsh"];
在您可以使用着色器之前,您的应用程序必须加载和编译每个着色器。这样做是为了保持设备独立性。
以苹果决定为未来发布的 iPhone 更换不同的 GPU 制造商为例,如果苹果更换了 GPU 制造商,编译后的着色器可能在新 GPU 上无法工作。让您的应用程序在运行时延迟编译将避免这个问题,并且任何最新的 GPU 版本都将得到全面支持,而无需您重新构建应用程序。
下表解释了两种着色器之间的区别。
| 着色器类型 | 描述 |
|---|---|
| 顶点着色器 | 这些是在您的场景中每次调用一次的程序。为了更好地解释这一点,例如,如果您使用单个正方形渲染一个简单的场景,每个角落有一个顶点,这将被调用四次。它们的工作是执行一些计算,如光照、几何变换、移动、缩放和旋转对象,以模拟现实感。 |
| 片段着色器 | 这些是在您的场景中每次调用一次的程序。例如,如果您使用单个正方形渲染相同的简单场景,它将为正方形覆盖的每个像素调用一次。片段着色器还可以执行光照计算等操作,但它们最重要的任务是设置像素的最终颜色。 |
接下来,我们将从检查 OpenGL 模板为我们创建的顶点着色器实现开始。您会注意到这些着色器是使用类似 C 语法的指令实现的代码文件。让我们首先通过以下简单步骤检查顶点着色器文件的每个部分:
-
打开位于项目导航器窗口中的
OpenGLESExample文件夹内的Shader.vsh顶点着色器文件,并检查以下代码片段。attribute vec4 position; attribute vec3 normal; varying lowp vec4 colorVarying; uniform mat4 modelViewProjectionMatrix; uniform mat3 normalMatrix; void main(){ vec3 eyeNormal = normalize(normalMatrix * normal); vec3 lightPosition = vec3(0.0, 0.0, 1.0); vec4 diffuseColor = vec4(0.4, 0.4, 1.0, 1.0); floatnDotVP = max(0.0, dot(eyeNormal, normalize(lightPosition))); colorVarying = diffuseColor * nDotVP; gl_Position = modelViewProjectionMatrix * position; } -
接下来,我们将查看这段代码的功能,并解释实际上发生了什么。那么,让我们开始吧。
attribute关键字声明这个着色器将通过一个名为position的输入变量传递。这将用于指示顶点的位置。您会注意到position变量已被声明为vec4类型,这意味着每个顶点包含四个浮点值。第二个声明为normal的属性输入变量已被声明为vec3类型,这意味着顶点包含三个用于围绕x、y和z轴旋转的浮点值。使用变量名
diffuseColor声明的第三个属性输入变量,定义了用于顶点的颜色。我们声明了另一个名为colorVarying的变量。你会注意到它不包含属性关键字。这是因为它是一个输出变量,将被传递到片段着色器。varying关键字告诉我们特定顶点的值。这基本上意味着你可以为每个顶点指定不同的颜色,这将使所有中间值形成一个整洁的渐变,你将在最终输出中看到。我们将其声明为 vec4,因为颜色由四个分量值组成。 -
最后,我们声明了两个名为
modelViewProjectionMatrix和normalMatrix的统一关键字变量。模型矩阵、视图矩阵和投影矩阵是三个独立的矩阵。模型矩阵将对象的局部坐标空间映射到世界空间,视图矩阵将世界空间映射到相机空间,投影矩阵将相机空间映射到屏幕空间。当使用所有三个时,你可以使用其中一个结果将对象空间映射到屏幕空间,从而让你能够确定需要传递给可编程管道下一阶段的顶点位置信息。
正常矩阵向量用于确定指定顶点或表面接收到的光量。统一变量是允许你从应用程序代码传递到着色器的第二种数据形式。统一类型对顶点和片段着色器都可用,与属性不同,属性只对顶点着色器可用。
注意
统一变量的值不能被着色器更改,并且每次着色器在管道中给定行程时都将具有相同的值。统一变量还可以包含任何你想要传递到着色器中以供使用的任意类型的数据。
-
接下来,我们将从每个顶点的颜色属性值将值赋给
colorVarying变量。这个值将在片段着色器中以插值形式可用。 -
最后,我们修改了
gl_Position输出变量,使用浮点平移变量沿着X, Y和Z轴移动顶点,基于平移统一变量的值。接下来,我们将查看 OpenGL ES 模板为我们创建的片段着色器。
-
打开位于 Project Navigator 窗口 OpenGLESExample 文件夹中的
Shader.fsh片段着色器文件,并检查以下代码片段。varying lowp vec4 colorVarying; void main(){ gl_FragColor = colorVarying; }
我们现在将查看这段代码,并解释这里实际上发生了什么。
你会注意到在片段着色器中,突出显示的 colorVarying 变量的声明与顶点着色器中的名称相同。这非常重要;如果这些名称不同,OpenGL ES 将不会意识到它们是相同的变量,你的程序将产生意外的结果。
类型也非常重要,它必须与在顶点着色器中声明的数据类型相同。这是一个 GLSL 关键字,用于指定表示数字的字节数的精度。
从编程的角度来看,用于表示数字的字节数越多,你可能会遇到的浮点计算舍入问题就越少。GLSL 允许用户在任何声明变量时使用精度修饰符,并且必须在文件中声明。如果在片段着色器中未声明它,将导致你的着色器无法编译。
lowp关键字在插值过程中会提供最佳性能和最低精度。当处理颜色时,这是更好的选择,因为小的舍入误差并不重要。如果你需要提高精度,最好使用mediump或highp,如果精度不足导致你在应用程序中遇到问题。
注意
更多关于OpenGL ES 着色语言(GLSL)或精度修饰符的信息,请参阅以下位于:www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf的文档。
OpenGL ES 的错误处理
OpenGL 为基本 GL 和 GLU 库提供简单的错误处理例程。你可以使用glGetError函数来检查错误。OpenGL 只记录第一个发生的错误。所有后续的错误都会被忽略,直到通过调用glGetError清除错误缓冲区。
导致错误的命令被忽略,因此它对 OpenGL 状态或帧缓冲区内容没有影响。一旦记录,当前错误代码不会清除,并且不会记录额外的错误,直到你调用查询命令glGetError(),该命令返回当前错误代码。在你查询并清除当前错误代码之后,或者如果一开始就没有错误,glGetError()返回GL_NO_ERROR。glGetError函数的语法定义如下:
GLenum glGetError (void);
glGetError函数返回错误标志的值。当在 GL 或 GLU 库中检测到错误时,错误标志设置为适当的错误代码值。
注意
如果返回GL_NO_ERROR,则自上次调用glGetError()以来或自 GL 初始化以来没有检测到错误。
如果在调用glGetError()方法之前没有记录其他错误,将返回错误代码,并将标志重置为GL_NO_ERROR。
以下表格列出了由glGetError方法调用返回的基本定义的 OpenGL 错误代码及其描述:
| 错误代码 | 描述 |
|---|---|
GL_INVALID_ENUM |
GLenum参数超出范围 |
GL_INVALID_VALUE |
数字参数超出范围 |
GL_INVALID_OPERATION |
当前状态下非法操作 |
GL_STACK_OVERFLOW |
命令会导致栈溢出 |
GL_STACK_UNDERFLOW |
命令会导致栈下溢 |
GL_OUT_OF_MEMORY |
执行命令时内存不足 |
GL_NO_ERROR |
没有发生错误。 |
通过设置断点来检测错误
在你的代码中设置断点为你提供了在任何点停止执行的能力,这样你可以调查并逐步执行以找出为什么某段代码不能正确工作。如果你想要逐步执行特定的 OpenGL 函数,这尤其有用。这些断点应该在调用函数之前立即设置,并且你的程序将被停止,状态栏将指示哪个函数导致了断点。
在你的代码中设置断点
虽然你可以使用调试器在任何时候暂停程序的执行并查看运行代码的状态,但在运行可执行文件之前设置断点通常更有帮助,这样你可以在已知点停止,并查看源代码中变量的值。
断点基本上是代码中的一条指令,告诉应用程序在达到断点时停止,程序执行暂停,等待进一步的指令以确定下一步操作。在这个阶段,你有机会检查任何属性当前值,或者逐步执行代码。
让我们看看以下使用 glGetError 方法的例程。
- (void)startAnimation{
if (!animating) {
CADisplayLink *aDisplayLink = [[UIScreenmainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
[aDisplayLinksetFrameInterval:animationFrameInterval];
[aDisplayLinkaddToRunLoop:[NSRunLoopcurrentRunLoop] forMode:NSDefaultRunLoopMode];
self.displayLink = aDisplayLink;
GLenum err;
err = glGetError();
while ( GL_NO_ERROR != err ) {
NSLog(@"Error. glError: 0x%04X", err);
err = glGetError();
}
animating = YES;
}
}
你会注意到我们已声明了一个变量 err,它将用于存储 glGetError 方法将返回的错误号。然后我们遍历并输出每个错误消息的详细信息到调试控制台窗口,直到没有更多错误为止,然后退出循环。虽然你可以使用 Xcode 4 调试器在任何时候暂停程序的执行以查看运行代码的状态,但在运行应用程序之前在那些区域设置断点更有帮助。
要设置断点,请打开任何源实现文件,并在 Xcode 源编辑器的行号区域(gutter pane section)中点击,点击位置是你希望程序停止的地方。当你添加断点时,Xcode 会自动启用它,并在下面的屏幕截图中以浅蓝色显示。通过再次点击断点,也可以切换断点以关闭,此时颜色会变得更加透明。
断点导航器窗口显示项目中设置的所有当前断点,并将显示所有活动和非活动断点。

此视图包含可以针对每个断点配置的几个选项,一个断点可以包含多个条件。您可以选择将消息记录到 Xcode 控制台窗口,或执行一个debug命令。要访问此视图,请按住控制键,并用鼠标右键单击。
Xcode 4.2 带来的一个新功能是能够捕获 OpenGL 帧,这样您就可以在 Xcode 开发环境中直接调试代码,滚动查看每个 OpenGL ES 方法调用,以及查看状态和对象。
设置条件 OpenGL ES 入口点断点
我们已经讨论了在您希望应用程序在特定行被击中时停止的情况下设置代码中的断点。另一种使用断点的方法是,当满足特定条件时停止,然后执行特定的操作,如下面的示例所示:

您可以选择在条件满足时停止,如图所示,或者您可以选择完全忽略条件,并在方法被调用指定次数后执行。
然后,您可以选择执行特定的操作。如从下面的屏幕截图中可以看到,我们设置了一个条件,当变量transY大于或等于2时捕获当前帧。这将启动OpenGL 帧捕获部分,以便我们可以逐步调试代码,查看发生了什么。
我们还可以通过使用Instruments来调试 OpenGL ES 项目,这些内容将在第六章Xcode 工具改进中介绍。
在帧边界上断点
OpenGL ES 调试器允许您查看应用程序中正在绘制的所有帧。您可以让应用程序在程序中的某个特定点中断,然后使用调试导航器导航到代码中帧被绘制的地方。在下面的屏幕截图中,它显示了捕获的帧实例,并显示了与该帧相关联的状态调用:

选择如图所示的glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)选项,将允许您看到所有相关绘制调用的列表。您还可以使用滚动条循环查看之前屏幕截图中捕获的帧。

点击如图所示的[OpenGLESExampleViewController drawframe]方法,将打开 Xcode 开发 IDE,并直接带您到代码所在的位置,如下面的屏幕截图所示:

通过使用 OpenGL ES 调试器,你可以逐帧遍历你的应用程序,帮助你追踪和调试纹理渲染不正确的情况,或者如果对象的颜色看起来很奇怪。
摘要
在本章中,我们学习了顶点着色器和片段着色器之间的区别,以及它们之间的关系。然后我们探讨了 OpenGL ES 的新调试功能,以及这个新的 Xcode 调试器如何允许你在 Xcode IDE 中追踪代码中特定的 OpenGL ES 问题。
我们熟悉了 OpenGL ES 帧捕获工具,它能够停止程序的执行,并抓取 iOS 设备上正在渲染的当前帧内容。这样我们就能轻松追踪并纠正程序问题,通过仔细查看对象的状态信息,通过滚动调试导航器堆栈跟踪,以及查看应用程序当前正在使用的所有纹理和着色器。
我们还学习了 OpenGL ES 的glGetError方法调用,以及我们如何使用它来提供已检测到的错误列表。为了结束本章,我们探讨了如何在 OpenGL 中在帧边界处中断,并查看由对象定义的当前程序帧状态。
在下一章中,我们将了解什么是故事板,以及我们如何应用视图之间的各种过渡,以及如何创建和配置场景和故事板文件,以程序化地展示它们。我们还将探讨如何构建和集成 Twitter 功能到我们的应用程序中,以发送照片和标准消息。
第四章:使用故事板
从 Xcode 4.2 的发布开始,开发者和设计师现在可以使用新加入 Interface Builder 的故事板功能来布局他们应用程序的工作流程。故事板可以用来构建游戏菜单系统,在屏幕之间切换,或者它们可以用来构建使用导航栏和标签栏控件在不同视图之间切换的商业应用程序,因为它们管理由开发者创建的视图控制器。
以前,您不是创建多个界面文件,现在您可以在一个地方开始拖放并编辑所有视图,同时能够指定屏幕之间的过渡以及触发它们的关联操作。故事板还包括一种可以实施的设计模式,用于在控制器之间发送和接收数据。在之前的实例中,您将不得不实现协议、代理、通知或某种其他自定义方式来维护屏幕之间的状态。
在本章中,我们将深入了解故事板实际上是什么,以及熟悉在 Interface Builder 中实施的新工作流程。我们将查看创建故事板的步骤以及如何在不同视图之间应用不同的过渡技术,以创建一个用于发布消息和照片的 Twitter 应用程序。
在本章中,我们将:
-
了解故事板是什么
-
学习如何使用故事板创建和配置场景之间的过渡
-
创建一个简单的具有 Twitter 集成的故事板应用程序
-
学习创建故事板文件的过程
-
发布推文消息并添加照片
-
以编程方式过渡到新的故事板视图控制器
在本章中,我们将涵盖一些精彩的内容,让我们开始吧。
理解故事板
在过去,当您需要为您的应用程序创建一个新视图时,您将不得不为每个视图创建一个新的 xib 文件。当处理复杂的应用程序时,这变得非常繁琐,因为它们包含了许多不同的视图,并且从视图控制器切换到下一个视图变得困难。
苹果决定大幅改进这一领域,通过在用户界面设计过程中引入一种称为故事板的技术,在用户界面设计方面进行了实质性的改进。
故事板是 Xcode 4.2 及其后续版本中内置的功能,它允许将 iOS 应用程序的各个屏幕以及通过这些屏幕的导航路径进行视觉组装。当您使用故事板时,它们使您能够设计屏幕的应用程序工作流程,类似于电影导演为每个拍摄场景准备故事板草图的方式。
你可以使用 Interface Builder 图形化地布局每个屏幕的各个部分,以及它们之间的过渡,以及触发过渡的控制。
以下截图显示了一个简单的 Storyboard 应用程序,其中包含两个视图控制器,它们之间存在链接。

过渡
Xcode 提供了更改在 Storyboard 中从一个场景到另一个场景发生的过渡视觉外观的选项,这被称为segue。使用过渡可以使你为每个要渲染和显示到视图中的视图控制器应用各种不同的样式,这些样式由视图控制器之间的箭头表示。默认情况下,执行垂直覆盖过渡,其中新场景从视图底部垂直向上滑动以覆盖当前显示的场景。
你可能已经在应用程序中看到过这样的过渡,例如 iPhone 和 iPad 中包含的 Photos 应用程序,在那里你可以应用过渡并开始幻灯片放映。
你还可以定义自定义过渡,这使你能够提供一个自定义 segue 类方法来处理过渡。这可以通过将 segue 的样式选择为自定义,并填写要使用的自定义 segue 类名称来实现。要使用任何标准 segue 类,这些类位于UIKit类中。
注意
有关标准 segue 类的信息,请参阅位于 Apple 开发者连接网站上的UIKit框架参考,使用以下链接:developer.apple.com/library/ios/#documentation/uikit/reference/UIKit_Framework/_index.html。
为了配置 segue 以指定在场景之间使用的一种过渡,请单击 segue 并打开属性检查器,如图下所示:

你可以选择仅适用于模态样式的各种过渡类型;这些类型在以下表中解释:
| 过渡名称 | 描述 |
|---|---|
| 默认 | 当选择此过渡时,它使用垂直覆盖过渡样式。 |
| 垂直覆盖 | 当呈现视图控制器时,其视图从屏幕底部滑动向上。当视图消失时,它滑动回原位。 |
| 水平翻转 | 当呈现视图控制器时,当前视图从右到左开始一个水平 3D 翻转,从而像在先前视图的背面一样揭示新视图。当此视图消失时,翻转从左到右发生,返回到原始视图。 |
| 交叉溶解 | 当视图控制器被展示时,当前视图淡出,同时新视图淡入。当视图被关闭时,使用类似类型的交叉溶解来返回到原始视图。 |
| 部分卷曲 | 当视图控制器被展示时,当前视图的一个角落卷曲起来以显示下面的模态视图。当视图被关闭时,卷曲的页面会自动展开回到模态视图的顶部。使用这种转换方式展示的模态视图本身被阻止展示任何额外的模态视图。这种转换样式仅在父视图控制器展示全屏视图且你使用UIModalPresentationFullScreen模态展示样式时才受支持。尝试使用不同的父视图形式因子或不同的展示样式将触发异常。 |
注意
关于上述转换类型的更多信息,请参考位于苹果开发者网站上的UIViewController框架参考,使用以下链接:developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html。
现在我们已经了解了如何为视图创建转换,我们的下一步是看看我们如何创建 Storyboard 以及如何为 Storyboard 应用程序配置场景。
如何创建 Storyboard 文件
在下一节中,我们将探讨如何创建 Storyboard 应用程序。当你创建一个新的 Storyboard 文件时,这将为你提供一个代表场景的视图控制器对象,这是初始视图控制器。
每个视图控制器代表单个屏幕的内容。当为 iPad 创建应用程序时,一个屏幕可以由多个场景的内容组成,并且你需要将包含在视图控制器中的每个对象链接到实现另一个场景的另一个视图控制器。

如从这张截图中所见,初始视图控制器包含一个绿色轮廓。你可以通过 Interface Builder 使用Control + 拖动在控件和视图控制器之间来链接各种视图控制器。你可以在每个视图控制器的视图中添加控件和视图,就像你向窗口或 XIB 文件中的视图添加对象一样。
创建一个简单的 Storyboard(Twitter)应用程序
在我们继续之前,我们首先需要创建我们的TwitterExample项目。为了唤起你的记忆,你可以参考我们在第二章中介绍的部分。
-
从
/Developer/Applications文件夹中启动 Xcode。 -
选择创建一个新的 Xcode 项目,或文件 | 新项目。
-
从项目模板对话框中选择单视图应用程序模板。
![创建简单的 Storyboard(Twitter)应用程序]()
-
然后,点击下一步按钮继续向导的下一步。这将允许您输入产品名称和您的公司标识符。
![创建简单的 Storyboard(Twitter)应用程序]()
-
在产品名称中输入
TwitterExample,并确保您已从设备家族下拉框中选择iPhone,并且已勾选使用故事板选项。 -
接下来,点击下一步按钮继续向导的最终步骤。
-
选择您希望保存项目的文件夹位置。
-
然后,点击创建按钮以在指定位置保存您的项目。
一旦您的项目创建完成,您将看到 Xcode 开发界面,以及模板在项目导航器窗口中为您创建的项目文件。我们的下一步是开始构建我们的 Twitter 应用程序的用户界面。
创建场景
创建场景的过程涉及将一个新的视图控制器添加到故事板中,这被称为场景。每个视图控制器负责管理单个场景。更好地描述场景的方式是将场景集合想象成一部电影,其中正在显示的每一帧都是实际连接到下一部分的场景。
当向您的故事板文件添加场景时,您可以在视图控制器视图中添加控件和视图,就像您为 XIB 文件做的那样,并且能够在视图控制器及其视图之间配置出口和动作。
要将新场景添加到您的故事板文件中,请按照以下简单步骤操作:
-
在项目导航器中,选择名为
MainStoryboard.storyboard的文件。 -
从对象库中选择并拖动一个新的视图控制器到故事板画布上。这在上面的屏幕截图中显示:
![创建场景]()
-
接下来,将一个
UIButton控件拖动到我们将在后续部分使用的视图中。在按钮的属性中,将文本更改为读取返回。 -
最后,在第一个视图控制器上,将一个
UIButton控件拖动到视图上,位于推文消息按钮上方。在按钮的属性中,将文本更改为读取关于应用。这将用于调用我们在上一步中添加的新视图。一旦您已将控件添加到每个视图中,您的最终界面应类似于以下屏幕截图所示。
![创建场景]()
-
接下来,为关于应用按钮创建动作事件;在键盘上按住控制键,并将鼠标从关于应用按钮拖动到
ViewController.h接口文件。 -
从连接类型下拉菜单中选择动作(Action),然后输入
showAbout作为要创建的IBAction方法的名称,然后点击连接(Connect)按钮以接受更改,如图所示:

现在我们已经创建了场景、按钮和动作,我们的下一步是配置场景,这将在下一节中展示。
配置场景
当你想从一个视图控制器切换到另一个视图控制器时,你可以按住控制键并点击一个按钮、表格视图单元格或任何其他来自一个视图控制器的对象,然后将其拖动到新的视图控制器以创建不同的场景。这种在视图控制器之间拖动的技术被称为转场(Segue)。
转场是一个可配置的对象,支持UIKit类参考中提供的所有类型的转换,例如模态转换和导航转换。
你还可以定义自定义转换,用另一个视图控制器替换一个视图控制器。要创建转场并配置场景,请按照以下简单步骤操作:
-
使用鼠标选择关于应用按钮,并按住控制键将其拖动到当按钮被选中时要加载的场景视图控制器。
-
释放鼠标按钮,然后从弹出菜单选择模态(Modal)选项。配置场景
- 你会注意到一个灰色箭头连接了两个视图控制器。当按下关于应用按钮时,它将显示包含返回按钮的页面。
-
接下来,我们需要为我们的第二个视图做同样的事情,以便当按下返回按钮时,它将返回到我们的第一个视图。
-
重复步骤 1到步骤 2,但将返回按钮替换为关于应用按钮。
Xcode 4 中包含的 Storyboard 转场解释如下表所示:
转场名称 描述 模态(Modal) 模态视图控制器不是 UIViewController类的特定子类,因为任何类型的视图控制器都可以通过你的应用程序以模态方式呈现。然而,就像标签栏和导航视图控制器一样,当你想要传达前一个视图层次结构和新呈现的视图层次结构之间的特定关系时,你可以以模态方式呈现你的视图控制器。推送(Push) 推送(Push)转场允许你将新的视图控制器推送到导航堆栈中,就像堆叠盘子一样。堆栈顶部的视图是渲染的视图。 自定义 这些允许你使用 prepareForSegue方法从代码中自定义和调用视图控制器,并且是你用来展示应用程序内容的方式。视图控制器的工作是管理某些内容的展示,并协调内容的更新以及与应用程序底层数据对象的同步。在自定义视图控制器的情况下,这涉及到创建一个视图来展示内容,并实现将视图内容与你的应用程序数据结构同步所需的基础设施。- 完成这些操作后,你的视图控制器应该看起来像下一张截图所示的那样。你可以为每个视图控制器应用多种过渡效果,以便它们在显示到视图时执行动画。
![配置场景]()
- 要了解如何将过渡效果应用到你的视图控制器上,请参阅本章中关于过渡的部分。
-
现在你已经将每个 segues 应用到视图控制器上,我们的最后一步是编译、构建并运行我们的应用程序。
-
构建 Twitter 应用程序
以下截图显示了我们的应用程序在 iOS 模拟器中运行,以及它们各自关联的屏幕显示。
接下来,将一个UITextView拖到你的视图中,并调整大小以适应合理数量的文本输入,并确保删除此控件中显示的默认文本。
所以,这就是全部内容。在本节中,我们学习了如何创建和添加新场景到我们的主故事板中,以及当按钮被按下时如何链接和配置每个场景的过程。

我们还可以通过编程方法过渡到故事板中的场景。当我们开始本章的以编程方式展示故事板视图控制器部分时,我们将更详细地探讨这一点。
Twitter 为我们提供了一些非常简单的 API 来关注,这使得与它们交互变得轻而易举。在本节中,我们将探讨如何使用这些 API 发布推文消息并添加图片。
-
从产品菜单中选择运行。或者,按Command | R键编译、构建并运行应用程序。
-
从产品菜单中选择运行。或者,按Command | R键编译、构建并运行应用程序。
-
最后,我们需要将一个
UIButton拖到视图中以处理消息的发布。在按钮的属性中,将文本更改为“推文消息”。你的最终界面应该看起来像这样:![构建 Twitter 应用程序]()
-
接下来,打开
ViewController.h接口文件,并创建以下突出显示的条目,如下面的代码片段所示:#import <UIKit/UIKit.h> @interface ViewController :UIViewController { UIButton *postTweet; } @property (nonatomic, retain) IBOutlet UIButton *postTweet; @end -
接下来,我们需要连接我们的Tweet Message按钮控件,并创建用于发布推文的
IBAction事件。我们需要确保使用的事件类型是UIButton的TouchUpInside方法。 -
要创建一个动作事件,请按住键盘上的Control键,然后将鼠标拖动到以下截图所示的
ViewController.h接口文件。 -
将要创建的
IBAction方法的名称输入为postTweet,然后点击Connect按钮以接受更改。

现在我们已经将IBAction事件连接到用于发布推文的调用方法,我们的下一步是在查看如何实现此代码之前,将 Twitter 框架添加到我们的项目中。
要将 Twitter 框架添加到你的项目中,请选择Project Navigator Group,然后按照以下简单步骤操作:
-
在Project Navigator中选择你的项目。
-
然后,从TARGETS组下选择你的项目目标。
-
选择Build Phases标签。
-
展开链接二进制与库的展开三角形。
-
最后,使用+来添加你想要的库。如果你在列表中找不到 Twitter 框架,也可以进行搜索。
如果你仍然不清楚如何添加框架,请参考以下截图,它突出显示了你需要选择的区域:

现在我们已经将Twitter.framework添加到我们的项目中,我们可以看看如何实现使用此框架发布推文消息的代码。
编写推文消息
每当你想要提交 Twitter 消息时,你都需要使用TWTweetComposeViewController类实例。这个类处理所需的一切,并为我们提供一个推文编辑表单,以便我们可以开始输入推文消息。此类还允许你设置要使用的初始 Twitter 文本信息,以及如何添加图片和 URL。
注意
关于TWTweetComposeViewController类的更多信息,你可以参考位于以下位置的 Twitter 框架参考文档:http://developer.apple.com/library/ios/#documentation/Twitter/Reference/TWTweetSheetViewControllerClassRef/Reference/Reference.html。
在以下代码片段中,我们看看如何使用TWTweetComposeViewController类来编写推文。
在我们能够在应用程序中使用 Twitter 的功能之前,我们需要包含 Twitter 框架的头文件。
-
从Project Navigator中打开
ViewController.m实现文件,并输入以下import语句,如下所示:#import <Twitter/Twitter.h> #import <Twitter/TWTweetComposeViewController.h> -
接下来,我们需要实现代码以显示 Twitter 推文表单,我们可以在这里编辑,然后发布我们的消息。打开位于
TwitterExample文件夹中的ViewController.m实现文件,并输入以下代码片段:- (IBAction) postTweet: (id) sender { TWTweetComposeViewController *myTwitter = [[TWTweetComposeViewController alloc] init]; [myTwitter setInitialText:@"Welcome to iOS 5 and Xcode 4.2, using the Twitter API."]; [self presentModalViewController:myTwitter animated:YES]; // Retrieve the result of the Twitter handler to // determine if the message was successfully sent. myTwitter.completionHandler = ^(TWTweetComposeViewControllerResult res){ if (res == TWTweetComposeViewControllerResultDone) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Success" message:@"Your Tweet was posted successfully." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; [self dismissModalViewControllerAnimated:YES]; } else if (res == TWTweetComposeViewControllerResultCancelled) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Your Tweet was not posted." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; [self dismissModalViewControllerAnimated:YES]; } }; } -
在这个代码片段中,我们声明了一个变量
myTwitter,并将其赋值为我们的TWTweetComposeViewController类的一个实例。然后我们通过设置setInitialText方法,给我们的编辑表单设置了一些文本,并将其显示到视图中。然后我们设置了一个处理程序,使用completionHandler方法来通知我们用户已经完成了推文的编辑,并根据方法返回的结果显示了相关的警告。 -
可选地,你可以在向用户展示视图之前,使用
canSendTweet类方法检查 Twitter 是否已设置并可达,如下面的代码片段所示:BOOL isSUCCESS = TWTweetComposeViewController.canSendTweet; if (isSUCCESS== YES){ Do something… } // Twitter account credentials have not been set up correctly. else{ UIAlertView *alertView = [[UIAlertViewalloc] initWithTitle:@"Twitter Error" message:@"Your Twitter account has not been set up correctly." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; }
在上述代码片段中,我们使用了TWTweetComposeViewController类的canSendTweet类方法。这个方法会检查用户是否正确安装并正确设置了 Twitter。如果没有这样做,这个语句将失败,并将返回一个NO(或FALSE)值给isSuccess变量。
向推文添加照片
每当你想要向推文消息中添加图片以提交时,你需要使用TWTweetComposeViewController类实例。这个类处理所有必需的操作,并展示给我们一个推文编辑表单,以便我们可以添加图片和 URL。
在下一个代码片段中,我们看看如何使用TWTweetComposeViewController类轻松地向现有的 Twitter 消息添加图片。
打开位于项目导航器中的TwitterExample文件夹内的ViewController.m实现文件,找到postTweet方法,并输入以下高亮显示的代码,如下所示:
- (IBAction) postTweet: (id) sender {
// Attach an image to our Tweet message
TWTweetComposeViewController *myTwitter = [[TWTweetComposeViewControlleralloc] init];
[myTwitter addImage:[UIImage imageNamed:@"Blue-Aqua-Apple.png"]];
[self presentModalViewController:myTwitteranimated:YES];
}
在这个代码片段中,我们声明了一个变量myTwitter,并将其赋值为我们的TWTweetComposeViewController类的一个实例。然后我们使用addImageinstance方法向推文消息中添加一张图片,并将带有图片的视图展示给用户。
现在我们已经将最后一部分代码添加到TwitterExample应用中,我们需要在构建和运行应用之前首先配置我们的 Twitter 账户信息。按照以下步骤设置和配置 Twitter。
-
从 iOS 主屏幕打开设置应用。
-
从设置菜单中选择Twitter选项。
-
输入你的用户名和密码凭证信息,然后点击登录按钮。如果你没有 Twitter 账户,你可以通过选择创建新账户选项在此屏幕上创建一个。
-
我们的最后一步是编译、构建并运行我们的
TwitterExample应用,可以通过在 Xcode IDE 中点击播放按钮,或者使用Command + R快捷键。以下截图显示了当 TwitterExample 应用程序在 iOS 模拟器上运行时的样子。

当你开始编写推文消息时,你可以选择将你的当前地理位置添加到消息中。此功能基本上使用 Google Maps API 来映射推文,并给 Twitter 用户提供在twitter.com/上推文位置并允许他人查看 Google Maps 上精确位置的选择。
你还可以将附件添加到已编写的消息中,这可以是任何有效的图像(PNG、JPG 等)。点击发送按钮将消息提交到你的 Twitter 账户,你将收到一条消息,说明推文已成功发布。
以下截图显示了由前一个截图提交的推文条目,它将显示在twitter.com/:上的样子。

在本节中,我们探讨了如何将类似 Twitter 的功能集成到我们的应用程序中。通过包含 Twitter,应用程序可以以多种方式变得更加社交。例如,你可以在解锁游戏中的特殊物品、完成游戏或只想上传你的高分成就时自动推文。
这让他们的所有朋友都知道他们正在玩你的游戏,这反过来又让你获得更多的曝光。另一个例子可能是一个商业应用程序,它可能允许用户推文他们已完成的项目数量。鉴于 Twitter 最近受到如此多的关注,不将某种 Twitter 集成到自己的 iOS 应用程序中简直是疯了。
准备过渡到新的视图控制器
当用户在当前场景中触发 segue 时,故事板运行时会调用当前视图控制器的prepareForSegue:sender:方法。此方法给当前视图控制器一个机会,将任何所需的数据传递给即将显示的视图控制器。
为了以编程方式执行 segue,请按照以下简单步骤操作:
-
确保你已经在两个
UIViewControllers之间绘制了一个 segue。 -
接下来,点击 segue 并填写标识符字段,使用一个唯一的名称,如以下截图所示:
![准备过渡到新的视图控制器]()
-
现在,从
UIButton的IBAction级别运行prepareForSegue:segue:sender:方法调用,如下面的代码片段所示:- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ // Check to see that we are processing the correct // segue, before processing the alert. if ([segue.identifierisEqualToString: @"secondViewController"]) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle: @"TwitterExample" message:@"Currently displaying View #2" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } }
在此代码片段中,我们使用控制器的标识符执行与控制器关联的 segue 调用。在显示警报之前,我们首先检查确保我们正在处理正确的 segue。
以这种方式处理可以让我们自定义转场,并且只要标识符是唯一的,就会将任何过渡应用到你的故事板中位于场景内的场景。
注意
关于如何实现UIViewController类的方法的信息,你可以参考以下位置的UIViewController类参考:http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/cl/UIViewController。
以编程方式呈现故事板视图控制器
虽然故事板运行时通常处理视图控制器之间的过渡,但你也可以通过编程方式直接在代码中触发转场。你可能选择在 Interface Builder 中设置转场时这样做,或者你可能想使用加速度计事件来触发过渡并显示图形动画。
如果你查看以下示例代码片段,你将能够看到我们首先使用UIStoryboard的instantiateViewControllerWithIdentifier方法编程地加载视图控制器。最后,我们通过将其推入导航堆栈来呈现视图控制器。
// SampleViewController
- (void)viewDidLoad{
[super viewDidLoad];
// Instantiate the Samplesubview controller
// from the storyboard.
SampleViewController *subviewController = [self.mainStoryboard instantiateViewControllerWithIdentifier:@"subviewController"];
// Note: the "subviewController" Identifier value must
// be set in the Attributes Inspector on the
// subviewController scene.
// Add to self as child controller
[self addChildViewController:subviewController];
[self mainSubviewaddSubview:subviewController.view];
}
在本例中,我们将探讨如何将额外的视图控制器子类添加到我们的故事板中,并使用performSegueWithIdentifier方法调用编程地确定我们处于哪个视图。那么,让我们开始吧。
我们需要创建一个新的UIViewController子类文件,该文件将用于我们的第二个视图控制器。要创建UIViewController子类文件,请按照以下简单步骤操作:
-
从Xcode IDE菜单中选择文件 | 新建 | 新文件。
-
接下来,从可用模板列表中选择要使用的UIViewController 子类模板,如图所示。
![以编程方式呈现故事板视图控制器]()
-
在以下屏幕截图中输入
secondViewController作为要创建的类的名称:![以编程方式呈现故事板视图控制器]()
-
确保你选择
UIViewController作为要创建的子类名称。 -
确保你没有勾选针对 iPad和带有 XIB 用户界面两个复选框。
-
指定保存类文件的路径,然后点击创建按钮。
完成后,你将返回到 Xcode IDE。你的
secondViewController的接口和实现文件将出现在项目导航器窗口中。 -
在项目导航器中,从TwitterExample文件夹下打开ViewController.h接口文件。
-
修改文件并包含以下代码片段中指定的突出显示的代码部分:
// // ViewController.h // TwitterExample // #import <UIKit/UIKit.h> @interface ViewController : UIViewController <UIActionSheetDelegate, UIAlertViewDelegate>{ UIButton *postTweet; UIButton *aboutApp; } @property (nonatomic, retain) IBOutlet UIButton *postTweet; @property (nonatomic, retain) IBOutlet UIButton *showAbout; - (IBAction)postTweet:(id)sender; - (IBAction)showAbout:(id)sender; @end -
在此代码片段中,我们正在设置我们的代理对象,以便在视图控制器之间传递信息。
-
在Project Navigator中,从
TwitterExample文件夹中打开位于ViewController.m实现文件。 -
修改文件,并包含以下代码片段中指定的突出显示的代码部分:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ // Check to see that we are processing the correct segue, //before processing the alert. if ([segue.identifierisEqualToString: @"firstViewController"]) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle: @"TwitterExample" message:@"Currently displaying View #2" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } }
在此代码片段中,我们所做的是确定我们视图控制器的当前视图,并确保我们处于firstViewController中。我们通过检查segue属性,并获取我们之前声明的标识符值来实现这一点。如果我们处于正确的视图,则会在当前视图中显示一个弹出警告。
-
在Project Navigator中,从
TwitterExample文件夹中打开位于secondViewController.h接口文件。 -
修改文件,并包含以下代码片段中指定的突出显示的代码部分:
// // secondViewController.h // TwitterExample // #import <UIKit/UIKit.h> @interface secondViewController :UIViewController <UIActionSheetDelegate, UIAlertViewDelegate> - (IBAction)GoBack:(id)sender; @property (strong, nonatomic) IBOutlet UIButton *GoBack; @end -
在此代码片段中,我们所做的是设置我们的代理对象,以便在视图控制器之间传递信息。
-
在Project Navigator中,从
TwitterExample文件夹中打开位于secondViewController.m实现文件。 -
修改文件,并包含以下代码片段中指定的突出显示的代码部分:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ // Check to see that we are processing the correct segue, // before processing the alert. if ([segue.identifierisEqualToString: @"secondViewController"]) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle: @"TwitterExample" message:@"Currently displaying View #1" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } } -
在这里,我们正在确定我们视图控制器的当前视图,并确保我们处于
secondViewController中。我们通过检查segue属性并获取我们之前声明的标识符值来实现这一点。如果我们处于正确的视图,则会在当前视图中显示一个弹出警告。 -
选择我们刚刚创建的第二个视图控制器,然后在身份检查器部分中,点击Custom Class标题栏,并将Class更改为
secondViewController,如图所示:![以程序方式呈现故事板视图控制器]()
-
在Inspector属性部分下,在Storyboard Segue部分中,输入
secondViewController作为在视图之间移动时要使用的标识符,如图所示:![以程序方式呈现故事板视图控制器]()
-
接下来,我们需要为我们的第一个视图控制器应用相同的 Storyboard Segues。
-
选择Inspector属性部分,然后在Storyboard Segue部分中,输入
firstViewController作为要使用的唯一标识符。 -
我们需要重复与
secondViewController相同的步骤。 -
我们的最后一步是通过在 Xcode IDE 中点击Play按钮或按Command + R来编译、构建和运行我们的
TwitterExample应用程序。以下截图显示了在 iOS 模拟器中运行的
TwitterExample应用程序,显示了在故事板中定义的每个视图控制器之间的程序性转换。

当你点击关于应用按钮时,它将过渡到第二个视图控制器,然后根据prepareForSegue:(UIStoryboardSegue*)segue方法调用显示消息,确定当前在视图中显示的当前视图控制器的标识符。
当你点击返回按钮时,这将控制权转移到第一个视图控制器,会调用prepareForSegue方法,以确定当前视图的当前标识符。
摘要
在本章中,我们学习了故事板实际上是什么,它们是如何工作的,如何添加场景并在故事板中配置这些场景,以及如何应用不同的过渡方法。
我们学习了 Twitter 框架以及我们如何使用可用的 Twitter API 集合成功发布消息和图片到 Twitter 账户。为了结束本章,我们探讨了如何使用各种方法在主故事板中的每个视图控制器之间进行过渡,无论是通过编程还是使用故事板过渡。
在下一章中,我们将学习AirPlay和Core Image框架,并探讨我们如何使用和实现这些框架到我们的应用中。我们将了解不同的图像过滤效果,以及如何在我们的应用中展示这些效果并将输出发送到外部设备,例如 Apple TV。
第五章:使用 AirPlay 和 Core Image
从 iOS 4.2 的发布开始,开发者可以使用 AirPlay 将视频、音频和照片流式传输到支持 Apple TV 的设备。在 iOS 5 中,现在甚至更容易通过 Apple TV 自动无线镜像 iPad 2 上的所有内容到 HDTV。
随着 iOS 5 中提供的附加 API 集,使用 AV Foundation 框架构建的应用程序现在支持加密的音频和视频流,这些流通过 HTTP Live Streaming 传输,并且可以在每个 HDTV 和 iPad 2 屏幕上显示不同的内容。
Core Image 框架是一个硬件加速框架,它提供了一种简单的方式来增强照片和视频。这使得你可以在相机和图像编辑应用程序中创建惊人的效果。Core Image 提供了几个内置的过滤器,如色彩效果、扭曲和过渡。它还包括高级功能,如自动增强、红眼减少和面部识别。
在本章中,我们将更深入地了解这些框架是什么,以及如何在我们的应用程序中实现它们。我们将查看如何在我们的应用程序中集成 AirPlay,并使用 Apple TV 将信息导向另一个输出设备。我们还将查看 Core Image 框架,以及如何使用CIImage类使用各种过滤器效果。
在本章中,我们将:
-
了解 AirPlay 和 Core Image 框架
-
创建一个简单的 AirPlay 和 Core Image 应用程序
-
学习如何将应用程序内容输出到 Apple TV 设备
-
查看如何使用
CIImage类应用各种过滤器效果,用于扭曲、过渡和色彩效果
本章我们将探讨一些精彩的内容。那么,让我们开始吧。
理解 AirPlay 框架
AirPlay 框架是一个更新的框架,允许您从任何基于 iOS 的设备流式传输音频和视频内容到任何支持 Airplay 的设备,这些设备能够播放音频和视频,例如电视和音频系统。从 iOS 5 开始,开发者现在可以灵活地将 Airplay 内容集成到他们的应用程序中,并将这些信息输出到附近的 Apple TV 2 接收器。
在本节中,我们将查看如何创建一个简单的应用程序,在 iOS 设备上播放视频内容,然后查看将此内容输出到 Apple TV 2 设备所需的步骤。
创建一个简单的 AirPlay 应用程序
播放视频是任何 iOS 设备上最常见的一项任务,所有视频都必须全屏播放和显示。在我们能够播放任何视频之前,我们需要将媒体播放器框架添加到我们的应用程序项目中。
在我们继续之前,我们首先需要创建我们的AirPlayExample项目。为了回忆如何创建新项目,你可以参考我们在第一章中覆盖的章节,名为Creating the MyEmailApp application下的What's New in iOS5。
-
从
/Xcode4/Applications文件夹中启动 Xcode。 -
选择Create a new Xcode project,或File | New Project。
-
从可用模板列表中选择Single View Application模板。
-
在Device Family下拉菜单中选择iPhone。
-
点击Next按钮继续到向导的下一步。
![创建一个简单的 AirPlay 应用]()
-
将项目名称输入为
AirPlayExample,然后点击Next按钮继续到向导的下一步。 -
指定你想要保存项目的位置。
-
点击Save按钮继续并显示 Xcode 工作区环境。
现在我们已经创建了AirPlayExample项目,我们需要向项目中添加一个重要的框架,以使我们的应用具有播放电影文件的能力。要将媒体播放器框架添加到你的项目中,选择Project Navigator Group,然后按照以下简单步骤操作:
-
点击并从Project Navigator中选择你的项目。
-
然后从TARGETS组下选择你的项目目标。
-
选择Build Phases标签。
-
展开Link binary with Libraries三角符号。
-
最后,使用+来添加你想要的库。
-
从可用框架列表中选择MediaPlayer.framework。如果你在列表中找不到所需的框架,也可以进行搜索。
如果你仍然不清楚如何添加框架,可以参考以下截图,它突出显示了你需要选择的部分(被红色矩形包围):

现在你已经将MediaPlayer.framework添加到你的项目中,我们需要开始构建我们的用户界面,该界面将负责播放电影:
-
从 Project Navigator 中选择并打开ViewController.xib文件。
-
从Object Library中选择并拖动一个圆角矩形按钮(
UIButton)控件,并将其添加到我们的视图中。 -
根据需要调整大小,然后修改圆角矩形按钮的Object Attributes部分,并将其标题设置为
Play Movie。
我们不需要添加停止按钮,因为我们将在电影播放完毕时添加一个事件来处理它。如果你正确地遵循了步骤,你的视图应该看起来像以下截图所示。如果它看起来和我的一样,请随意调整你的:

如您所见,我们的表单在这个阶段并没有做什么,如果您在模拟器中运行此应用程序,您会看到控件放置在您的屏幕上。以下步骤将向您展示如何将按钮连接到操作事件,每个事件将执行播放视频的任务。所以让我们开始吧:
-
打开
ViewController.h接口文件,并创建以下突出显示的条目,如代码片段所示:#import <UIKit/UIKit.h> #import <MediaPlayer/MediaPlayer.h> @interface ViewController : UIViewController -(IBAction)playMovie:(id)sender; @end -
我们需要创建一个操作事件。选择 播放电影 按钮,并按住 Control 键,将其拖入以下截图所示的
ViewController.m实现文件类中:![创建一个简单的 AirPlay 应用程序]()
-
为您要创建的操作指定一个名称。输入
playMovie作为操作的名称。 -
设置事件类型为 Touch Up Inside。
![创建一个简单的 AirPlay 应用程序]()
-
点击 连接 按钮让 Xcode 创建事件。
-
现在我们需要向
playMovie函数中添加代码,该函数将处理播放我们的示例电影文件。为此函数输入以下代码片段:-(IBAction)playMovie:(id)sender{ NSString *filepath = [[NSBundle mainBundle] pathForResource:@"GenieCompanyVideo" ofType:@"mp4"]; NSURL *fileURL = [NSURL fileURLWithPath:filepath]; MPMoviePlayerController *moviePlayerController = [[MPMoviePlayerController alloc]initWithContentURL:fileURL]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackComplete:) name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayerController]; [self.view addSubview:moviePlayerController.view]; moviePlayerController.fullscreen = YES; moviePlayerController.scalingMode = MPMovieScalingModeAspectFit; [moviePlayerController play]; }
我们刚刚声明了一个变量(NSString)filePath,它将包含电影文件的文件路径。接下来,我们创建一个(NSURL)fileUrl,将文件路径转换为对象,这是 MPMoviePlayerController 在初始化时所需的。然后我们将 MPMoviePlayerController 视图添加到我们的自定义视图控制器中,以便它在屏幕上显示。我们指定要全屏显示,最后我们告诉 moviePlayerController 开始播放。
由于我们已经为 moviePlayerController 对象分配了内存,在这个阶段我们还没有释放它,这主要是因为我们不知道电影播放何时实际上会完成。
幸运的是,MPMoviePlayerController 对象预先构建了处理此场景的方法,并在电影播放完成后向 NSNotificationCenter 发送一个名为 MPMoviePlayerPlaybackDidFinishNotification 的通知方法,如前一个代码片段中突出显示的代码所示。
当我们在 iPhone 应用程序中播放视频内容时,有时需要修改 MPMoviePlayerController 对象的 scalingMode 属性。通过设置此属性,它将确定电影图像如何适应填充您定义的播放大小。以下是目前存在的缩放模式,并在此处显示:
-
MPMovieScalingModeNone -
MPMovieScalingModeAspectFit -
MPMovieScalingModeAspectFill -
MPMovieScalingModeFill
两个主要的常用缩放模式是 MPMovieScalingModeAspectFill 和 MPMovieScalingModeFill。
注意
关于不同缩放模式之间的比较的更多信息,请参阅以下位置的MPMoviePlayerController 类参考 http://developer.apple.com/library/ios/#documentation/mediaplayer/reference/MPMoviePlayerController_Class/Reference/Reference.html#//apple_ref/doc/c_ref/MPMoviePlayerController。
为了在您的应用程序中实现此属性,请在[moviePlayerController play]语句之前插入以下行代码:
moviePlayerController.scalingMode = MPMovieScalingModeFill;
当您运行应用程序时,您会注意到视频占据了整个可用空间。接下来,我们需要创建moviePlaybackComplete:方法,它将负责释放我们的moviePlayerController对象,如下面的代码片段所示:
- (void)moviePlaybackComplete:(NSNotification *)notification{
MPMoviePlayerController *moviePlayerController = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayerController];
[moviePlayerController.view removeFromSuperview];
[moviePlayerController release];
}
在此代码片段中,我们将一个对象传递给了通知方法。这 whatever 我们在之前的代码片段中传递的内容,由于moviePlayerController对象。我们首先使用[notification object]语句检索对象,然后使用新的MPMoviePlayerController指针引用它。
然后,我们向NSNotificationCenter方法发送消息,移除我们在playMovie函数中之前注册的观察者。我们最后继续清理我们的自定义视图控制器,并释放之前分配给moviePlayerController对象的内存。
以下截图显示了我们的AirPlayExample应用程序在 iOS 模拟器中运行,电影播放设置为横幅模式进行查看;支持在竖幅模式下显示此内容:

在本节中,我们学习了MediaPlayer框架,以及我们如何在应用程序中使用它来获得播放音频和视频的能力。
如您所见,通过使用媒体播放器框架和MPMoviePlayerController类,您可以将电影播放功能集成到您的 iOS 应用程序中。在下一节中,我们将探讨修改应用程序的步骤,以便使用 Apple TV 在电视屏幕上显示。我们学习了视频播放的各种缩放模式以及如何实现这些模式。
使用 AirPlay 将应用程序内容呈现到 Apple TV
从 iOS 4.3 版本开始,苹果公司决定向其开发者提供迄今为止最令人印象深刻的框架之一,这将允许开发者将 AirPlay 功能集成到他们的应用程序中。只需几行代码,任何 iOS 应用程序都可以修改为具有直接将视频流式传输到 Apple TV 设备的能力。
要启用 AirPlay 功能,我们需要在MPMoviePlayerController对象上启用一个特殊属性,通过将allowsAirPlay属性设置为YES。
要在您的应用程序中启用 AirPlay 功能,请按照以下简单步骤操作:
-
打开位于
AirPlayExample文件夹内的ViewController.m实现文件,并在playMovie函数中找到以下语句:[self.view addSubview:moviePlayerController.view]; -
接下来,添加以下代码片段:
if([moviePlayerController respondsToSelector:@selector(setAllowsAirPlay:)]){ [moviePlayerController setAllowsAirPlay:YES]; } -
在此代码片段中,我们使用
MPMoviePlayerController对象的.respondsToSelector:方法来适应不支持allowsAirPlay属性的较旧 iOS 设备。如果我们不这样做,它将导致运行时错误异常发生,这将导致您的应用程序崩溃。为了仅向支持 AirPlay 的设备提供 AirPlay,我们需要在检查
MPMoviePlayerController对象是否支持allowsAirPlay选项的语句周围放置一个条件语句。注意
当此设置被激活时,它将在电影播放器控制器面板中引起一个额外图标的显示。您无法通过编程方式控制此图标的位置。
-
最后,构建并运行您的应用程序,然后点击播放电影按钮。以下截图显示了启用 AirPlay 时此图标的外观:
![使用 AirPlay 将应用程序内容呈现到 Apple TV]()
-
当按下 AirPlay 图标时,您将看到一个弹出列表,其中包含可选择的检测到的输出设备选项。
![使用 AirPlay 将应用程序内容呈现到 Apple TV]()
-
如果您选择如截图所示的Apple TV选项,您的 iOS 设备上的输出将消失,并且您将收到通知,视频正在 Apple TV 设备上播放。这如图下所示:
![使用 AirPlay 将应用程序内容呈现到 Apple TV]()
-
最后,您将在 Apple TV 设备上看到您的视频正在播放,如图下所示:

如您所见,通过遵循几个简单的步骤,您可以轻松地将现有应用程序的功能集成到 Airplay 感知应用程序中。
在以下列表中,您将找到在将 AirPlay 集成到您的项目中时需要考虑的一些事项:
-
苹果公司仅在其最新的设备上提供了 AirPlay 4.3 SDK 的此功能。因此,iPhone 3G 设备没有 AirPlay 支持。
-
当启动 AirPlay 启用应用程序时,您需要确保您的 iOS 设备和 Apple TV 软件运行的是同一版本的操作系统,否则您可能会遇到一些问题。
-
为了让 iOS 设备找到其他 Apple AirPlay 启用设备,您需要确保您连接的是与您的 AirPlay 设备相同的 Wi-Fi 网络。
注意
如需了解更多关于 AirPlay 框架的信息,您可以参考以下 Apple 开发者网站:developer.apple.com/library/ios/#releasenotes/General/WhatsNewIniPhoneOS/Articles/iOS4_3.html#//apple_ref/doc/uid/TP40010567-SW1.
理解 Core Image 框架
Core Image 框架是一个可扩展的图像处理技术架构,它已集成到 Mac OS X v10.4 和 iOS 5.0 中。此框架利用可编程图形硬件提供近似实时的、像素级准确的图像处理,以及视频处理。Core Image 随带超过 100 个内置过滤器,供希望在其应用程序中支持图像处理的过滤器客户端使用。
Core Image 过滤器参考描述了这些过滤器;内置过滤器的列表可能会更改,因此出于这个原因,Core Image 提供了让您查询系统以获取这些可用过滤器的功能。您还可以加载第三方开发者打包为图像单元的过滤器。Core Image 应用程序编程接口(API)是 Quartz Core 框架(QuartzCore.framework)的一部分,它提供了对内置图像过滤器(包括视频和静态图像)的访问,并支持创建自定义过滤器。
您可以通过链接到 Core Image 框架来使用 Cocoa 和 Carbon 框架中的 Core Image。通过使用 Core Image 框架,您可以使用 Core Image 中捆绑的过滤器或您或另一位开发者创建的过滤器执行以下类型的操作:
-
裁剪图像并校正颜色,例如执行白点调整
-
应用颜色效果,例如棕褐色
-
模糊或锐化图像
-
合成图像并扭曲或变换图像的几何形状
-
生成颜色、棋盘图案、高斯渐变和其他图案图像
-
向图像或视频添加过渡效果
-
在视频上提供实时颜色调整
以下截图向您展示了 Core Image 在 Mac OS X 中与其他图形技术的关系:

如您所见,Core Image 框架已与这些技术集成,允许您将它们一起使用以实现广泛的结果。您可以使用 Core Image 处理在 Quartz 2D(Core Graphics)中创建的图像以及在 OpenGL 中创建的纹理。您还可以将 Core Image 过滤器应用于使用 Core Video 播放的视频。
Core Image 随带超过 100 个内置过滤器,供希望在其应用程序中支持图像处理的过滤器客户端使用。Core Image 过滤器参考描述了这些过滤器;内置过滤器的列表可能会更改,因此 Core Image 提供了让您查询系统以获取这些可用过滤器的功能。您还可以加载第三方开发者打包为图像单元的过滤器。
备注
关于 Core Image API 中可用的内置过滤器更多信息,请参考 Mac OS X 开发者库。
以下代码片段显示了可用内置 Core Image 过滤器的列表。
NSArray *builtInFilterList = [CIFilter filterNamesInCategory:kCICategoryBuiltIn];
NSLog(@"%@", builtInFilterList);
以下表格显示了当执行此代码时可用核心图像过滤器列表:
| 核心图像过滤器名称 | 核心图像过滤器名称 |
|---|---|
CIAdditionCompositing |
CIAffineTransform |
CICheckerboardGenerator |
CIColorBlendMode |
CIColorBurnBlendMode |
CIColorControls |
CIColorCube |
CIColorDodgeBlendMode |
CIColorInvert |
CIColorMatrix |
CIColorMonochrome |
CIConstantColorGenerator |
CICrop |
CIDarkenBlendMode |
CIDifferenceBlendMode |
CIExclusionBlendMode |
CIExposureAdjust |
CIFalseColor |
CIGammaAdjust |
CIGaussianGradient |
CIHardLightBlendMode |
CIHighlightShadowAdjust |
CIHueAdjust |
CIHueBlendMode |
CILightenBlendMode |
CILinearGradient |
CILuminosityBlendMode |
CIMaximumCompositing |
CIMinimumCompositing |
CIMultiplyBlendMode |
CIMultiplyCompositing |
CIOverlayBlendMode |
CIRadialGradient |
CISaturationBlendMode |
CIScreenBlendMode |
CISepiaTone |
CISoftLightBlendMode |
CISourceAtopCompositing |
CISourceInCompositing |
CISourceOutCompositing |
CISourceOverCompositing |
CIStraightenFilter |
CIStripesGenerator |
CITemperatureAndTint |
CIToneCurve |
CIVibrance |
CIVignette |
CIWhitePointAdjust |
显示的列表包含适用于 Mac OS X 和 iOS 5 操作系统的过滤器,因此建议您参考 Core Image 过滤器参考文档以确定哪些过滤器适用于哪些技术。
备注
关于 Core Image 框架和 Core Image 过滤器参考文档的更多信息,您可以在 Apple 开发者网站上获取:developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/CoreImaging/ci_intro/ci_intro.html。
创建一个简单的 Core Image 应用程序
Apple 提供了超过 100 个 Core Image 图像处理过滤器,因此你很容易在你的应用程序中启用对图像处理的支持,使用这些内置功能。图像处理包括对照片应用效果以翻转或旋转图像,增强图像的清晰度,甚至从家庭照片中去除红眼。在我们能够做到这一点之前,我们需要将 Core Image 框架作为我们应用程序项目的一部分包含进来。
在我们继续之前,我们首先需要创建我们的CIFilterEffectsproject。为了刷新你的记忆,你可以参考我们在第一章中覆盖的部分,即iOS5 的新特性下的创建 MyEmailApp 应用程序部分。
-
从
/Xcode4/Applications文件夹中启动 Xcode。 -
选择创建一个新的 Xcode 项目,或者文件 | 新项目。
-
从可用模板列表中选择单视图应用程序模板。
-
从设备家族下拉菜单中选择iPhone。
-
点击下一步按钮,进入向导的下一步。
-
将项目名称输入为
CIFilterEffects,然后点击下一步按钮继续向导的下一步。 -
指定你想要保存项目的位置。
-
点击保存按钮继续,并显示 Xcode 工作区环境。
现在我们已经创建了CIFilterEffects项目,我们现在需要向项目中添加一个重要的框架,这将使我们能够应用多种不同的图像效果。要将 Core Image 框架添加到你的项目中,选择项目导航器组,然后按照以下简单步骤操作:
-
点击并从项目导航器中选择你的项目。
-
然后在TARGETS组下选择你的项目目标。
-
3 选择构建阶段标签。
-
展开链接二进制与库的展开三角形。
-
使用+添加你想要的库。
-
从可用框架列表中选择CoreImage.framework。如果你在列表中找不到你想要的框架,也可以进行搜索。
如果你仍然不清楚如何添加框架,请查看这个截图,它突出显示了你需要选择的部分(被红色矩形包围):

现在你已经将CoreImage.framework添加到你的项目中,我们需要开始构建我们的用户界面,它将负责允许选择图像并应用过滤器效果:
-
从项目导航器中,选择并打开ViewController.xib文件。
-
从对象库中,选择并拖动一个(
UIImageView)图像视图控件到我们的视图中。 -
根据需要调整这个控件的大小,使其占据屏幕的整个区域。
-
从对象库中,选择并拖动一个(
UIButton)圆形矩形按钮控件到我们的视图中。 -
根据需要调整大小,然后修改圆角矩形按钮的 对象 属性部分,并将其标题设置为
Choose Image。 -
接下来,从 对象库 中选择并拖动一个 (
UIButton) 圆角矩形按钮控件到 Choose Image 按钮右侧的视图中。 -
根据需要调整大小,然后修改圆角矩形按钮的 对象属性 部分,并将其标题设置为
Filter Effects。
如果你正确地遵循了步骤,你的视图应该看起来像以下截图所示。如果它看起来不太像我的一样,请随意调整你的。

如您所见,我们的表单在这个阶段并没有做什么,如果您在模拟器上运行此应用程序,您将看到控件放置在您的屏幕上。以下步骤将向您展示如何将按钮连接到动作事件,每个事件都将执行选择图片并应用滤镜效果的任务。那么,让我们开始吧:
-
打开
ViewController.h接口文件,并按照以下代码片段创建以下突出显示的条目:#import <UIKit/UIKit.h> @interface ViewController : UIViewController<UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIAlertViewDelegate, UIActionSheetDelegate> { UIImageView *imageView; UIButton *chooseImage; UIButton *filterEffects; } @property (nonatomic, retain) IBOutlet UIImageView *imageView; @property (nonatomic, retain) IBOutlet UIButton *chooseImage; @property (nonatomic, retain) IBOutlet UIButton *filterEffects; -(IBAction) getImage:(id) sender; -(IBAction) getFilterEffects:(id) sender; @end -
我们需要创建一个动作事件。选择 选择图片 按钮,并按住 Ctrl 键将其拖动到
ViewController.m实现文件类中,如图所示:![创建一个简单的 Core Image 应用程序]()
-
为要创建的动作指定一个名称。输入
getImage作为动作的名称。 -
将事件类型设置为 触摸内部释放:
![创建一个简单的 Core Image 应用程序]()
-
点击 连接 按钮让 Xcode 创建事件。
-
我们需要创建一个动作事件。选择 Filter Effects 按钮,并按住 Ctrl 键将其拖动到
ViewController.m实现文件类中,如图所示:![创建一个简单的 Core Image 应用程序]()
-
为要创建的动作指定一个名称。输入
getFilterEffects作为动作的名称。 -
将事件类型设置为 触摸内部释放:
![创建一个简单的 Core Image 应用程序]()
-
点击 连接 按钮让 Xcode 创建事件。
现在我们已经连接了我们的动作事件,我们现在需要合成我们的用户界面控件,以便我们可以在视图中访问它们。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在@implementation语句下方添加以下突出显示的语句。#import "ViewController.h" #import "QuartzCore/QuartzCore.h" @implementation ViewController @synthesize imageView, chooseImage, filterEffects; -
在此代码片段中,我们让实现文件了解位于我们用户界面表单上的控件。如果没有声明,我们将收到警告消息,这可能会使程序产生一些奇怪的结果,甚至可能在 iOS 设备上导致应用程序崩溃。
-
接下来,我们需要将代码添加到我们的
getImage函数中,这将使我们能够从照片库中选择一个图像,并将其显示到我们的UIViewImage控件中。为此函数输入以下代码片段:-(IBAction) getImage:(id) sender { UIImagePickerController * picker = [[UIImagePickerController alloc] init]; picker.delegate = self; picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; [self presentModalViewController:picker animated:YES]; } - (void)imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(NSDictionary *)info { [picker dismissModalViewControllerAnimated:YES]; imageView.image = [info objectForKey:@"UIImagePickerControllerOriginalImage"]; [picker release]; } -
此代码片段创建了一个
UIImagePickerController实例,这将使我们能够从 iOS 设备的相册中选择照片图像。然后,我们修改并初始化选择器控件的sourceType属性,并告诉它使用UIImagePickerControllerSourceTypeSavedPhotosAlbum常量。最后的语句显示相册,并允许你选择一个图像。 -
然后,我们声明另一个方法,
imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info,该方法在图像选择后会被调用。选择器随后关闭,并将图像显示到我们放置在用户界面上的UIImageView控件中。UIImagePickerController类采用了UIImagePickerControllerDelegate和UINavigationControllerDelegate协议。 -
接下来,我们需要将代码添加到我们的
getFilterEffects函数中,这将使我们能够从一系列选项中选择一个滤镜效果,并将其应用到imageView控件中的加载图像上。 -
在此函数中输入以下代码片段:
// Displays our Action Sheet - (IBAction)getFilterEffects:(id)sender { // Define an instance of our Action Sheet UIActionSheet *actionSheet; // Initialise our Action Sheet with options actionSheet=[[UIActionSheet alloc]initWithTitle:@"Available Actions" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Close" otherButtonTitles: @"Hue Adjust",@"Vibrance",@"Color Invert",@"Straighten Filter",@"Ripple Effect", nil]; [actionSheet showInView:self.view]; [actionSheet release]; } -
此代码片段声明、创建并初始化了一个
actionSheet变量,该变量设置了一个可以选择并应用于图像的滤镜选项列表。值得一提的是,UIActionSheet类采用了UIActionSheetDelegate协议。以下截图显示了这些选项在显示时的样子:![创建一个简单的 Core Image 应用程序]()
-
接下来,我们需要创建
actionSheet函数,该函数将根据列表中选择的按钮索引处理并应用所需的滤镜类型到图像上。 -
为此函数输入以下代码片段:
// Delegate which handles the processing of the option buttons //selected - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{} -
此代码片段将用于确定从动作表选项面板中选择了哪个按钮。这是通过传递到该函数的
buttonIndex属性推导出来的。在下一节中,我们将探讨如何根据列表中的选择应用这些图像效果。
学习如何使用 CIImage 类应用图像滤镜效果
当你想对图像应用效果时,会使用Core Image类。这可能是当你想要像素化一个图像,或者处理图像中的红眼时。你可以使用CIImage对象与其它Core Image类一起使用,例如CIFilter、CIContent、CIVector和CIColor类。为了在处理图像时利用内置的Core Image滤镜,你可以使用CVImageBufferRef从多种数据源创建CIImage对象,包括 Quartz 2D 图像和 Core Video 图像缓冲区。
CIImage 对象与其相关联的是图像数据,但它本身并不是一个图像。一个 CIImage 对象包含了生成图像所需的所有信息,但 Core Image 不会实际渲染图像,直到被指示这样做。此方法允许 Core Image 以尽可能高效的方式运行。当使用 CIImage 类时,它包含多个参数,这些参数在以下表格中进行了说明:
| CIImage 类参数 | 描述 |
|---|---|
| 过滤器类别 | 这指定了效果类型(模糊、扭曲、生成等)或其预期用途(静态图像、视频、非正方形像素等)。一个过滤器可以属于多个类别之一。 |
| 显示名称 | 这是应在用户界面中显示的名称 |
| 过滤器名称 | 这是用于程序访问过滤器的名称。 |
| 输入参数 | 这些可以包含一个或多个输入参数,让你可以控制处理方式。 |
| 属性类 | 你创建的每个输入参数都包含一个属性类,用于指定其数据类型,例如 NSNumber。输入参数可以可选地具有其他属性,例如其默认值、允许的最小和最大值、参数的显示名称以及 CIFilter 中描述的任何其他属性。 |
例如,如果你选择单色颜色过滤器,它包含三个输入参数:要处理的图像、单色颜色和颜色强度。你提供图像,并可以选择设置颜色和颜色强度。
大多数过滤器,包括单色颜色过滤器,为每个非图像输入参数都有默认值。如果你选择不提供自己的输入参数值,Core Image 将使用默认值来处理你的图像。过滤器属性存储为键值对。
键是一个标识属性的常量,值是与键关联的设置。Core Image 属性值通常是以下数据类型之一:
-
字符串:这些用于诸如显示名称之类的用途。
-
浮点数:它们用于指定标量值,例如强度级别和半径。
-
向量:它们可以有两个、三个或四个元素,每个元素都是一个浮点数。这些用于指定位置、区域和颜色值。
-
颜色:它们指定颜色值和颜色空间,以解释这些值。
-
图像:它们是轻量级对象,用于指定图像。
-
变换:它们指定应用于
image.CIContext的仿射变换。
在下一节中,我们将探讨如何在将各种类型的颜色效果应用到我们的 CIFilterEffects 应用程序时使用一些这些技术,当从我们的操作表选项列表中选择了一个过滤器类型时。
打开位于 CIFilterEffects 文件夹中的 ViewController.m 实现文件,找到 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex,并在函数声明之后添加以下代码语句。
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *cImage = [CIImage imageWithCGImage:[imageView.image CGImage]];
CIImage *result;
在这个代码片段中,我们声明了一个名为 context 的 CIContext 变量。这个变量将用于将图像对象 cImage 渲染到视图中。然后,我们声明了一个 cImage 变量对象,其类型为 CIImage,包含指向 imageView 中图像的指针。最后,我们声明了一个 CIImage result 变量,用于应用图像过滤器更改,并将修改后的图像输出到 imageView 控件。
颜色效果
在本节中,我们将查看将操作表弹出窗口中显示的每个选项应用到我们选择的苹果标志图像上,该图像来自我们的 iOS 照片库。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件。 -
接下来,定位到
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex,并在之前代码片段中应用的变量声明之后添加以下代码语句:// Handle when the Hue Adjust Filter option has been chosen. if (buttonIndex == 1){ CIFilter *hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"]; [hueAdjust setDefaults]; [hueAdjust setValue: cImage forKey: @"inputImage"]; [hueAdjust setValue: [NSNumber numberWithFloat: 2.094] forKey: @"inputAngle"]; result = [hueAdjust valueForKey: @"outputImage"]; } -
在这个代码片段中,我们首先声明了一个名为
hueAdjust的CIFilter变量。这个变量将用来表示我们想要应用到图像上的过滤器类型。 -
在下一步中,我们将变量
cImage的类型CIImage指向我们UIImageView控件中选择的图像,并将其分配给inputImage。
接下来,我们通过设置 inputAngle 属性的值来指定应用到图像上的色调级别。完成这些操作后,我们将对图像应用 hue Adjustment,并将结果返回到我们的 UIImage 结果中,基于 outputImage 属性,然后将这个结果输出到我们的 UIImageView 控件。
注意
当设置 inputAngle 属性的值时,这些值从最小值 -3.14 到最大值 3.14 有一个起始范围。还有一个默认值 0.00。
以下截图显示了应用了色调饱和度的图像输出。你会注意到它改变了源像素的整体色调或色调:

接下来,我们将查看 Vibrance 选项,并看看当我们在操作表中的选项列表中选择这个 Core Image 过滤器时会发生什么。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在之前代码片段中应用的下一段代码块下方添加以下代码语句:// Handle when the Vibrance Filter option has been chosen. else if (buttonIndex == 2){ CIFilter *vibrance = [CIFilter filterWithName:@"CIVibrance"]; [vibrance setDefaults]; [vibrance setValue: cImage forKey: @"inputImage"]; [vibrance setValue: [NSNumber numberWithFloat: 1.00] forKey: @"inputAmount"]; result = [vibrance valueForKey: @"outputImage"]; } -
在这个代码片段中,我们首先声明了一个名为
vibrance的CIFilter变量。这个变量将用来表示我们想要应用到图像上的过滤器类型。 -
在下一步中,我们将变量
cImage的类型设置为CIImage,它指向我们UIImageView控件中选择的照片,并将其分配给inputImage。最后,我们通过设置inputAmount属性的值来分配应用到图像的饱和度级别。
注意
在设置 inputAmount 属性的值时,这些值从最小值 -1.00 到最大值 1.00 有一个起始范围。还有一个默认值 0.00。
- 以下截图显示了应用了 Vibrance 饱和度的图像输出。您会注意到它减少了图像颜色,同时保持了良好的肤色平衡:

接下来,我们将查看 Color Invert 选项,并观察当我们在 actionsheet 的选项列表中选择此 Core Image 过滤器时会发生什么。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在之前代码片段中应用的下一段代码块下方添加以下代码语句。// Handle when the Color Invert option has been chosen. else if (buttonIndex == 3){ CIFilter *invert = [CIFilter filterWithName:@"CIColorInvert"]; [invert setDefaults]; [invert setValue: cImage forKey:@"inputImage"]; result = [invert valueForKey:@"outputImage"]; } -
在此代码片段中,我们首先声明一个名为
invert的CIFilter变量。这将用于表示我们想要应用到图像上的过滤器的类型。 -
在下一步中,我们将变量
cImage的类型设置为CIImage,它指向我们UIImageView控件中选择的照片,并将其分配给inputImage。以下截图显示了应用了 Color Invert 过滤器的图像输出。您会注意到图像颜色已被反转,以显示更多负图像:

接下来,我们将查看 Straighten Filter 选项,并观察当我们在 actionsheet 的选项列表中选择此 Core Image 过滤器时会发生什么。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在之前代码片段中应用的下一段代码块下方添加以下代码语句。// Handle when the Straighten Filter option has been chosen. else if (buttonIndex == 4){ CIFilter *straightenFilter = [CIFilter filterWithName:@"CIStraightenFilter"]; [straightenFilter setDefaults]; [straightenFilter setValue: cImage forKey:@"inputImage"]; [straightenFilter setValue: [NSNumber numberWithFloat: 3.10] forKey: @"inputAngle"]; result = [straightenFilter valueForKey:@"outputImage"]; } -
在此代码片段中,我们首先声明一个名为 straightenFilter 的 CIFilter 变量。这将用于表示我们想要应用到图像上的过滤器的类型。
-
在下一步中,我们将变量
cImage的类型设置为CIImage,它指向我们UIImageView控件中选择的照片,并将其分配给inputImage。最后,我们通过设置inputAngle属性的值来分配应用到图像的角度级别旋转。
注意
在设置 inputAngle 属性的值时,这些值从最小值 -3.14 到最大值 3.14 有一个起始范围。还有一个默认值 0.00。
- 以下截图显示了应用了 Straighten 过滤器的图像输出。您会注意到它将源图像按指定的弧度旋转。然后图像被缩放并裁剪,以便旋转后的图像适合视图:

接下来,我们需要添加代码,用于在应用基于我们的核心图像滤镜后输出更新后的图像。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在之前代码片段中应用的上一个代码块下方添加以下代码语句:// Only process when button index is based on the list of //options.Ignore the Close and Cancel buttons and then display // our update image to our UIImageView Control. if (buttonIndex != 0 && buttonIndex != 5 && buttonIndex != 6) { self.imageView.image = [UIImage imageWithCGImage:[context createCGImage:result fromRect:CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height)]]; } -
在此代码片段中,我们首先检查确保我们不是在处理我们的关闭和取消按钮,因为这些按钮不会将核心图像过滤器应用于图像。这是一种保护我们应用程序的通用方法,以防止它崩溃。
接下来,我们使用
imageWithCGImage方法创建并返回一个表示指定 Quartz 图像的图像对象,然后将此图像显示回我们的UIImageView imageView控件,并设置其显示为图像视图的宽度和高度。
在下一节中,我们将探讨如何利用 Quartz Core 框架将转换效果应用于图像。
转换
转换通常用于将某种效果应用于图像。这些效果随时间渲染,需要你设置一个计时器事件。在本节中,我们将向我们的CIFilterEffects示例应用程序添加一些代码,以展示我们可以将水波纹效果应用于图像的最简单方法之一。
幸运的是,你不必担心,因为 QuartzCore 框架中已经包含了一个涟漪效果组件,这将在渲染此效果时利用图形硬件加速。
为了在我们应用程序中使用转换,我们需要向项目中添加一个重要的框架,这将使我们能够应用多种不同的图像效果。
要将 QuartzCore 框架添加到你的项目中,请选择项目导航器组,然后按照以下简单步骤操作:
-
点击并从项目导航器中选择你的项目。
-
然后从TARGETS组下选择你的项目目标。
-
选择构建阶段选项卡。
-
展开显示链接二进制与库的展开三角形。
-
最后,使用+来添加你想要的库。
-
从可用的框架列表中选择QuartzCore.framework。如果你找不到你想要的框架,也可以在列表中进行搜索。
如果你仍然不清楚如何添加框架,可以查看这张截图,它突出显示了你需要选择的区域(被矩形包围):

现在我们已经将QuartzCore.framework添加到你的项目中,我们可以开始向示例项目添加必要的代码,以应用水波纹效果。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在直通滤波器代码块下方添加以下代码语句:else if (buttonIndex == 5){ CATransition *animation = [CATransition animation]; [animation setDelegate:self]; [animation setDuration:3.0f]; [animation setTimingFunction:UIViewAnimationCurveEaseInOut]; [animation setType:@"rippleEffect" ]; [self.view.layer addAnimation:animation forKey:NULL]; } -
在这个代码片段中,我们首先声明了一个名为
animation的变量,该变量将负责处理我们UIView层的过渡动画。在下一步中,我们指定了涟漪效果的持续时间,这将用来定义动画的单次迭代显示需要多长时间,以秒为单位。接下来,我们设置了一个计时函数。这将用于指定
UIViewAnimationCurveEaseInOut作为我们想要使用的动画类型。这会导致动画开始时速度较慢,然后在中期加速,最后在迭代的末尾开始减速。这是大多数动画的默认曲线。在下一步中,我们指定我们想要使用的动画类型是
rippleEffect过渡效果。最后,我们将动画效果应用到我们的视图中。以下截图显示了应用了水波纹效果的输出。您会注意到它从内部向外弯曲,更像是真空效果:

如您所见,通过使用 Core Image 和 QuartzCore 框架,您可以在应用程序中创建一些令人惊叹的视觉效果,并使它们栩栩如生。
注意
关于 Core Image 和 QuartzCore 框架的更多信息,请参阅以下链接:developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/CoreImaging/ci_intro/ci_intro.html。
关于 Core Image 过滤器的更多信息,请参阅以下链接:developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CoreImageFilterReference/Reference/reference.html。
摘要
在本章中,我们学习了 AirPlay 和 Core Image 框架,并探讨了如何将这些框架集成到我们的应用程序中,以便将输出输出到外部设备,例如 Apple TV。
然后,我们学习了 Core Image 过滤器类,以及我们如何通过不同的内置过滤器(如颜色效果)应用不同的图像过滤效果来增强图像。然后,我们熟悉了 QuartzCore 框架,并探讨了如何使用该框架,使用内置的扭曲和过渡效果过滤器,将水波纹效果应用到图像上。
在下一章中,我们将学习对 Xcode 开发工具所做的改进,并查看 自动引用计数 (ARC)。这是 LLVM 编译器的最新功能。我们还将查看对 Interface Builder、iOS 位置模拟器和 OpenGL ES 调试工具集的改进。
第六章。Xcode 工具 - 改进
自从 2010 年 iPhone 4 发布以来,开发者们对那块令人印象深刻的960x640 分辨率视网膜屏幕显示印象深刻,并提供了使用 FaceTime 视频通话功能与朋友和家人保持联系的方法。
iPhone 4 的摄像头已更新,具有前置和后置摄像头,以及内置 LED 闪光灯的标准 500 万像素摄像头,以及支持录制和编辑令人惊叹的 HD 视频的高清视频编辑功能。随着 iPhone 4S 的发布,这一功能已更新,包括录制 1080 像素 HD 视频的能力,并增加了直接在 iOS 设备内编辑视频的功能。
从 Xcode 4 开始,陀螺仪功能被整合到加速度计中,这为开发者提供了编程此功能并创建一些令人惊叹的游戏的灵活性。随着 iOS 5 SDK 的发布,LLVM 编译器已更新,包括新的自动引用计数(ARC)功能。
随着 Xcode 4.2 和 iOS 5 SDK 的发布,Interface Builder 已更新,通过引入 iOS 应用程序的故事板,为你的视图和视图控制器之间提供更好的过渡方式,这些故事板直接集成在 Xcode IDE 中。
你还会注意到 iOS 模拟器也得到了改进,现在可以使用 Core Location 框架在 Xcode 开发环境中直接模拟不同的位置。
在本章中,我们将:
-
了解 LLVM 编译器的最新改进
-
了解如何使用 Interface Builder 创建故事板文件
-
了解 iOS 模拟器所做的更改
-
了解 OpenGL ES 的改进
-
了解应用程序数据管理和 UI 自动化增强
让我们开始吧。
LLVM 编译器
这项技术是一种开源编译器技术,目前由苹果的编译器团队领导,用于全球多个高端性能项目。LLVM 2.0 编译器也得到了大幅更新,现在编译速度是 GCC 编译器的两倍,产生的应用程序在 iOS 设备上加载速度更快。
它已被重写为一组优化后的代码库,这些代码库是围绕当今的现代芯片架构设计的。它已完全集成到 Xcode 4 开发 IDE 中,并提供了对以下语言的完整支持:C、Objective-C 和 C++。
在下一节中,我们将讨论作为 LLVM 编译器一部分添加的自动引用计数功能。
自动引用计数(ARC)
自动引用计数(ARC)使内存管理成为编译器的职责。当你使用新的 Apple LLVM 3.0 编译器启用 ARC 时,这将很大程度上减轻手动释放内存的负担,并避免因内存泄漏或过早释放的对象而导致的程序错误的无限循环。
ARC 编译器完全理解你的对象,并在对象不再使用时立即释放它们,因此应用程序运行速度与以前一样快,具有可预测的、平稳的性能。在大多数情况下,你将不再需要键入 retain 或 release,这将极大地简化开发过程,同时减少崩溃和内存泄漏。
Xcode 附带了一个新的转换为 Objective-C ARC工具,该工具位于 IDE 中的编辑 | 重构菜单内,如下截图所示:

此工具通过删除retain和release等方法调用来自动化 ARC 转换的机械部分,并帮助你修复迁移器无法自动处理的错误。ARC 迁移工具将项目中的所有文件转换为使用 ARC;你还可以选择按文件使用 ARC,以克服 ARC 的一些当前限制,并对某些文件使用手动引用计数。
以下截图表明,编写操作代码所需的时间几乎与 retain/release 逻辑一样长。对于经验丰富的 Objective-C 开发者来说,这并不准确,但如果你是新手,刚开始接触 Objective-C,这可能是保守的估计。
注意
关于 Objective-C 的更多信息,请参考以下位置的Apple 开发者文档:developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html#//apple_ref/doc/uid/TP30001163.
你仍然需要为你的类如何管理对其他对象的引用承担一些责任,而不是完全依赖 ARC。

ARC 为你的对象提供自动内存管理,无需你记住何时使用 retain、release 和 auto-release。ARC 首先评估你的对象,并在编译时自动为你插入适当的方法调用,同时为你生成适当的dealloc方法调用。
例如,让我们看看在 ARC 使用之前的老方法的一个示例,如下代码片段所示:
NSObject *obj = [[NSObject alloc] init];
…
…
// do some program logic here.
[obj release];
在分配和初始化对象之间,以及最终释放对象之前,你可以随意使用它,对象只有在不再使用时才会被释放和解除分配。
同样,通过将对象添加到自动释放池中,它将一直存在,直到需要它,并在不再需要时被释放。以下代码片段显示了在 ARC 之前如何进行这一操作。
- (NSObject *) someMethod {
NSObject *obj = [[[NSObject alloc] init] autorelease];
return obj; // This will be released by the autorelease pool.
}
如果你刚开始学习 iOS 编程,你可能会在最初难以理解引用计数内存的使用,但一旦你掌握了它,你很快就会看到它的潜力。这在为 iOS 设备开发应用程序时尤其有用,因为它可以消除由泄漏或过度释放对象引起的错误追踪负担。
许多开发者忘记释放先前声明的对象的内存分配,这会导致性能问题,或者更严重的是,导致他们的应用程序挂起或崩溃。
在 ARC 下,这会有不同的处理方式,并且会进行预编译步骤,这会自动将保留、释放和自动释放语句添加到代码中。这绝对不是垃圾回收的一种形式,引用计数的内存并没有消失,它只是被自动化了。
查看以下启用 ARC 的代码片段:
NSObject *obj = [NSObject alloc] init];
…
…
// do some program logic here.
ARC 预编译步骤会自动将其转换为:
NSObject *obj = [NSObject alloc] init];
// do some program logic here.
[obj release]; // Added by ARC
为了编译器生成正确的代码,ARC 对你可以使用的方法施加了一些严格的限制,并引入了新的生命周期限定符用于对象引用和声明属性。
这些新规则在其他编译器模式下并不存在,旨在提供一个完全可靠的内存管理模型。在某些情况下,它们被设置为强制最佳实践。在其他情况下,它们简化了你的代码,这样你就不必处理内存管理问题。
违反这些规则将导致立即的编译时错误,而不仅仅是程序运行时可能出现的某些程序错误。以下表格解释了你需要遵守的规则,以便使用 ARC 进行编译:
| ARC 规则 | 描述 |
|---|---|
| 分配/初始化对象 | 在创建对象时,你不能调用保留、释放、自动释放和保留 Count 方法,或者间接调用它们的选择器,即 @selector(retain) 和 @selector(release)。 |
| 释放方法 | 通常这些方法会为你创建,但你不能直接调用 dealloc。然而,如果你需要释放除了实例变量之外的其他资源,你仍然可以创建一个自定义的 dealloc 方法。在创建自定义 dealloc 方法时,不要调用 [super dealloc] 方法,因为这将为你完成,并且由编译器强制执行。 |
| 声明属性 | 在 ARC 之前,我们通过使用 @property 指令的 assign, retain 和 copy 参数来告诉编译器如何使用这些参数来管理声明的 public 属性的内存。在 ARC 中,这些参数不再使用。相反,我们有两个新的参数,weak 和 strong,它们告诉编译器我们希望如何处理我们的属性。 |
| C 结构中的对象指针 | Apple 文档建议将它们存储在类中而不是结构体中。这很有道理,因为否则它们将不为 ARC 所知。这可能会引起一些额外的迁移问题。 |
id和void*之间的随意转换 |
当在 Core Foundation 的 C 库函数和 Foundation Kit 的 Objective-C 库方法之间传递对象时,经常会在id和void*数据类型之间进行转换。这被称为Toll Free Bridging。在 ARC 中,你必须提供提示/限定符来告诉编译器 CF 对象何时进入和退出其控制范围以进行内存管理。这些限定符包括__bridge, __bridge_retain, 和 __bridge_transfer。你仍然需要调用CFRetain和CFRelease来管理 Core Foundation 对象的内存。 |
@autoreleasepool代替NSAutoReleasePool |
如果你在应用程序中使用 ARC 兼容的代码,它不能使用NSAutoReleasePool对象,而必须使用@autoreleasepool{}代码块。一个很好的例子可以在任何 ARC 项目的main.m文件中找到.int main(int argc, char *argv[]){@autoreleasepool {return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));}} |
| 内存区域 | 你不能使用基于NSZone的内存区域(这不再是运行时的一部分);你不能使用NSAllocateObject或NSDeallocateObject。 |
作为程序员,我们发现自己需要做出决定,比如是否将某个东西定义为变量或常量,或者是否需要在局部或全局范围内定义。当我们决定我们的属性如何与其他对象相关联时,这个概念同样适用。为了做到这一点,我们使用强和/或弱限定符来通知编译器这些关系。
强引用
这些提供了对对象的引用,阻止它被释放。换句话说,它创建了一个所有者关系。在 ARC 之前,你会这样声明你的属性:
// Non-ARC Compliant Declaration
@property(retain) NSObject *obj;
如果我们看看在 ARC 下如何声明相同的属性,这将按照以下方式完成,以确保类实例拥有被引用的对象:
// ARC Compliant Declaration
@property(strong) NSObject *obj;
考虑以下代码片段:
MyClass *obj1 = [[MyClass alloc] init];
MyClass *obj2 = obj1;
如您所见,我们声明了两个对象,并将内存分配给了我们的obj1对象变量。然后我们声明一个新的对象变量obj2,它对obj1有一个强引用。如果我们从内存中移除obj2,那么obj1也会被移除。
弱引用
这些提供了对对象的引用,但不会阻止它被释放。换句话说,它不会创建所有者关系。在 ARC 之前,你会这样做:
// Non-ARC Compliant Declaration
@property(assign) NSObject *parentObj;
如果我们看看在 ARC 下如何声明相同的属性。这将按照以下方式完成,以确保你不拥有被引用的对象。
// ARC Compliant Declaration
@property(weak) NSObject *parentObj;
考虑以下代码片段:
__weak NSString *weakName = self.textField.text;
我们首先声明一个名为weakName的变量,它指向与textField.text属性指向的相同字符串对象——这包含名字Albert Einstein。如果字符串内容发生变化,那么字符串对象就不再有任何所有者,并且会被释放。这在下面的代码片段中显示:
__weak NSString *weakName = @"Captain Jack Sparrow";
当这种情况发生时,weakName 的值会自动变为 nil,这被称为零引用弱指针。这非常方便,因为它防止了弱指针指向已释放的内存。以前,这类事情经常导致许多编程错误;例如,悬垂指针或僵尸。
弱指针主要用于两个对象具有父子关系时。父对象将拥有对子对象的强指针并因此拥有子对象,但为了防止所有权循环,子对象只对父对象有弱指针。考虑以下代码片段:
__weak NSString *str = [[NSString alloc] initWithFormat:@"Weakname: %@", weakName];
NSLog(@"%@", str); // This will output "(null)"
由于字符串对象没有所有者(因为 str 是弱引用),对象将在创建后立即被释放。当你尝试这样做时,Xcode 会给出警告,因为这可能不是你想要做的(警告:将保留对象赋值给弱变量;对象将在赋值后释放])。
ARC 限定符常规变量
ARC 为对象引入了几个新的生命周期限定符,以及零引用弱引用。弱引用不会延长它所指向的对象的生命周期。零引用弱引用,也称为弱限定符,指示编译器你不需要保留该对象。如果指向此对象的引用都降为零,则对象将被释放并设置为 nil。
这很重要,因为向 nil 对象发送的消息不会导致崩溃;它只是什么也不做。然而,你仍然可以使用 assign,但建议你使用 weak,因为它会将已释放的对象设置为 nil。弱限定符特别用于父子对象关系,其中父对象对子对象有强引用,而子对象对父对象有弱引用,否则你将最终创建一个循环引用。
变量限定符
在之前的代码片段中,我们说明了如何管理我们声明的属性。对于常规变量,我们有:
-
__strong -
__weak -
__unsafe_unretained -
__autoreleasing
一般而言,这些额外的限定符不需要经常使用。你可能会首先在使用迁移工具时遇到这些限定符和其他限定符。然而,对于新项目,你通常不需要它们,并且主要使用 strong/weak 与你声明的属性一起使用。
| ARC 类型 | 描述 |
|---|---|
__strong |
这是默认值,因此你不需要输入它。这意味着使用 alloc/init 创建的任何对象都会保留在其当前作用域的生命周期内。当前作用域通常是指变量声明的花括号内(即方法、for 循环、if 块等)。 |
__weak |
这意味着对象可以在任何时候被销毁。这仅在对象在其他地方有强烈的引用时才有用。当对象被销毁时,带有 __weak 的变量会被设置为 nil。 |
__unsafe_unretained |
这与 __weak 类型相似,但对象被释放时指针不会被设置为 nil。相反,指针被留在指向内存中不安全区域的指针上。 |
__autoreleasing |
这不是在从方法返回对象之前调用 autorelease,而是用于通过引用传递对象,例如,在通过引用传递 NSError 对象时,如 [myObject performOperationWithError:&tmp]; |
注意
关于 LLVM Clang Objective-C 自动引用计数 文档的更多信息,您可以参考以下提供的链接:clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership。
Interface builder
在 Xcode 4 中,Interface Builder 是一个用户界面设计工具,您可以通过从对象库中将对象拖放到空白画布上来构建用户界面。生成的用户界面将保存为 XIB 文件,它是您对象及其实例变量的 XML 表示。
在过去,当创建一个新视图时,您必须为应用程序所需的每个视图创建一个 XIB 文件,以便从每个视图转换到下一个视图。为了使设计 iOS 应用程序变得更加容易,苹果改进了用户界面设计过程,并引入了 Storyboarding 功能。
支持为 iOS 应用程序创建 storyboard 文件
随着 Xcode 4.2 的发布,Interface Builder 已更新,提供了一种更好的方式来设计用户界面,通过在单个画布中图形化地排列所有视图,以便您可以定义应用程序,逻辑流程以及它们之间的转换。
在您的应用程序中使用 storyboards 可以通过为您管理视图控制器来简化开发过程。您可以在不手动编码的情况下指定在视图之间切换时使用的转换和 segues。
为了刷新您的记忆,您可以参考在 如何创建 Storyboard 文件 部分下的 第四章,使用 Storyboards,以获取有关如何使用 Interface Builder 创建 storyboard 文件的信息。
位置模拟器
从 Xcode 4.2 和 iOS 5 的发布开始,您现在可以在不离开办公桌的情况下测试应用程序中的基于位置的功能。您现在可以在 iOS 模拟器中选择预设的位置和路线,并在运行模拟应用程序的同时选择具有精度的自定义纬度和经度。
创建一个简单的地理应用程序
在我们继续之前,我们首先需要创建我们的 MapKitExample 项目。为了刷新您的记忆,您可以参考 第一章 中名为 创建 MyEmailApp 应用程序 的部分,iOS5 中的新功能。
-
从
/Xcode4/Applications文件夹启动 Xcode。 -
选择创建一个新的 Xcode 项目,或文件 | 新建项目。
-
从可用模板列表中选择Single View Application模板。
-
从设备家族下拉菜单中选择iPhone。
-
确保你已从iPhone 设备家族下拉菜单中勾选了使用自动引用计数选项。
![创建一个简单的地理应用程序]()
-
点击下一步按钮继续向导的下一步。
-
输入
MapKitExample,然后点击下一步按钮继续向导的下一步。 -
指定你想要保存项目的位置。
-
点击保存按钮继续,并显示 Xcode 工作区环境。
现在我们已经创建了MapKitExample项目,我们需要将MapKit框架添加到我们的项目中,以便我们的应用程序可以查看地图信息。选择项目导航器组,然后按照以下简单步骤操作:
-
选择你的项目。
-
然后从
TARGETS组下选择你的项目目标。 -
选择构建阶段选项卡。
-
展开链接库与库的展开三角形。
-
使用+添加你想要的库。如果你在列表中找不到你想要的框架,也可以进行搜索。
如果你仍然不清楚如何添加框架,请参考以下截图,它突出显示了你需要选择的部分(被红色矩形包围):

现在你已经将MapKit.framework添加到你的项目中,我们需要将代码导入到负责显示我们的地图位置信息的ViewController中。
为了使我们的应用程序在我们的视图中显示地图,我们需要导入<MapKit/MapKit.h>接口头文件,以便我们可以利用其方法:
-
打开位于Classes文件夹中的
ViewController.h接口文件,并添加以下代码:#import <UIKit/UIKit.h> #import <MapKit/MapKit.h> @interface ViewController : UIViewController { MKMapView *mapView; } -
在这个代码片段中,我们包含了对 Cocoa 的
MapKit.h头文件的引用,这将暴露其方法,以便我们可以在ViewController实现文件中使用这些方法,然后我们创建了一个实例变量(mapView),它是一个指向我们的MKMapView对象的字符串指针,该对象负责存储我们的地图位置信息。 -
我们还没有完全完成。现在我们需要修改位于
ViewController.m实现文件中的ViewDidLoad方法。因此,打开ViewController.m实现文件。 -
定位并取消注释
ViewDidLoad方法,并向其中添加以下代码片段:- (void)viewDidLoad { [super viewDidLoad]; mapView = [[MKMapView alloc] initWithFrame:[self.view bounds]]; [self.view addSubview:mapView]; } -
在这个代码片段中,我们实际上为在
ViewController.h文件中声明的mapView对象分配和初始化了内存,然后我们将mapView对象添加到当前视图中,以便我们可以在屏幕上显示它。 -
mapKit框架具有在地图上显示您当前位置的能力。它还允许您设置各种mapTypes。接下来,我们将在ViewDidLoad方法中添加一些额外的代码,如下面的代码片段所示。此代码位于我们的ViewController.m实现文件中。- (void)viewDidLoad { [super viewDidLoad]; mapView = [[MKMapView alloc] initWithFrame:[self.view bounds]]; mapView.mapType=MKMapTypeHybrid; mapView.showsUserLocation=YES; [self.view addSubview:mapView]; } -
在此代码片段中,我们所做的是增加了显示我们的地图在 混合 视图(卫星视图和道路信息的组合)中的能力,同时将我们的地图指向显示我们的当前位置,该位置将由一个动画蓝色标记指示。
iOS 原生地图应用程序允许您从以下三种可能的地图类型中选择:
| 地图类型常量 | 描述 |
|---|---|
MKMapTypeStandard |
这是默认的地图类型,如果没有指定,此类型将显示包含街道和道路名称的正常地图。 |
MKMapTypeSatellite |
设置此类型的地图将显示卫星视图信息。 |
MKMapTypeHybrid |
此类型的地图将显示卫星视图与道路和街道信息叠加的组合。 |
如果您构建并运行了您的应用程序,现在应该会看到一个带有闪烁的动画蓝色标记的地图显示。我已经旋转了设备并在一个随机位置进行了缩放,以展示 MapKit 框架的功能,如下面的截图所示:

小贴士
当使用 iOS 模拟器运行 MapKit 应用程序时,它将始终默认显示位于加利福尼亚州 1 Infinite Loop 的苹果公司总部。
为了获得更好的位置,使用您的 iOS 设备会更好。这是因为 iOS 模拟器使用您的 IP 地址来估算您的位置。
您也可以选择在 iOS 模拟器运行时导航到不同的位置。为此,请按照以下简单步骤操作:
-
点击如下所示的 模拟位置 图标。这将显示可用位置列表:
![创建一个简单的地理应用程序]()
-
从显示的位置列表中选择 东京,日本 或类似选项。
-
iOS 模拟器将更新以反映所选位置,如下面的截图所示:

在本节中,我们了解了 MapKit 框架,以及我们如何在应用程序中使用它来模拟特定位置。我们学习了如何使用 Xcode 调试器的 模拟位置 功能,在 iOS 模拟器中导航到各种位置。
注意
有关MKMapView类参考的更多信息,请参阅以下链接位置提供的Apple 开发者文档:developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html#//apple_ref/doc/uid/TP40008205。
OpenGL ES 调试
OpenGL ES 调试器允许你追踪应用程序中特定于 OpenGL ES 的问题。然后你可以选择在程序中的某个特定点中断。为了刷新你的记忆,你可以参考我们在第三章,使用 OpenGL ES 进行调试中提到的名为通过设置断点检测错误的部分,以获取有关如何调试 OpenGL ES 项目的更多信息。
OpenGL ES 帧捕获
OpenGL ES 帧捕获是 Xcode 调试器的一部分,允许你在捕获点捕捉到应用程序中正在绘制的所有帧的快照。然后你可以选择浏览每个帧,查看相关的代码,以及在不同之间的实体或线框视图之间切换。为了刷新你的记忆,你可以参考名为在帧边界处中断的部分,在第三章,使用 OpenGL ES 进行调试,以获取有关如何捕获 OpenGL ES 帧的更多信息。
应用数据管理
iOS 为在 iOS 设备上安装的应用程序之间共享信息提供了强大的连接选项。使用基于 URL 的语法,你可以让你的应用程序访问来自 Web 的数据,以及将此信息传递给其他已安装的应用程序,如邮件、iTunes 和 YouTube。
你的应用程序可以声明一个独特的 URL 方案,允许任何应用程序与你的应用程序协作并共享数据。
你还可以选择使用 XML 文件;这些文件提供了一种轻量级结构化格式,你的应用程序可以轻松地读取和写入。XML 文件可以很好地适应 iOS 文件系统,并可用于在内置的用户默认值数据库中存储你的应用程序设置和用户偏好设置。这个基于 XML 的数据存储包括一个简单的 API,具有强大的功能,包括按需序列化和恢复复杂对象的能力。
注意
有关应用数据管理功能的更多信息,请参阅以下链接位置的Apple 开发者文档:DOCUMENTATION/DataManagement/Conceptual/iPhoneCoreData01/Articles/01_StartingOut.html#//apple_ref/doc/uid/TP40008305-CH105-SW2。
UI 自动化增强
自动化工具是在 iOS SDK 4.0 的版本中添加的。这个工具允许你通过脚本化触摸事件来自动化你的 iOS 应用程序的界面测试,允许你将这些结果记录下来,以便稍后用于分析。自动化工具包含一个脚本编辑器,因此你可以选择使用 JavaScript 将测试脚本写入 UI 自动化 API,或者从文件中将其加载到编辑器中。
这是在 iOS 平台上使用测试自动化测试你的应用程序的一个巨大的进步,这可以减少你手动测试应用程序所花费的时间。自动化功能可以用于模拟支持多任务处理且运行 iOS 4.0 或更高版本的设备上的许多用户操作。
你还有能力直接在你的脚本中捕获和记录在 iOS 设备上执行的动作。
自动化 UI 测试允许你:
-
为其他工作释放关键人员和资源
-
进行更全面的测试
-
开发可重复的回归测试
-
最小化程序错误
-
提高产品更新开发周期时间
自动化工具的一个重要好处是你可以用它与其他工具一起执行复杂的测试,例如追踪内存泄漏和隔离性能问题的原因。
注意
自动化工具不允许你处理任何未使用你的配置文件签名应用的应用程序,并且它不会在 iOS 模拟器中运行。它需要在运行 iOS 4 或更高版本的 iOS 兼容设备上运行。
准备你的应用程序
在我们开始使用自动化工具之前,我们需要做一些前期工作来准备我们的应用程序,以便它可以与自动化工具一起工作。UI 自动化库依赖于你的 UI 中的可访问性信息,因此我们将在稍后添加这一信息,这将使你的应用程序测试变得容易得多。
创建一个简单的 UIAutomation 应用程序
在我们继续之前,我们首先需要创建我们的 UIAutomation 项目。为了刷新你的记忆,你可以参考第一章,iOS5 中的新功能中名为 创建 MyEmailApp 应用程序 的部分。
-
从
/Xcode4/Applications文件夹中启动 Xcode。 -
选择创建一个新的 Xcode 项目,或者文件 | 新项目。
-
从可用模板列表中选择单视图应用程序模板。
-
从设备家族下拉菜单中选择iPhone。
-
确保你已经检查了设备家族下拉菜单下的使用自动引用计数复选框。
![创建一个简单的 UIAutomation 应用程序]()
-
点击下一步按钮,进入向导的下一个步骤。
-
输入
UIAutomation,然后点击下一步按钮,进入向导的下一个步骤。 -
指定你想要保存项目的位置。
-
点击** 保存 按钮继续并显示 Xcode 工作区环境。
现在我们已经创建了我们的 UIAutomation 项目,我们可以开始构建我们的用户界面,并添加所需的代码。
-
从项目导航器中选择并打开
ViewController.xib文件。 -
从对象库中选择并拖动一个(
UIButton)圆形矩形按钮控件,并将其添加到我们的视图中。 -
根据需要调整大小,然后修改圆形矩形按钮的对象属性部分,并将其标题设置为
Tap Me。 -
接下来,从对象库中选择并拖动一个(
UIButton)圆形矩形按钮控件,并将其添加到Tap Me按钮下面的视图中。 -
根据需要调整大小,然后修改圆形矩形按钮的对象属性部分,并将其标题设置为
Press Me。
如果您正确地遵循了这些步骤,您的视图应该看起来像以下截图所示。如果它与我的不完全相同,请随意调整您的。

如您所见,我们的表单在这个阶段并没有做什么,如果您在模拟器上运行此应用程序,您会看到控件像在屏幕上放置一样。
以下步骤将向您展示如何将按钮连接到它们的事件,以便它们可以执行各自的任务。那么,让我们开始吧。
-
打开
ViewController.h接口文件,并创建如下所示的高亮条目:#import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (strong, nonatomic) IBOutlet UIButton *btnTapMe; @property (strong, nonatomic) IBOutlet UIButton *btnPressMe; @end -
通过从导航菜单中选择在辅助编辑器中打开选项,或通过按住Option + Command + ,(选项键 + 命令键 + 逗号键),打开辅助编辑器窗口。
-
我们需要创建一个动作事件。选择 Tap Me 按钮,并在将此拖动到
ViewController.m实现文件类中时按住控制键,如图所示:![创建简单的 UIAutomation 应用程序]()
-
为要创建的动作指定一个名称。输入
btnTapMe作为动作的名称。 -
将事件类型设置为 触摸内部释放:
![创建简单的 UIAutomation 应用程序]()
-
点击 连接 按钮,让 Xcode 创建事件。
-
我们需要创建一个动作事件。选择 按我 按钮,并在将此拖动到
ViewController.m实现文件类中时按住 Control 键,如图所示:![创建简单的 UIAutomation 应用程序]()
-
为要创建的动作指定一个名称。输入
btnPressMe作为动作的名称。 -
将事件类型设置为 触摸内部释放:
![创建简单的 UIAutomation 应用程序]()
-
点击 连接 按钮,让 Xcode 创建事件。
现在我们已经连接了我们的动作事件,我们现在需要合成我们的用户界面控件,以便我们可以在视控制器中访问它们。
-
打开位于
CIFilterEffects文件夹中的ViewController.m实现文件,并在@implementation语句下方添加以下突出显示的语句。// // ViewController.m // UIAutomation // // Created by Steven F Daniel on 19/09/11. // Copyright (c) 2011 GenieSoft Studios. All rights reserved. // #import "ViewController.h" @implementation ViewController @synthesize btnTapMe, btnPressMe; ```** -
在这个代码片段中,我们使我们的实现文件知道位于用户界面表单上的控件。如果没有声明这些控件,我们将收到警告消息,这可能会使您的程序产生一些奇怪的结果,甚至可能使 iOS 设备上的应用程序崩溃。
-
接下来,我们需要将代码添加到我们的
btnTapMe函数中,该函数将用于在按钮被按下时显示一个警告消息弹出。为此函数输入以下代码片段:// Event to handle when the Tap Me button has been pressed. - (IBAction)btnTapMe:(id)sender { // Define our alert dialog popup UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"UIAutomation Example" message:@"Tap Me button pressed" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; // Display our alert [alert show]; } ```** -
此代码片段创建了一个
UIAlertView类的实例,它将使我们能够在按钮被按下时显示一个警告弹出对话框。您会注意到我们没有释放我们的警告对象变量。这主要是因为 ARC 将自动为我们管理这个对象的释放。 -
接下来,我们需要将代码添加到我们的
btnPressMe函数中,该函数将帮助我们确定自动化工具何时按下它。为此函数输入以下注释掉的代码片段:// Event to handle when the Press Me button has been pressed. - (IBAction)btnPressMe:(id)sender { // Define our alert dialog popup // UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"UIAutomation Example" message:@"Press Me button pressed" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; // Display our alert //[alert show]; } ```** -
此代码片段创建了一个
UIAlertView类的实例,它将帮助我们确定当我们在进行单元测试时,UI 自动化工具何时按下它。这段代码已被故意注释掉,以便我们能够失败我们稍后将要设置的自动化测试用例。
在下一节中,我们将探讨如何设置我们的控件,以便它们可以被自动化工具访问和通信。UI 自动化工具库依赖于您 UI 中的可访问性信息,并查找您控件的AccessibilityLabel属性。
-
从
UIAutomation示例项目导航窗口中,选择UIAutomation文件夹中的ViewController.xib文件。 -
点击“Tap Me”按钮,并选择“身份检查器”**按钮。
![创建一个简单的 UIAutomation 应用程序]()
-
确保已勾选“可访问性”选项,并且已勾选“用户交互启用”选项。这使得仅能访问具有这些属性设置的对象,可以直接从视图中访问。这主要是因为它们的属性被公开,这使得从
UIAutomation测试脚本中访问它们变得更容易。 -
重复步骤 2到步骤 3,并将相同的操作应用于“按我”按钮。
编写 UIAutomation 测试脚本
下一步是使用您选择的任何编辑器编写 JavaScript 测试脚本。测试脚本基本上是一组有序命令,每个命令都访问您应用程序中的一个用户界面元素,以执行某种用户操作或使用与之关联的信息。
您应用程序中的所有用户界面元素都通过UIAElements类及其子类定义的对象的有序层次视图来表示给脚本。
为了到达指定的 UI 元素,脚本只需从顶层目标对象开始调用元素层次结构。以下代码语句展示了如何在 JavaScript 中声明它:
var target = UIATarget.localTarget();
UIATarget对象是您在 iOS 设备或 iOS 模拟器上运行应用程序的主要起点。当您想要与 iOS 设备交互或需要在 iOS 设备上执行操作时,例如用户手势(包括点击、滑动和摇晃),此对象提供了一种方式。
app对象是UIAApplication类的实例,它为您提供了访问应用程序顶级结构的方式。这为您提供了访问诸如标签栏、导航栏和主窗口等元素的方式。以下代码语句展示了如何在 JavaScript 中声明它:
var app = target.frontMostApp();
现在您已经了解了 UI 元素结构,我们可以开始构建我们的UIAutomation测试脚本。所以,打开您最喜欢的编辑器,让我们开始吧。
-
创建一个新空白文档,并将其保存为
UIAutomation.js。 -
接下来,我们需要声明应用程序将使用的对象。如下面的代码片段所示:
// Initialise our application objects. var target = UIATarget.localTarget(); var app = target.frontMostApp(); var window = app.mainWindow(); var view = window.elements()[0]; var buttons = window.buttons(); ```** -
在这个代码片段中,我们声明了一组对象,我们可以在代码中使用并引用它们。
-
我们已声明了一个目标级别对象,它指向我们的层次结构的顶层,一个
app应用程序对象,以及窗口、视图和按钮对象,这些对象可以通过mainWindow方法访问。 -
使用
var关键字告诉编译器您想要在内存中声明一个新变量实例的对象。这类似于 Visual Basic 中的Dim(维度)关键字。 -
接下来,我们想在结果面板中添加一些初始标题信息,以显示我们正在为此运行哪个测试用例,如下面的代码片段所示:
// UI Automation Test Case - Initial Logging var testName = "UI Automation Test Case 1"; UIALogger.logStart(testName); ```** -
在这个代码片段中,我们使用
var关键字声明了一个名为testName的变量,并将其分配给自动化标题信息。这些信息将在结果面板中显示。接下来,我们使用UIALogger类的方法logStart。这告诉编译器启动指定的测试。 -
在下一步中,我们需要确定屏幕上有多少个按钮。如下面的代码片段所示:
// TC001: Check for the number of buttons on screen. UIALogger.logMessage("Assert Text - Check number of button(s) on screen"); if (buttons.length != 2) { UIALogger.logFail("FAIL: Invalid number of button(s)"); } else { UIALogger.logPass("PASS: Correct number of button(s)"); } ```** -
在这个代码片段中,我们使用
logMessage方法将消息记录到结果窗口。然后我们使用按钮对象来确定我们视图中有多少个可见的按钮,然后使用logFail和logPass方法来处理这种情况。logFail方法将消息记录到结果面板,指示测试未成功完成。logPass方法将消息记录到结果面板,指示测试已成功完成。** -
在下一步中,可能会有时候你想检查是否按下了特定的按钮。这在上面的代码片段中显示:
// TC002: Check for the existence of the Press Me // button within the view. UIALogger.logMessage("Assert Text - Check for the existence of the Press Me button."); // Get a handle to the button that we are after. var btnPressMe = buttons.firstWithName("Press Me"); if (btnPressMe == null || btnPressMe.toString() == "[object UIAElementNil]") { UIALogger.logFail("FAIL: Press Me button not found."); } else { UIALogger.logPass("PASS: Press Me button was found."); } ```** -
在这个代码片段中,我们使用
UIAElementArray类的firstWithName方法返回按钮数组中名为Press Me的第一个元素。然后我们使用null和UIAElementNil对象比较和检查按钮是否存在,以防止它引发异常错误,最后使用UIALogger类的logFail和logPass方法将测试结果输出到结果面板。 -
在下一步中,可能会有时候你想模拟屏幕上显示的特定按钮的点击,并显示一个弹窗。这在上面的代码片段中显示:
// TC003: Tap on the Press Me button and check for the alert. UIALogger.logMessage("Assert Text - Checking for the Press Me Alert dialog."); var btnPressMe = buttons.firstWithName("Press Me"); // Simulate a tap on the Press Me button btnPressMe.tap(); var alert = app.alert(); if (alert == null || alert.toString() == "[object UIAElementNil]") { UIALogger.logFail("FAIL: The alert dialog was not shown after pressing the button."); } else { UIALogger.logPass("PASS: The alert dialog was shown after pressing the button."); } ```** -
在这个代码片段中,我们使用
UIAElementArray类的firstWithName方法返回按钮数组中名为Press Me的第一个元素。然后我们使用按钮的tap方法来模拟点击。当发生这种情况时,与按钮关联的代码将被执行,并显示一个警报。然后,我们声明一个
alert变量,它接受由表示警报的app对象返回的警报UIAAlert对象。接下来,我们使用null和UIAElementNil对象比较和检查警报是否存在,以捕获错误,防止它引发exception错误。最后,我们使用UIALogger类的logFail和logPass方法将返回的结果输出到结果面板。** -
在我们的最后一部分,我们希望向结果面板显示我们的测试用例已完成。这在上面的代码片段中显示:
// UI Automation Test Case 1 Completed UIALogger.logMessage("UI Automation Test Case 1 Completed. Please check results panel for any errors."); ```** -
在这个代码片段中,我们使用
logMessage方法将消息记录到结果窗口,以显示 UI 自动化测试用例已完成,或者可以使用一个过程在延迟完成后点击警报对话框中的按钮。
以下表格显示了与 UIALogger 类相关的所有方法。它已被分成几个部分,以突出显示哪些用于记录状态,哪些可以用于指定严重性类型。
| 使用测试状态进行日志记录 | |
|---|---|
logStart |
记录一条消息,并指示测试已开始 |
logPass |
记录一条消息,并指示测试已成功完成 |
logIssue |
记录一条消息,并指示测试异常终止 |
logFail |
记录一条消息,并指示测试失败 |
| 使用严重程度级别进行记录 | |
logDebug |
记录指定的消息,并将严重程度级别设置为debug |
logMessage |
记录指定的消息,并将严重程度级别设置为message |
logWarning |
记录指定的消息,并将严重程度级别设置为warning |
logError |
记录指定的消息,并将严重程度级别设置为error |
注意
有关 UI 自动化类参考和 JavaScript API 的更多信息,您可以参考以下链接的Apple 开发者文档:developer.apple.com/library/ios/#documentation/DeveloperTools/Reference/UIAuto/_index.html.
现在我们已经创建了测试脚本,我们准备开始处理下一部分,即开始对UIAutomation示例应用程序进行性能分析。这将在下一节运行您的测试中介绍。
运行您的测试
现在我们已经创建了测试,我们的下一步是在 Instruments 应用程序环境中对UIAutomation示例应用程序进行性能分析。
-
从
/Xcode4/Applications文件夹中启动 Xcode。 -
打开
UIAutomation项目,或文件** | 打开。 -
从产品** | 配置菜单中选择配置,或Command + I。
** -
这将启动Xcode Instruments应用程序。从 iOS 模板部分选择自动化,如图所示:
![运行您的测试]()
-
接下来,点击配置按钮以进入下一步。
-
从Instruments窗口中,点击添加按钮,然后从下拉列表中选择导入,如图所示:
![运行您的测试]()
-
接下来,从列表中选择
UIAutomationTest.js文件,然后点击打开按钮将此文件加载到 Instruments 应用程序中。![运行您的测试]()
-
最后,点击记录按钮,或Command + R以开始对
UIAutomation示例应用程序进行性能分析。几分钟后,您的应用程序将启动,然后您的测试将运行。这在上面的屏幕截图中显示:![运行您的测试]()
-
一旦测试完成,Instruments 应用程序将继续运行您的应用程序。要正式结束正在执行的测试,请点击红色停止**按钮,或再次按Command + R。
测试结果列在详细视图部分,包括日志消息列中的测试名称。如果测试通过,日志类型列的值将是通过,显示为绿色。如果测试失败,日志类型值将是失败,显示为红色。
您可以选择扩展测试结果以查看发生情况的详细信息。截图列在测试失败时使用。在我们的情况下,没有显示警报对话框,因此捕获了截图以显示它失败了。这在上面的截图中显示:
![运行您的测试]()
-
回到
UIAutomation示例项目,并在btnPressMe事件中取消注释alert弹出对话框代码,如下面的代码片段所示:**```swift
// Event to handle when the Press Me button has been pressed. - (IBAction)btnPressMe:(id)sender {
// Define our alert dialog popup
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"UIAutomation Example" message:@"Press Me button pressed" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
// Display our alert
[alert show];
} -
现在,编译并重新运行测试。我们应该现在看到在详细视图部分和 日志消息 列下,所有测试现在都应该显示为 通过 的值,以绿色显示。这在上面的截图中显示:

如您所见,通过将 UIAutomation 工具作为测试的一部分,您可以减半测试时间,并更多地专注于修复在应用程序发布之前那些令人烦恼的程序错误。
# 摘要
在本章中,我们学习了针对 Xcode 开发工具所进行的改进。我们了解了自动引用计数(ARC)是什么,以及需要应用的一些编码差异。
我们还查看了对 Interface Builder、iOS 位置模拟器和 OpenGL ES 调试工具集的改进。为了结束本章,我们探讨了如何使用自动化工具来帮助对应用程序进行单元测试,使用 JavaScript 编写的测试脚本调用 UI 自动化 API。
在我们的最后一章中,我们将探讨如何使您的应用程序运行顺畅,Instruments 的新功能,以及如何使用这些功能来跟踪和改进应用程序的性能。**
第七章:使用 Instruments 使您的应用程序运行流畅
恭喜你来到这本书的最后一章。在本章中,我们将关注如何在我们的应用程序中有效地使用仪器来追踪可能影响 iOS 应用程序整体性能的区域。
这些类型的问题可能会使我们的应用程序运行缓慢,甚至可能在用户的 iOS 设备上崩溃。我们将探讨 Instruments 应用程序中包含的内置仪器的不同类型,以及我们如何使用 iOS 仪器的系统跟踪来帮助您追踪代码中的系统调用、内存和线程,这些可能会影响您 iOS 应用程序的性能。
接下来,我们将探讨如何配置仪器,以便在报告的跟踪文档中以不同的方式显示数据。
在本章中,我们将涵盖以下主题:
-
介绍 Instruments 环境
-
学习如何添加和针对不同的仪器集进行性能分析
-
学习如何检查 iOS 应用程序的性能
-
介绍 Instruments 家族的其他组件
-
介绍 Xcode 4.2 中包含的新 Instruments
我们有很多内容要介绍。所以,让我们开始吧。
仪器简介
Instruments 应用程序是一个强大的工具,它使您能够收集有关应用程序性能随时间变化的信息。通过使用 Instruments 应用程序,您可以根据各种不同类型的数据收集信息,并同时在同一时间查看它们。因此,这将使您能够发现其他情况下难以发现的趋势,并且这可以用来查看程序中运行的代码及其相应的内存使用情况。
Instruments 应用程序附带一个标准库,您可以使用它来检查代码的各个方面。您可以将 Instruments 配置为收集有关同一进程或系统上不同进程的数据。
每个仪器收集并显示与文件访问、内存使用、网络连接等相关信息的不同类型。以下截图显示了 Instruments 应用程序正在使用多种不同类型的仪器来监控系统行为,以分析我们的MapKitExample:

表格中的以下信息概述了 Instruments 应用程序的每个功能,并提供了每个部分覆盖的描述:
| Instruments 功能 | 描述 |
|---|---|
| Instruments 面板 | 此部分列出了所有已添加的仪器,供您针对它们进行性能分析。您可以通过选择并从仪器库中将每个仪器拖动到该面板中添加新仪器。此面板中的项目也可以被删除。 |
| 轨迹面板 | 此部分显示当前仪器返回数据的图形摘要。每个仪器都有自己的轨迹,它提供该仪器收集的数据图表。此面板中的信息为只读。 |
| 详细面板 | 此部分显示每个仪器收集的数据的详细信息。它显示用于在轨迹面板中创建图形视图的事件集。根据仪器的类型,此面板中表示的信息可以自定义以不同方式表示数据。 |
| 扩展详细面板 | 此部分显示当前在详细面板中选定的项目的详细信息。此面板显示为给定事件收集的完整堆栈跟踪、时间戳和其他仪器特定数据。 |
| 导航栏 | 此部分显示您的位置以及您到达那里的步骤。它包括两个菜单:活动仪器菜单和详细视图菜单。您可以在导航栏中单击条目以选择活动仪器以及详细视图中的信息和级别。 |
仪器跟踪文档工具栏允许您添加和控制仪器、打开视图以及配置轨迹面板。

以下表格为工具栏上的每个不同控件提供解释:
| 工具栏项 | 描述 |
|---|---|
| 暂停/恢复按钮 | 在录制过程中暂停收集跟踪数据。选择此选项实际上不会停止录制;它只是简单地停止仪器在录制过程中收集数据。当按下暂停按钮时,在轨迹面板中会显示跟踪数据中的间隙以突出显示这一点。 |
| 记录/停止按钮 | 开始或停止录制过程。您使用此按钮开始收集应用程序的跟踪数据。 |
| 循环按钮 | 允许您设置记录器在回放期间是否应该循环,以连续重复记录的步骤。如果您想为给定的步骤集收集多个运行,这可能很有用。 |
| 目标菜单 | 选择文档的跟踪目标。这是收集数据的过程。 |
| 检查范围控件 | 此控件允许您在轨迹面板中选择时间范围。当设置此范围后,仪器仅显示指定时间段内收集的数据。使用此控件上的按钮,您可以设置检查范围的起始和结束点,并清除当前范围。 |
| 时间/运行控件 | 显示当前文档跟踪经过的时间。如果跟踪文档包含与它关联的多个数据运行,您可以使用箭头控件选择要在轨迹面板中显示的运行数据。 |
| 视图 控制 | 隐藏或显示 Instruments 选项卡、详细选项卡和扩展视图选项卡。此控制使您能够仅关注您感兴趣的区域。 |
| 库 按钮 | 隐藏或显示仪器库窗口。 |
| 搜索 字段 | 此选项根据您输入的搜索词过滤详细选项卡中的信息。 |
Instruments 应用程序是 Xcode 4 工具安装的一部分,可以在 <Root>/Developer/Applications 文件夹中找到,其中 <Root> 是 Xcode 4 在您的系统上安装的文件夹。
跟踪 iOS 应用程序
Instruments 的一个常见用途是对您的应用程序进行系统跟踪。这个新奇的仪器被添加到 Xcode 4.2 的发布中,可以帮助您追踪可能影响您 iOS 应用程序性能的系统调用、内存和线程。
要展示 iOS Instruments 中系统跟踪的使用,我们将使用我们在 第六章 中创建的 MapKitExample,Xcode 工具改进。您有多种方法可以启动 Instruments 应用程序;您可以在运行 Instruments 后启动 iOS 应用程序,或者您可以使用 Xcode 中的 产品 菜单下的工具。下一节将向您展示如何运行和性能分析 theMapKitExample 应用程序项目。
加载 MapKitExample 项目
在我们开始性能分析 MapKitExample 项目之前,我们必须首先启动 Xcode 开发环境。这可以在 /Xcode4/Applications 文件夹中找到。或者,您可以使用 spotlight 通过在搜索框窗口中输入 Xcode 来搜索 Xcode。
-
选择 文件 | 打开 或 Command + O。
-
双击进入 MapKitExample 文件夹,并选择 MapKitExample.xcodeproj 文件。
![加载 MapKitExample 项目]()
-
点击 打开 按钮继续加载并打开文件到 Xcode 工作区环境。
我们现在需要开始运行和性能分析我们的应用程序,这将用于执行系统跟踪,以确定正在处理的线程和系统调用。
运行和性能分析项目
要在 Xcode 环境中运行 Instruments 应用程序,请在 构建目标 菜单下选择 构建用于分析 选项,或者使用键盘快捷键 Shift + Command + I,然后从 产品 菜单中选择 分析 选项以启动 Instruments 应用程序。同样,您也可以使用快捷键 Command + I。

一旦选择了此选项,您最终会在屏幕上看到 Instruments 应用程序窗口。以下截图显示了这一过程:

以下表格概述了每个可用的模板,以及 iOS 开发所需的模板:
| 模板 | 描述 |
|---|---|
| 空白 | 创建一个空白的跟踪文档,您可以添加自己的仪器组合。 |
| 时间剖析器 | 对一个或所有进程进行低开销和时间采样。 |
| 系统跟踪 | 提供您针对可能影响应用程序性能的操作系统不同方面的剖析能力。 |
| 活动监视器 | 此监视器监控整体 CPU、内存、磁盘和网络活动。 |
| 自动化 | 在您的应用程序内自动执行用户界面测试。 |
| 能耗诊断 | 显示有关设备上用于 GPU 活动、显示亮度、睡眠/唤醒、蓝牙、Wi-Fi 和 GPS 的能耗的诊断信息。 |
| 网络连接 | 使用此工具,您可以查看每个应用程序的每个连接上流动的数据量,以及一些有趣的统计数据,例如往返时间和重传请求。您可以使用这些信息来帮助减少网络流量和能耗。 |
| 分配 | 监控程序中内存和对象分配模式。 |
| 泄漏 | 检测应用程序中的内存泄漏。 |
| 线程 | 分析进程内线程状态转换,包括运行和终止的线程、线程状态以及相关的回溯。 |
| 文件活动 | 监控应用程序与文件系统的交互。 |
我们想要用于此示例的工具类型是系统跟踪工具。选择系统跟踪选项,然后点击剖析按钮,继续加载Instruments 跟踪文档窗口,并开始对 MapKitExample 应用程序进行剖析。
然后,您的应用程序将被分析,并对所有已对内存进行调用的系统调用和线程进行剖析。这还包括虚拟内存(VM)操作。

您会注意到,经过几秒钟后,您的跟踪信息将显示出来。这包含有关线程和系统调用的信息,以及您的应用程序当前正在进行的持续时间。其他信息,如虚拟内存故障,也被记录。
您可以选择通过点击红色记录按钮来停止应用程序的剖析,因为 Instruments 应用程序已经完成了其全面分析。
以下列表项显示了您在开发 iOS 应用程序时可能遇到的各类故障的比较,以及它们的解释。
VM 故障
虚拟内存(VM)是位于计算机硬盘上的一种辅助存储,当随机存取存储器(RAM)满时,操作系统会使用它。它用于所有正常的计算机应用程序。许多计算机没有正确设置虚拟内存,因此无法获得最佳性能,导致系统故障。
内存泄漏
当应用程序分配内存但从未释放时,会发生内存泄漏。
让我们更仔细地看看以下示例:
for (int i = 1; I <= 500; i++) {
NSString *MemStatus = [[NSString alloc]
initWithFomat:@"Memory allocating…"];
}
在这个代码片段中,我们在循环中分配了500个字符串来演示内存泄漏可能发生的方式。每次循环时,代码都会为每个新的字符串MemStatus分配内存,并且让每个分配的字符串指针超出作用域。正如你所看到的,分配的内存从未被释放,导致你的应用程序运行缓慢,甚至可能引发崩溃或简单地挂起。
运行时错误
这类错误会导致你的应用程序停止执行。它可能是由未处理的异常引起的,这些异常是由于内存不足问题,或者你正在将一些数据写入数据库,或者你可能已经超过了字段可以处理的允许的最大大小。
编译时错误
这类错误最为明显,因为直到所有这些错误都被修复,你的程序将无法编译(因此无法运行)。通常,这些错误来自打字错误。
Xcode 中的 Objective-C 编译是区分大小写的,这意味着UIcolor和UIColor被处理为不同。例如,在 Objective-C 中,编译器可以理解以下:
self.view.backgroundColor = [UIColor blueColor];
但如果你输入:
self.view.backgroundColor = [UIColor bluecolor];
编译器会将其称为编译时错误,因为你指定了它无法识别的语言特定(语法)。
Instruments 窗口的Trace Highlights部分显示了一组基于已分析信息的有用图表。它包含显示系统总体使用情况的图表,以及线程数、系统调用和虚拟内存调用的数量。
此视图中每种颜色都表示与每个不同轨迹相关的信息,以及每个方法属于哪个库。单击任何图表都会进入一个摘要视图,显示每个部分的总体分解,包括调用栈视图、持续时间等。

你还可以更改并使每个图表的颜色以不同的颜色显示。这可以通过单击此截图所示的Scheduling按钮图标来完成。
要更改任何颜色,请从弹出列表中单击每个颜色。这将显示位于 Instruments 窗口左侧的颜色轮,你可以在其中更改颜色轮提供的相应颜色值。

在本节中,我们探讨了如何使用 Instruments 应用程序运行和配置现有项目,通过使用 iOS 的系统跟踪仪来帮助我们追踪应用程序性能问题。
我们探讨了 Instruments 应用程序中可用的不同视图,以及如何通过 Trace Highlights 视图将跟踪文档的结果表示为彩色编码的图表,以指示每个方法属于哪个部分。
添加和配置仪器
Instruments 应用程序内置了广泛的工具,可以帮助您更轻松地工作,通过它们从一个或多个进程中收集数据。大多数这些工具使用时配置简单,只需将它们添加到您的跟踪文档中即可开始收集跟踪数据。我们将探讨如何将工具添加和配置到现有的跟踪文档中。
使用 Instruments 库
Instruments 库显示了您可以使用并添加到跟踪文档中的所有仪器。库包含与 Xcode 4 安装一起提供的所有内置仪器,以及您已经创建的任何自定义仪器。
要打开 Instruments 窗口,请从跟踪文档窗口中点击 库按钮,或从菜单栏中选择 窗口 | 库。或者,您可以使用 Command + L 键盘快捷键。

如您从屏幕截图中所见,Instruments 库列表包含大量仪器,数量会随着时间的推移而增长,尤其是当您开始添加自己的自定义构建仪器时。
库列表提供了多种组织方式,通过不同的查看模式来查找您正在寻找的仪器。查看模式帮助您决定在任何时候应显示多少信息,以及您希望该仪器组占用多少空间。
在以下表中,我们描述了 Instruments 库支持的查看模式。
| 查看模式类型 | 描述 |
|---|---|
| 查看图标 | 此设置仅显示代表每个仪器的图标 |
| 查看图标和标签 | 此设置显示带有仪器名称的图标 |
| 查看图标和描述 | 此设置显示每个仪器的图标、名称和完整描述 |
| 查看小图标和标签 | 此设置显示仪器的名称及其小图标版本 |
除了设置 Instruments 库的查看模式外,仪器还可以分组组织,这使得识别哪个仪器属于哪个组变得更容易。这在上一个屏幕截图中有所展示。
在库中定位仪器
在库中定位仪器的有两种方法。一种常见的方法是使用分组选择标准控件,该控件位于库窗口的顶部,可以用来选择一个或多个组以限制在库窗口中显示的仪器数量。
如果您将弹出菜单和仪器面板之间的分隔条向下拖动,您会注意到弹出菜单从单选选择变为轮廓视图,这样您就可以通过按住Command键组合,然后使用鼠标选择所需的组来选择多个组,如图下截图所示:

另一种过滤仪器库窗口内容的方法是使用位于库窗口底部的搜索字段。通过使用此搜索字段,您可以快速缩小范围并仅显示名称、描述、类别、列表或关键字中包含搜索关键字的所有仪器。
在以下截图中,显示所有包含搜索字符串file的仪器。

添加和删除仪器
有时候,您可能想要将您的应用程序与其他仪器库中的仪器进行跟踪。这可能是因为您想检查应用程序在设备上的性能以及应用程序消耗了多少电池。
您可以将任意数量的仪器添加到您的跟踪文档中,但请注意,库中并非所有仪器都能跟踪广泛的系统进程;您会发现有些仪器只能跟踪单个进程。为了解决这个问题,您可以添加多个仪器的实例,并将每个实例分配给不同的进程。通过这种方式,您可以收集同时运行的多程序的相关信息。
要将仪器添加到跟踪文档中,请从仪器库中选择仪器,然后将其拖动到仪器面板或跟踪文档的轨迹面板上,如图下截图所示:

要从跟踪文档中删除仪器,请从仪器面板中选择您想要删除的仪器,然后按下键盘上的Delete键。您将收到一个确认消息。点击确定按钮继续。

在下一节中,我们将探讨如何配置您已添加到跟踪文档中的仪器。
配置仪器
你会发现,你添加到跟踪文档中的大多数仪器都是即用型的。然而,一些仪器可以使用仪器检查器进行配置,并且根据配置的仪器类型而有所不同。
你会注意到,大多数仪器都包含配置轨道窗格内容的选项,而只有少数仪器包含额外的功能来确定仪器收集的信息类型。要配置一个仪器,请从仪器窗格中选择该仪器,然后点击位于仪器右侧的仪器检查器图标。这在上面的屏幕截图中显示:

当点击仪器检查器图标时,它会在仪器名称旁边显示检查器配置对话框。要关闭检查器,请点击突出显示的X的关闭按钮。你可以使用类似的方式,通过命令 + I 和 文件 | 获取信息命令来关闭此窗口。根据配置的仪器类型,它们可以在记录跟踪文档中的数据之前、期间或之后进行配置。
在你配置的这些仪器的检查器控制中,你可以找到缩放控制。此功能控制显示在轨道窗格中的跟踪数据的放大倍数,并调整仪器在轨道窗格中的高度。或者,你也可以使用视图 | 减小面板大小和视图 | 增大面板大小菜单选项来完成相同的事情。
仪器家族的其他组件解释
除了追踪内存泄漏和分配对象之外,Instruments 应用程序还包含其他仪器。尽管并非每个仪器都与 iOS 应用程序一起工作,但以下表格解释了与哪种类型相关的仪器列表:
| 仪器 | 平台 | 描述 |
|---|---|---|
| 活动监视器 | iOS / 模拟器 | 将系统工作负载与虚拟内存大小相关联。 |
| 分配 | iOS / 模拟器 | 这可以用来在应用程序执行任务时捕获堆快照。如果在两个不同时间点进行捕获,可以用来识别内存丢失或未泄漏的情况。测试案例是先捕获快照,在应用程序中执行某些操作,然后撤销这些操作,使应用程序返回到执行操作之前的状态。如果堆中分配的内存相同,则无需担心。这是一个简单且可重复的任务执行测试场景,并使应用程序返回到执行任务之前的状态。 |
| 自动化 | iOS / 模拟器 | 用于自动化 iOS 应用程序的用户界面测试。 |
| 核动画 | iOS | 通过视觉提示来衡量在 iOS 设备上运行的进程中的核动画每秒帧数,这些提示有助于您理解内容如何在屏幕上渲染。 |
| CPU 采样器 | iOS / 模拟器 | 将整体系统工作量与您的应用程序所做的工作相关联。 |
| 能量诊断 | iOS | 显示有关设备上用于 GPU 活动、显示亮度、睡眠/唤醒、蓝牙、WiFi 和 GPS 的能量使用情况的诊断信息。 |
| 文件活动 | 模拟器 | 通过监控文件何时打开、关闭、读取和写入操作来检查系统中的文件使用模式。它还监控文件系统本身的更改,包括权限和所有者变更。 |
| 内存泄漏 | iOS / 模拟器 | 此工具寻找内存已分配但无法再使用的场景。这些内存泄漏可能导致应用程序崩溃或关闭。 |
| OpenGL ES 驱动程序 | iOS | 确定您在 iOS 设备上使用 OpenGL 和 GPU 的效率。 |
| 系统使用情况 | iOS | 记录在 iOS 设备上对进程内文件操作函数的调用。 |
| 线程 | 模拟器 | 分析进程内的状态转换,包括运行和终止的线程、线程状态以及相关的回溯。 |
| 时间分析器 | iOS / 模拟器 | 执行低开销和时间基础的采样,针对一个或所有进程。 |
| 僵尸 | 模拟器 | 僵尸工具在已释放的对象位置保持一个空或“死亡”对象(在某种意义上),这些“死亡”对象随后被有缺陷的应用程序逻辑访问,导致应用程序执行中断而不会崩溃。这些“僵尸”对象接收调用,并将工具指向应用程序通常崩溃的确切位置。 |
Instruments 的新功能
Xcode 中的 Instruments 应用程序包含一系列内置工具,旨在使您的工作更轻松,并收集和显示一个或多个进程的数据。在 Xcode 4.2 中,添加了一系列新的工具,以下将进行解释。
带有 CPU 策略的时间分析器
时间分析器工具展示了每个代码段花费了多少时间。这允许开发者确定在发布前需要重构的逻辑部分。尽管这可以在 iOS 模拟器上运行,但建议在 iOS 设备上运行,因为两者的性能差异很大。

在时间分析器工具中,您可以使用此栏左侧的按钮来显示跟踪视图窗格,使用以下表中显示的三个策略之一:
| 视图模式类型 | 描述 |
|---|---|
| CPU 策略 | 此设置显示每个活动核心上的 CPU 活动。此策略可以帮助您确定您的应用程序是否实现了并发。 |
| Instruments 策略 | 此设置以单条轨迹显示 CPU 活动。这是默认策略。 |
| 线程策略 | 此设置显示每个单独线程的 CPU 活动。 |
时间分析器工具还向开发者提供了在 iPad 2 和 iPhone 4/4S 上运行他们正在开发的应用程序的能力。您可以使用 CPU 策略功能来测量每个 CPU 核心的活动。
这在上一张截图中的红色矩形中突出显示。如果您的应用程序支持并发,这应该同时显示 iPad 2 两个核心的活动证据。
注意
CPU 策略功能目前仅在时间分析器工具中可用。
您还可以配置时间分析器工具以限制活动处理器核心的数量。这是为了允许您配置应用程序以查看它在运行较少核心的系统上的性能。例如,如果您有一台运行四个活动核心处理器的 MacBook Pro,但您想将其限制为与两个活动核心处理器一起工作,以查看这在运行两个核心的 MacBook Pro 上的性能分析。
如果您的 CPU 支持多线程,这也可以称为超线程。这意味着对于每个物理核心,都有一个第二个逻辑核心。例如:如果您有一个启用了超线程并且运行在四个物理核心上的系统,这将导致系统运行在总共八个核心上。

此截图显示了如何配置活动处理器核心的总数。此屏幕可以通过Instruments | 预设菜单选项访问,或者您也可以使用Command + 键组合。
从此屏幕,点击常规选项卡,然后从活动处理器核心中选择或取消选择硬件多线程复选框,前提是您的系统支持此功能。您还可以使用滑块来指定要使用的活动核心数量。
注意
对活动核心数量的任何更改都不会关闭任何处理器核心——相反,Instruments 应用程序会通知系统不要在已停用的核心上调度工作。
iOS 系统跟踪器
iOS 系统跟踪器工具让您能够针对可能影响应用程序性能的操作系统不同方面进行性能分析。这提供了有关任何系统调用、线程调度和虚拟内存(VM)操作的信息。
此仪器可能有用的情况示例包括:当你想知道为什么你的代码没有及时在 CPU 上执行时,或者如果你是游戏开发者,你想找出为什么你的应用程序帧率意外下降。
在 Instruments 4.2 中,你可以使用系统跟踪工具来分析 iOS 和 Mac OS X。
注意
有关如何使用此类仪器的更多信息,请参阅本章中名为跟踪 iOS 应用程序的部分。
网络连接
网络连接仪器让你能够检查你的 iOS 应用程序如何使用 TCP/IP 和 UDP/IP 连接。当你使用此仪器时,它会自动捕捉所有打开的端口的快照,并在详细视图中报告它们的累积网络活动,以查看每个连接和每个应用程序有多少数据流动。
以下截图显示了详细视图中的连接摘要部分,显示了传入和传出的网络连接,以及所有进程的所有打开连接。
你还会看到,你可以查看统计数据,例如往返时间和重传请求,以帮助减少网络流量和能耗。

详细视图还允许你从以下显示视图中选择;以下表格中解释了这些视图:
| 视图模式类型 | 描述 |
|---|---|
| 进程摘要 | 此设置仅聚合每个进程的累积数据。 |
| 接口摘要 | 此设置按网络聚合数据。 |
你还可以选择让详细视图显示一组有用的图表,通过选择跟踪高亮选项,如下面的截图所示:

此截图显示了端口活动和进程活动的比较。端口活动功能测量传入和传出的连接。进程活动功能测量应用程序使用的活动。
网络活动
网络活动仪器帮助你在网络(蜂窝和 Wi-Fi)和能耗之间架起桥梁。你可以使用此仪器来跟踪通过每个网络接口的数据流量总量,以及直接从电池获取的能耗水平,同时关联 iOS 设备中的网络活动与能耗,并且它也是 iOS 能耗诊断模板的一部分。
以下截图显示了能耗诊断跟踪文档,显示了显示不同能耗级别的运行结果:

当你第一次运行这个程序时,你会注意到网络活动频繁到足以保持进程活跃,这将导致更大的能量消耗。如果你再次运行这个程序,你会注意到相同的数据以更大的但更少的数据包形式传输,允许应用程序在传输之间进入休眠状态。
能源诊断仪器是苹果给予开发者的最激动人心的工具。这个仪器将帮助你通过尽可能接近真实世界场景来测试你的应用程序,从而识别 iOS 设备资源的最佳使用方式。
收集的数据可以稍后进行分析,以查看每个功能消耗了多少设备的电池寿命,并且它将告诉开发者设备的各种组件各自使用了多长时间。如果你需要知道用户的位置,它将告诉你哪些设备被打开以及持续了多长时间。GPS是消耗设备大量电池寿命的特定资源。一旦获得位置信息,关闭位置服务是理想的。
注意
如果你想要了解更多关于 Instruments 的信息,可以参考以下链接提供的Instruments 用户指南文档:developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Introduction/Introduction.html。
摘要
在本章中,我们专注于 Xcode Instruments 应用程序的新增功能,以及我们如何使用这个出色的工具来确保我们的应用程序运行顺畅,不受可能影响应用程序性能的瓶颈的影响。
我们查看了 Instruments 应用程序中成为其一部分的每种内置仪器的不同类型,特别是 iOS 仪器的系统跟踪。这有助于追踪可能影响 iOS 应用程序性能的系统调用、内存和线程。
我们在本章结尾处探讨了如何配置仪器,以便在跟踪文档中用不同的方式表示数据。
我希望你喜欢阅读它,就像我喜欢写作它一样。这当然不是你的终点。在 Xcode 和 iPhone 的世界中还有很多东西可以探索。别担心!你不会孤单一人。有很多开发者愿意在你需要的时候帮助你:developer.apple.com/devforums/。
祝你在 Xcode 的旅程中一切顺利。我希望不久就能在苹果应用商店看到你的应用程序!

















































**





浙公网安备 33010602011771号