XCode4-秘籍-全-
XCode4 秘籍(全)
原文:
zh.annas-archive.org/md5/c634ae0f8fd0b9c8e2ef376220143e31译者:飞龙
前言
《Xcode 4 烹饪书》为你提供了技能和知识,以及如何在行业内开发有用应用程序的实际食谱。通过使用这本烹饪书的逐步风格方法,以食谱风格呈现示例,让你直接进入你感兴趣的主题,或者跟随章节中的主题来获得深入的知识,你将获得开发一些令人惊叹的应用程序所需的技能。
这本烹饪书是一本实用指南,包含超过 100 个食谱,展示了如何通过整合 iCloud、Facebook、移动核心服务、Core Image 和媒体播放器框架、以及 Core Graphics 和 Core Motion 框架来构建你自己的有趣和令人兴奋的 iOS 应用程序,这将使你能够通过内置的图像过滤器创建一些惊人的图像和过渡效果。
在这本书中,我尽力使代码简单易懂。我在每个步骤都提供了详细的步骤说明和大量的截图,以便更容易跟随。你很快就会掌握 iOS 6 编程的不同方面,以及创建一些令人惊叹的应用程序所需的技术和技能。
如有任何疑问,请随时通过<geniesoftstudios@gmail.com>联系我,或者如果你只是想过来打个招呼“你好”。任何关于改进这本书的建议都将受到高度重视。
本书涵盖的内容
第一章, 获取和安装 iOS SDK 开发工具,向开发者介绍了 Xcode 开发工具集,以及 iOS 模拟器和 iOS 架构中每个层的功能,最后探讨了如何创建一个简单的Hello World iOS 应用程序。
第二章, 用户界面 – 创建 UI,介绍了视图的概念以及它们如何成为完整的 iOS 应用程序的一部分。通过探索大量不同的视图组件,你将创建不同的应用程序,帮助你理解每个组件的工作原理。我们还将了解模型-视图-控制器(MVC)模式以及如何使用它来创建适合增强用户体验的应用程序。通过本章,你还将了解最有用的控制器,这些控制器将是你未来许多项目的组成部分。
第三章, 使用 Storyboard,了解 Storyboard 是什么以及我们如何应用视图之间的各种过渡。我们将探讨如何创建和配置场景和 Storyboard 文件,以编程方式展示这些内容。最后,我们将学习如何将 Twitter 功能集成到我们的应用程序中,使用新的 iOS 6 Social框架发送照片和标准消息。
第四章, 使用 Xcode 工具, 主要介绍如何在我们的应用程序中有效地使用工具来追踪内存泄漏和消除可能导致应用程序在用户的 iOS 设备上崩溃的瓶颈。我们还将探讨如何添加和配置工具,以及如何使用系统跟踪工具来监控系统调用并追踪应用程序中的性能问题。
第五章, 使用位置服务和 MapKit 框架, 提供了使用内置位置服务的详细指南,以创建向用户提供位置信息的应用程序。您不仅将学习如何使用 GPS 硬件,还将学习如何使用叠加来显示地图和布局信息。
第六章, 在云中存储文档, 介绍了使用 iCloud 的好处以及如何将 iCloud 功能集成到您的应用程序中,以使用存储 API 存储和检索文件及其数据。本章还将为您提供一些关于如何处理多个 iOS 设备上同一文件副本更新的文件版本冲突的见解。
第七章, 处理不同的多媒体资源, 主要教授您如何通过设备的硬件创建应用程序来捕捉、重现和管理多媒体内容。您不仅将学习如何使用相机捕捉图像和视频,还将学习如何播放和录制音频。我们还将学习如何实现不同的图像过滤效果和过渡动画来产生水波效果,以及如何将 Airplay 功能集成到我们的应用程序中。
第八章, 使用 CoreData 和 GameKit 框架, 主要向您展示如何使用 Core Data 框架创建一个简单的图书库应用程序,直接与SQLite数据库接口,创建和存储书籍详情。我们还将探讨如何集成蓝牙功能,以便您可以将书籍详情发送到另一台 iOS 设备,并且这些信息可以无线接收并在另一端数据库中存储。
第九章, 使用 Facebook iOS SDK 创建社交网络应用,展示了如何下载 Facebook SDK 并将您的应用程序注册到 Facebook 上。它还展示了如何使用 Facebook API 将 Facebook 功能集成到您的应用程序中,使用单点登录(SSO)功能。这为用户提供使用他们的 Facebook 身份登录到您的应用程序的能力,以便他们可以提交通知请求或提交内容到他们的墙。我们将学习如何使用 Open Graph API 和 Facebook 查询语言(FQL)传递类似 SQL 查询的语法来检索有关当前用户的信息,以及如何在我们的 iOS 应用程序中干净地处理 Facebook 错误。
第十章, 打包和部署您的应用程序,将指导您完成将完成的应用程序部署到设备上的必要步骤,同时向您展示如何准备和分发它到 App Store。我们还将探讨如何创建和获取开发和分发所需的配置文件。
附录, 探索多点触控界面,讨论了如何通过设备的传感器创建完全了解其周围环境的应用程序。您将学习如何根据设备方向调整用户界面以及如何响应加速度计和陀螺仪事件。您还将了解内置的摇晃手势以及如何响应摇晃动作。
您需要为本书准备的内容
本书的最低要求是运行 Mac OS X Snow Leopard (10.6.)或 Lion (10.7.)的基于 Intel 的 Macintosh 计算机。我强烈建议升级到 Lion 或 Mountain Lion,因为 Xcode 中有很多仅适用于这两个操作系统的全新功能。
我们将使用 Xcode 4.6.2,这是用于创建 iOS 应用程序的集成开发环境。您将使用本书创建的所有项目几乎都可以在 iOS 模拟器上运行。然而,一些项目将需要设备才能正确运行。您可以通过以下链接下载 Xcode 的最新版本:developer.apple.com/xcode/
本书面向的对象
如果您曾想构建与 Facebook、iCloud、Core Location 和 Core Motion 框架交互的应用程序,那么这本书就是为您准备的。您应该具备良好的 Objective-C 知识和编程经验,并使用过 Xcode 4 和 iOS 5。
术语约定
在本书中,您将找到许多不同风格的文本,以区分不同类型的信息。以下是一些这些风格的示例及其含义的解释。
文本中的代码单词如下所示:“在 Objective-C 中使用编译器指令时,它们负责响应并执行由#ifdef和#endif标签封装的关联代码片段”。
代码块以如下方式设置:
- (IBAction)btnTapHere:(id)sender { 
   NSString *greeting = [NSString stringWithFormat:@"Welcome 
                To Xcode 4 Cookbook series %@ %@",txtFirstname.text, txtSurname.text]; 
    lblOutput.text = greeting; 
    lblOutput.font = [UIFont boldSystemFontOfSize:21]; 
    lblOutput.textColor = [UIColor blueColor]; 
}
当我们希望将您的注意力引向代码块中的特定部分时,相关的行或项目将以粗体显示:
//  SecondViewController.h
//  TwitterExample
//
//  Created by Steven F Daniel on 21/09/12.
//  Copyright (c) 2012 GenieSoft Studios. All rights reserved.
#import <UIKit/UIKit.h>
@interface SecondViewController : UIViewController<UIAlertViewDelegate>
@end
新术语和重要词汇以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“要继续执行您的应用程序,请点击继续程序执行按钮”。
注意
警告或重要注意事项以如下方式显示。
小贴士
小贴士和技巧看起来是这样的。
读者反馈
我们读者的反馈总是受欢迎的。让我们知道您对这本书的看法——您喜欢什么或可能不喜欢什么。读者反馈对我们开发您真正从中获得最大收益的标题非常重要。
要向我们发送一般反馈,只需发送一封电子邮件到<feedback@packtpub.com>,并在邮件主题中提及书籍标题。
如果您在某个主题上具有专业知识,并且您对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在您是 Packt 书籍的骄傲拥有者,我们有众多方法可以帮助您从购买中获得最大收益。
下载示例代码
您可以从www.packtpub.com您购买的 Packt 书籍的账户中下载示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
错误清单
尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何错误清单,请通过访问www.packtpub.com/submit-errata来报告它们,选择您的书籍,点击erratasubmissionform链接,并输入您的错误清单详情。一旦您的错误清单得到验证,您的提交将被接受,错误清单将被上传到我们的网站,或添加到该标题的“错误清单”部分。任何现有的错误清单都可以通过从www.packtpub.com/support选择您的标题来查看。
侵权
在互联网上,版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果你在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过 <copyright@packtpub.com> 联系我们,并提供涉嫌盗版材料的链接。
我们感谢你在保护我们作者和提供有价值内容的能力方面的帮助。
问题和建议
如果你在本书的任何方面遇到问题,可以通过 <questions@packtpub.com> 联系我们,我们将尽力解决。
第一章:获取和安装 iOS SDK 开发工具
在本章中,我们将介绍:
- 
下载和安装 iOS SDK 
- 
使用 Xcode 创建 iOS 项目 
- 
使用 Interface Builder 创建用户界面 
- 
为我们的应用程序构建用户界面 
- 
创建连接到 Interface Builder 对象的出口 
- 
创建响应用户操作的动作用户界面 
- 
编译您的项目 
- 
使用 iOS 模拟器测试您的应用程序 
- 
配置和使用编译器指令 
- 
使用 Xcode 调试您的 iOS 应用程序 
- 
使用 Clang 静态分析器检查您的代码 
简介
欢迎来到使用 iOS 6 的激动人心的 iOS 编程世界。这个移动操作系统的最新版本包含了一些出色的全新功能和改进,并提供了超过 200 个新功能,以及一个更新的 SDK,其中包含超过 1,500 个新的开发 API,这些 API 可以集成到您的应用程序中。
在本章中,我们将探讨开发 iOS 平台应用程序所需的 集成开发环境(IDE)和软件开发工具包(SDK)。我们将解释每个工具在开发周期中的作用的重要性,最后开发我们的第一个应用程序。以下是开发 iOS 平台应用程序所需的工具说明:
- 
运行 Snow Leopard(10.6.)、Lion(10.7.)或 OS X Mountain Lion(10.8.*)操作系统的基于 Intel 的 Mac 计算机:基本开发工具不能安装在其他计算机平台上,因此如果您正在运行其他处理器类型(如较旧的 Mac G4 或 Mac G5),那么您就没有运气了。 
- 
iOS 5 SDK(或更高版本):为了下载 Apple iOS SDK,您必须注册为 Apple 开发者。iOS SDK 包括以下组件: 组件 描述 Xcode 这是主要的 IDE,允许您使用 Objective-C 编程语言开发、编辑和调试 iOS 和 Mac 平台的原生应用程序。 iOS 模拟器 这是一个基于 Cocoa 的应用程序,它允许您在计算机上调试 iOS 应用程序,而无需拥有 iOS 设备。在模拟器中,许多 iOS 功能根本无法工作,因此如果应用程序使用这些功能,则需要设备,即 Core Location 和 MapKit 框架。 Instruments 这些是分析工具,可以帮助您优化应用程序,并在应用程序执行过程中实时监控内存泄漏。 Dashcode 这允许您开发基于 Web 的 iOS 应用程序和仪表板小部件。 
下载和安装 iOS SDK
这个配方包括如何注册 Apple 开发者计划的信息,以及如何下载和安装使用 Xcode 开发应用程序所需的必要工具。
准备工作
在您开始构建 iOS 应用程序之前,您必须首先作为 iOS 开发者计划的注册用户加入,以便将所有必要的组件下载到我们的电脑上。在撰写本文时,最新版本是 4.5.2,iOS SDK 的最新版本是 6.x。注册过程是免费的,并为您提供访问 iOS SDK 和其他对您开始开发非常有用的开发者资源的权限。
以下简短列表概述了您成为 iOS 开发者计划的成员后可以访问的一些内容:
- 
有助于快速入门的实用指南 
- 
显示如何将您的应用程序提交到 App Store 的实用技巧 
- 
能够下载 iOS 软件的当前版本 
- 
能够测试 iOS 和 iOS SDK 的发布版本 
- 
访问苹果开发者论坛 注意无论您是为 iPhone 还是 iPad 开发应用程序,它们都使用相同的 操作系统(OS)和 iOS SDK,允许您创建适用于运行 iOS 4.3.* 及以上版本的 iPhone 和 iPad 的通用应用程序。 
如何操作...
为了准备您的电脑进行 iOS 开发,您需要按照以下顺序下载和安装必要的组件:
- 
要注册 iOS 开发者计划,您需要访问 developer.apple.com/devcenter/ios/index.action,然后点击 登录 按钮继续,如下面的截图所示:![如何操作...]() 
- 
注册后,您将能够下载 iOS SDK 并继续将其安装到您的电脑上。 
- 
您也可以从以下链接的 Mac App Store 获取 Xcode itunes.apple.com/us/app/xcode/id497799835?mt=12,具体取决于您是否选择了适用于 Mac OSX Lion 的版本。以下步骤中的安装过程显示了如何安装 Snow Leopard 的 iOS 开发工具。
- 
下载完 Snow Leopard 的 SDK 后,您可以继续安装它。您将需要接受一些许可协议。完成这些后,您需要做的就是选择安装的目标文件夹,然后点击 继续 按钮。 
- 
如果您在安装阶段选择默认设置,各种工具将被安装在 /Developer/Applications文件夹中。安装过程会带您通过自定义安装选项屏幕,如下面的截图所示:![如何操作...]() 
这些选项为您提供了对安装过程的一定程度的控制。例如,您可以选择您想要安装 Xcode 的文件夹位置,以及各种其他选项的设置。
工作原理...
现在一切都已经安装并准备就绪,我们的下一步是查看 Xcode 和 iOS SDK 中每个组件需要什么。
如本章简介部分所述,iOS SDK 由三个重要组件组成。主要组件是 Xcode IDE,这是苹果的 IDE,允许开发 iOS 和 Mac 平台的应用程序,并使用 Objective-C 作为默认编程语言。
此环境允许更好地集成和编辑源代码,以及构建、编译和调试您的应用程序。它包含许多可以帮助诊断您的 iOS 应用程序问题的工具。关于仪器的主题将在本书的后面部分介绍。IDE 包含一个设备信息窗口,称为组织者 – 设备,如下面的截图所示:

此屏幕是安装部署到设备进行测试或在苹果应用商店分发所需的各种证书和配置文件所必需的。使用组织者 – 设备窗口,您可以查看您应用程序的调试信息、崩溃日志,以及从设备截取屏幕截图的能力。
- 
界面构建器:这是集成在 IDE 中的用户界面设计器。界面构建器为您提供了构建应用程序用户界面所需的所有必要功能。所有对象都存储在一个或多个资源文件中,并包含与每个对象的关联关系。您对表单设计所做的任何更改都会自动同步回您的代码。 
- 
iOS 模拟器:这是一个非常有用的工具,可以作为测试床来测试您的应用程序,而无需使用实际的设备,无论是 iPad 还是任何其他 iOS 设备。每次您构建和运行应用程序时,Xcode 都会自动将您的应用程序安装到 iOS 模拟器上并启动它。 iOS 模拟器具有模拟不同 iOS 版本的能力,如果您的应用程序需要安装在不同的 iOS 平台上,或者测试和调试在不同 iOS 版本下运行时应用程序中报告的错误,这将变得极其有用。 
还有更多...
以下列表提供了包含安装所需工具和信息的链接:
- 
苹果 iOS 开发者门户: developer.apple.com/devcenter/ios/index.action
- 
苹果开发者工具信息: developer.apple.com/technologies/tools/whats-new.html
参见
- 
编译您的项目配方 
- 
使用 Xcode 调试您的 iOS 应用程序配方 
- 
在第十章打包和部署您的应用程序中的使用配置文件在 iOS 设备上安装 iOS 应用程序食谱,打包和部署您的应用程序,第十章。 
- 
在第十章打包和部署您的应用程序中的使用 iTunes Connect 提交应用程序到 App Store食谱,打包和部署您的应用程序,第十章。 
使用 Xcode 创建 iOS 项目
在本食谱中,我们将看到使用 Xcode IDE 创建我们的第一个 iOS 应用程序项目是多么容易。
准备工作
现在我们已经安装了所有初步组件,我们将开始使用 Xcode 创建我们的第一个HelloWorld项目。
如何操作...
要开始创建新的 Xcode 项目,请执行以下简单步骤:
- 
从 /Developer/Applications文件夹启动 Xcode。
- 
选择创建新的 Xcode 项目,或文件 | 新建项目。 
- 
从可用模板列表中选择单视图应用程序,如图下所示: ![如何操作...]() 
- 
点击下一步按钮以继续向导的下一步。 
- 
接下来,将项目名称输入为 HelloWorld。
- 
从设备下拉菜单中选择iPhone。 
- 
确保未勾选使用 Storyboards复选框。 
- 
确保已勾选使用自动引用计数复选框。 
- 
确保未勾选包含单元测试复选框。 ![如何操作...]() 注意您的应用程序的公司标识符需要是唯一的。苹果建议您使用反向域名样式(例如, com.domainName.appName)。
- 
点击下一步按钮以继续向导的下一步。 
- 
指定您想要保存项目的位置。 
- 
然后,点击创建按钮以在指定位置保存您的项目。 
一旦创建项目,您将看到 Xcode 开发环境以及模板为您创建的项目文件。如果您愿意,可以构建并运行应用程序。iOS 模拟器将启动并显示一个空白白色屏幕。
它是如何工作的...
现在我们已经创建了我们的HelloWorld项目,让我们花点时间看看模板向导为我们创建了什么。
当 Xcode 创建新的 iOS 项目时,它也会创建一系列文件。根据在此过程中选择哪些选项,可能会创建一些额外的文件。
以下截图显示了构成 iOS 项目一部分的基本文件列表:

以下是需要注意的重要文件:
- 
main.m
- 
AppDelegate.h和AppDelegate.m
- 
ViewController.h和ViewController.m
- 
HelloWorld-info.plist
main.m
主函数是程序运行时生命周期开始和结束的地方。UIApplicationMainstarts 函数运行循环,负责通过 AppDelegate 类向应用程序发送通知,并包含可以覆盖的各种事件处理器。此函数接受四个参数,并使用它们来初始化应用程序。
//  main.m
//  HelloWorld
//
//  Created by Steven F Daniel on 18/12/11.
//  Copyright (c) 2012GenieSoft Studios. All rights reserved.
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
  @autoreleasepool {
  returnUIApplicationMain(argc, argv, nil, 
NSStringFromClass([AppDelegate class]));
  }
}
还有更多...
你会注意到在我们的主函数中,它包含了 @autoreleasepool,这是一个支持你的 iOS 设备内存管理系统的对象。接下来,我们包括了 argc 和 argv[] 参数。鉴于所有 iOS 应用都在图形界面中运行,而不是从命令行运行,这些参数只是简单地在这里,以便它们符合标准的 ANSI C 编码实践。
理解应用程序代理
应用程序代理实现了你的程序在应用程序生命周期中的关键点应该如何反应。代理负责在应用程序启动时初始化窗口,并在程序终止时进行清理。此类负责使主视图控制器显示,以及处理应用程序在挂起或恢复时的响应能力。
理解 ViewController 类
此文件实现了视图功能,包含与正在加载的视图相对应的类方法,以及可以覆盖的方法声明。在下面的表中,我们描述了此类中包含的一些方法:
| 方法名称 | 描述 | 
|---|---|
| viewDidLoad | 此方法在视图控制器被加载时被调用,用于设置任何额外的对象。 | 
| viewDidUnload | 此方法在视图从内存中卸载时被调用。 | 
| viewWillAppear | 此方法在视图准备好在设备屏幕上出现,或者已经完全出现时被调用。 | 
| viewDidAppear | 此方法在你想在视图完全出现在屏幕上后执行特定操作时被调用,例如执行某种过渡动画或声音。 | 
| shouldAutorotateToInterfaceOrientation | 此方法通常在你想让你的应用程序支持多种屏幕方向时使用,即横屏或竖屏。 | 
理解 ViewController.xib
XIB 文件是否可见取决于你在创建项目时是否选择了使用故事板。XIB 文件基本上是一个具有特定结构的 XML 文件,可以从 Interface Builder 中读取,并包含有关用户界面的各种信息,例如它包含的控件类型、它们的属性、出口等。
理解 HelloWorld-info.plist
此文件基本上是应用程序的设置文件,包含定义 iOS 应用程序各种设置的属性及其值。这些设置包含有关它将支持的方向、应用程序图标、支持的 iOS 版本以及应用程序可以安装的设备的信息。以下截图显示了在 Xcode 编辑器中双击此文件时的文件结构:

相关内容
- 
构建我们应用程序的用户界面配方 
- 
创建到界面构建器对象的出口配方 
- 
第二章中的添加和自定义视图配方,用户界面 – 创建 UI 
使用界面构建器创建用户界面
在这个配方中,我们将熟悉界面构建器应用程序。界面构建器是一个可视化工具,它使您能够为 iOS 应用程序设计用户界面。
使用界面构建器,您可以从库面板中将视图和对象拖放到画布区域。然后,可以使用出口和动作将它们连接起来,以便它们可以与代码进行程序性交互。
如何操作…
要在界面构建器和 Xcode 环境中显示我们的视图控制器,请执行以下简单步骤:
- 
从项目导航窗口中选择 ViewController.xib文件。
- 
从 Xcode 工具栏中选择查看选项,如图下截图所示: ![如何操作…]() 
在前一个截图中,这显示了从项目导航窗口中选择 XIB 文件时界面构建器的样子。
工作原理…
每次您使用界面构建器设计用户界面时,从库面板中使用的任何对象都将连接到它们所属的 Xcode 项目。
如前一个截图所示,界面构建器工作区分为三个主要区域。以下表格简要说明了哪个区域用于哪些功能:
| 区域名称 | 描述 | 
|---|---|
| 导航区域 | 这个区域显示与项目相关的所有文件。 | 
| 编辑区域 | 这个区域是我们开始设计用户界面的地方。 | 
| 检查器面板 | 这个区域是我们配置每个对象的地方。 | 
| 库面板 | 这个区域是我们可以定位对象并将它们拖放到视图上的地方。这些对象包括 UILabel、UIButton、UITextField等。 | 
更多内容…
您可能已经注意到检查器面板窗口中的模拟度量部分,位于属性选项卡内。这个区域显示了我们的界面在设计师中的样子,以及最终在 iOS 设备上部署和运行时的样子。
在这里,您可以指定您的界面是否具有状态栏、工具栏,甚至导航栏。值得一提的是,如果您将状态栏选项设置为无,并不意味着我们的应用程序将没有状态栏启动。
注意
状态栏是出现在设备屏幕顶部的栏,向用户显示某些类型的信息,例如当前时间、电池状态、运营商名称等等。
参见
- 
构建我们应用程序的用户界面 菜谱 
- 
创建到 Interface Builder 对象的出口 菜谱 
- 
在第二章的添加和自定义视图菜谱中,用户界面 – 创建 UI 
构建我们应用程序的用户界面
在这个菜谱中,我们将学习如何构建我们的用户界面以及如何添加控件。
准备工作
现在我们已经成功创建了项目,我们可以开始使用 Interface Builder 设计器构建我们的用户界面。
如何操作...
使用 Interface Builder 创建我们应用程序的用户界面非常简单,可以通过执行以下简单步骤实现:
- 
从项目导航器窗口中选择ViewController.xib。 
- 
从对象库中拖动一个( Label)控件到视图的画布上。
- 
然后,从属性检查器部分,修改文本属性为名字:。 
- 
接下来,调整 Label控件的尺寸,使其内容适应。
- 
然后,从对象库中拖动一个( Textfield)控件到视图中,并将其放置在名字标签的右侧,并调整Textfield控件的尺寸,使其足够宽,以适应字段内容。
- 
从对象库中拖动一个( Label)控件到视图的画布上。
- 
从属性检查器部分,修改文本属性为姓氏:。 
- 
接下来,调整Label控件的尺寸,使其内容适应。 
- 
然后,从对象库中拖动一个( Textfield)控件到视图中,并将其放置在姓氏标签的右侧,并调整Textfield控件的尺寸,使其足够宽,以适应字段内容。
- 
从对象库中拖动一个( Label)控件到视图的画布上,并将其放置在姓氏标签下方稍远的位置。
- 
从属性检查器部分,修改文本属性为输出:。 
- 
接下来,调整 Label控件的尺寸,使其大约与屏幕宽度相同。
- 
然后,从对象库中拖动一个( Button)控件到视图中,并将其放置在Label控件下方并居中。
- 
调整 Button控件的尺寸,使其足够宽,以适应字段内容。
- 
从属性检查器部分,修改标题属性为点击此处。 
- 
添加按钮后,通过选择菜单栏中的文件 | 保存来保存文档。主视图现在应该看起来像以下截图: ![如何操作…]() 
它是如何工作的…
如您所见,一旦习惯了,使用 Interface Builder 并不困难。每次您将对象拖放到视图中,您都会注意到光标上出现一个小绿色圆形十字,表示可以安全地将对象拖放到视图中。
从此视图,我们还可以调整控件的大小,以及修改与控件相关的属性。
参见
- 
构建我们应用程序的用户界面配方 
- 
创建与 Interface Builder 对象的出口配方 
- 
创建响应用户操作的响应动作配方 
创建与 Interface Builder 对象的出口
在本配方中,我们将更详细地研究出口以及如何使用它们与我们的 UI 进行通信。
准备工作
在我们前面的部分中,我们探讨了如何向我们的ViewController添加控件以形成用户界面的构建,以及为控件设置一些属性。在本节中,我们将探讨如何连接这些控件并在代码中访问这些控件。
如何操作…
创建与 Interface Builder 对象的出口是一个简单的过程,可以通过执行以下简单步骤来实现:
- 
通过选择导航 | 在辅助编辑器中打开或按option + command + ,来打开辅助编辑器。 
- 
确保在辅助编辑器窗口中显示 ViewController.h接口文件。
- 
接下来,选择姓名( Label)控件,然后按住command键,将其拖入ViewController.h接口文件中,在括号和@interface和@end行之间。
- 
从连接下拉菜单中选择出口以创建连接类型。 
- 
输入 lblFirstname作为要创建的出口属性的名称。
- 
从存储下拉菜单中选择强,然后点击连接按钮。 
- 
接下来,选择姓名( Textfield)控件,然后按住command键,将其拖入ViewController.h接口文件中。
- 
从连接下拉菜单中选择出口以创建连接类型。 
- 
输入 txtFirstname作为要创建的出口属性的名称。
- 
从存储下拉菜单中选择强,然后点击连接按钮。 
- 
重复步骤 3 到 9 以添加姓氏、标签和点击此处按钮,并为每个提供如 lblSurname、txtSurname、lblOutput和btnTapHere等名称。注意每次创建出口时,都需要在花括号 {}内创建这些。Interface Builder 设计器不会自动为您创建这些,因此您需要在创建出口之前添加这些。![如何操作…]() 
- 
一旦创建了必要的出口,最好通过选择文件 | 保存来保存您的项目,或者也可以按Command + S。 
工作原理…
每当我们使用出口时,这些只是提供了一种方式,允许我们的界面构建器对象与代码进行通信。这是必要的,也是我们能够访问在界面构建器设计环境中创建的用户界面对象的唯一方式。
还有更多…
在声明出口时,我们还需要做另一件事,那就是为它们创建必要的属性。创建这些属性为我们提供了对这些控件访问的权限,并自动为我们的对象创建 getter 和 setter 方法。
创建接口构建器对象的属性与添加出口的过程类似,可以通过执行以下简单步骤实现:
- 
确保在辅助编辑器窗口中显示 ViewController.h接口文件。
- 
接下来,选择名字( Label)控件,然后按住command键,将其拖动到ViewController.h接口文件末尾的闭合花括号处。
- 
从连接下拉菜单中选择要创建的连接类型出口。 
- 
输入 lblFirstname作为要创建的出口属性名称。
- 
从存储下拉菜单中选择强,然后点击连接按钮。 
- 
接下来,选择名字( Textfield)控件,然后按住command键,将其拖动到ViewController.h接口文件中。
- 
从连接下拉菜单中选择要创建的连接类型出口。 
- 
输入 txtFirstname作为要创建的出口属性名称。
- 
从存储下拉菜单中选择强,然后点击连接按钮。 
- 
重复步骤 3 到 9 以添加姓氏、标签和点击此处按钮,并为每个提供如 lblSurname、txtSurname、lblOutput和btnTapHere等名称。![还有更多…]() 
一旦创建了每个属性出口,完成的ViewController.h接口文件将类似于以下代码片段:
//  ViewController.h
//  HelloWorld
//
//  Created by Steven F Daniel on 22/08/12.
//  Copyright (c) 2012 GenieSoft Studios. All rights reserved.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
    IBOutlet UILabel        *lblFirstname;
    IBOutlet UITextField  *txtFirstname;
    IBOutlet UILabel        *lblSurname;
    IBOutlet UITextField  *txtSurname;
    IBOutlet UILabel        *lblOutput;
    IBOutlet UIButton      *btnTapHere;
}
@property (strong, nonatomic) IBOutlet UILabel *lblFirstname;
@property (strong, nonatomic) IBOutlet UITextField *txtFirstname;
@property (strong, nonatomic) IBOutlet UILabel *lblSurname;
@property (strong, nonatomic) IBOutlet UITextField *txtSurname;
@property (strong, nonatomic) IBOutlet UILabel *lblOutput;
@property (strong, nonatomic) IBOutlet UIButton *btnTapHere;
@end
由于我们创建了出口,您会注意到界面构建器也为我们创建了每个出口,并在我们的ViewController.m实现文件中声明了这些,如下面的代码片段所示:
//  ViewController.m
//  HelloWorld
//
//  Created by Steven F Daniel on 22/08/12.
//  Copyright (c) 2012 GenieSoft Studios. All rights reserved.
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
@synthesize lblFirstname;
@synthesize txtFirstname;
@synthesize lblSurname;
@synthesize txtSurname;
@synthesize lblOutput;
@synthesize btnTapHere;
参见
- 
使用界面构建器创建用户界面菜谱 
- 
构建我们应用程序的用户界面菜谱 
- 
创建响应用户动作的动作菜谱 
创建响应用户动作的动作
在这个菜谱中,我们将更详细地了解动作以及如何使用它们来响应用户操作。
准备工作
在我们之前的章节中,我们探讨了如何向我们的ViewController添加控件以构建用户界面,连接每个出口,以及为每个控件创建属性。在本节中,我们将探讨如何与这些出口通信,并在点击点击此处按钮时向用户显示消息。
如何做到这一点…
向界面对象添加动作的过程类似于添加出口,可以通过执行以下简单步骤来实现:
- 
通过选择导航 | 在辅助编辑器中打开或按option + command + ,来打开辅助编辑器。 
- 
确保在辅助编辑器窗口中显示 ViewController.h接口文件。
- 
接下来,选择点击此处(按钮)控件,然后按住command键,将其拖动到 ViewController.h接口文件中。
- 
从连接下拉菜单中选择动作以创建连接类型。 
- 
为要创建的动作输入 btnTapHere作为名称,并单击连接按钮。![如何操作…]() 
它是如何工作的…
每当在 Objective-C 中创建动作时,它们负责响应和执行其背后的相关代码。我们决定不将事件处理程序连接到按钮的TouchUpInside事件,而是将其添加到动作中,并自行处理输出。这类动作被称为实例方法。
更多内容…
我们下一步是向btnTapHere事件方法中添加代码,当此按钮被点击时,将向用户显示问候消息。执行以下步骤来完成此操作:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,找到 btnTapHere事件方法,并输入以下代码片段:- (IBAction)btnTapHere:(id)sender { NSString *greeting = [NSStringstringWithFormat:@"Welcome To Xcode 4 Cookbook series %@ %@",txtFirstname.text, txtSurname.text]; lblOutput.text = greeting; lblOutput.font = [UIFont boldSystemFontOfSize:21]; lblOutput.textColor = [UIColorblueColor]; }
在我们的代码片段中,我们创建了一个名为 greeting 的NSString对象值,然后使用stringWithFormat方法将我们的firstname和surname对象连接起来。接下来,我们将这个变量分配给输出标签,并应用字体大小和文本颜色。
参见
- 
编译您的项目配方 
- 
使用 Xcode 调试 iOS 应用程序的配方 
- 
使用 iOS 模拟器测试您的应用程序的配方 
编译您的项目
在本配方中,我们将查看如何使用 Xcode 编译我们的项目。
准备工作
Xcode 为编译您的应用程序提供了各种选项。在本节中,我们将查看这些选项。
如何操作...
每当您想要编译应用程序时,您可以选择让应用程序在 iOS 设备上运行,或者在每个 iPad 或 iPhone 的模拟器中运行。模拟器版本号取决于您在计算机上安装的 iOS SDK 版本。在 Xcode 4 中,每次您打开现有的 Xcode 项目或创建一个新的项目时,Xcode 都会自动为您创建一个默认方案。
此方案允许您在 iOS 模拟器中测试您的应用程序,或者将其部署到 iOS 设备。还可以创建其他方案,我们很快就会查看。

方案不是 Xcode 4 的新功能,它们自 Xcode 3 的早期版本以来就存在了。在 Xcode 的早期版本中,当设置活动目标、构建配置或甚至可执行文件时,您必须单独配置每个项目。
由于所有这些都相互关联,这引发了许多问题。这就是定义自己的方案的重要性所在。
方案可以被视为独立的配置,这意味着您可以为指定要构建的目标、要使用的配置构建以及当启动指定的产品时要使用的可执行环境创建方案(这可能是在针对特定的 iOS 版本,或者您想在 iOS 模拟器中运行应用程序时)。请执行以下步骤:
- 
要选择方案,您只需从方案弹出菜单中选择,如前一张截图所示。HelloWorld是我们创建项目时自动为我们创建的默认方案。 
- 
为了创建一个新的方案,请选择新建方案…选项。或者,如果您想编辑活动方案,可以选择编辑方案…选项。这些选项也位于产品菜单下。 每个方案都可以设置为执行特定任务。例如,您可能有一个用于设计构建的方案和一个用于处理发布或分发的方案。有各种类型的构建选项可用于构建、测试、运行、分析(使用仪器)和存档您的产品,然后可以提交到 App Store。您可以定义的方案数量没有限制。然而,一次只能有一个方案处于活动状态。 
- 
您还可以通过选择管理方案…选项或类似地从产品菜单来管理方案。 
您可以指定方案是否应按项目存储,如果是这样,这些方案将提供给包含该项目的每个工作区,或者您可以选择将其存储在当前工作区环境中。
以下截图显示了如何自定义活动方案。您可以指定要使用的构建配置类型、调试器类型以及要使用的当前工作目录。您还可以选择以更高的分辨率运行您的产品,这使您能够模拟应用程序在不同显示分辨率下运行。

使用 iOS 模拟器测试您的应用程序
在本食谱中,我们将探讨如何使用 iOS 模拟器运行我们的HelloWorld应用程序。
准备工作
现在,让我们使用 iOS 模拟器运行我们的应用程序。确保项目窗口已打开,并且您已选择活动方案配置为HelloWorld | iPhone Simulator。
如何操作…
接下来,通过从产品菜单中选择运行或按command + R来构建和运行应用程序。
编译完成后,iOS 模拟器将自动出现,我们刚刚创建的应用程序将显示出来。填写文本字段,然后点击点击此处按钮查看结果。

它是如何工作的…
iOS 模拟器是一个出色的工具,它允许开发者无需实际设备即可测试他们的应用程序。每次您在 Xcode IDE 中使用 iOS 模拟器构建和运行应用程序时,Xcode 都会自动为您在 iOS 模拟器中安装应用程序。
iOS 模拟器的另一个优点是它能够模拟不同的 iOS 版本,如果您的应用程序需要安装在不同的 iOS 平台上,以及测试和调试在不同 iOS 版本下运行时应用程序中报告的错误,这将非常有用。
值得注意的是,在 iOS 模拟器上进行测试仅适用于第一轮测试,因为它甚至没有运行 iOS 代码,并且比实际设备更宽容(例如,通常文件名不区分大小写(遵循 Mac 文件系统规则)),您可以在任何地方写入文件。在模拟器上的测试永远不能替代在设备上的测试。
最后,您只是在运行一个在 Intel 处理器上运行的 Mac OS X 应用程序中的应用程序。它与在设备上运行 iOS 相去甚远。它对于在开发过程中捕获明显错误或进行快速测试很有用,但您仍然需要进行设备测试。
参见
- 
关于编译您的项目的配方 
- 
关于使用 Xcode 调试 iOS 应用程序的配方 
- 
在第十章中,关于注册 iOS 设备进行测试的配方,打包和部署您的应用程序 
- 
在第十章中,关于创建开发配置文件的配方,打包和部署您的应用程序 
- 
在第十章中,关于使用配置文件在 iOS 设备上安装应用程序的配方,打包和部署您的应用程序 
配置和使用编译器指令
在 C/C++中,我们使用指令来包含应用程序需要访问的任何其他头文件。这是通过使用#include指令来完成的。在 Objective-C 中,我们使用#import指令。如果您检查ViewController.h文件的内容,您会注意到文件顶部有一个#import语句。
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
    IBOutlet UILabel     *lblFirstname;
    IBOutlet UITextField *txtFirstname;
    IBOutlet UILabel     *lblSurname;
    IBOutlet UITextField *txtSurname;
    IBOutlet UILabel     *lblOutput;
    IBOutlet UIButton    *btnTapHere;
}
#import语句被称为预处理器指令。如前所述,在 C/C++中,您会使用#include预处理器指令将文件内容包含在当前源文件中。在 Objective-C 中,您会使用#import指令语句来实现相同的结果,但编译器确保文件只包含一次。
要从 Xcode 框架库中导入头文件,您需要在#import语句中使用尖括号(<>)指定头文件名。
如果您想导入包含您自己的方法的自定义构建的头文件,您可以使用双引号("")进行指定和使用,如下面的示例代码所示:
#import <UIKit/UIKit.h>
#import "MyClass.h" 
@interface ViewController : UIViewController {
}
在这个菜谱中,我们将探讨另一种方法,我们可以使用指令根据您自己定义的条件预处理器标志有条件地编译代码。这使得您能够选择性地启用应用程序的部分,而不会使您的代码看起来杂乱。
准备工作
在HelloWorld-Prefix.pch中,我们将首先声明一些宏(这样您就不需要在代码中添加#ifdef/#endif块)。HelloWorld-Prefix.pch文件具有全局作用域,因此您在那里定义的任何函数都将可用于所有类,这非常有用。
如何操作...
为您的应用程序声明编译器指令是一个非常简单的过程,可以通过执行以下简单步骤实现:
- 
从项目导航器中打开位于支持文件组下的 HelloWorld-Prefix.pch实现文件。
- 
接下来,将以下高亮代码片段添加到该文件中: // // Prefix header for all source files of the 'HelloWorld' target in the 'HelloWorld' project // #import <Availability.h> #ifndef __IPHONE_4_0 #warning "This project uses features only available in iOS SDK 4.0 and later." #endif #ifdef __OBJC__ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #endif #ifdef DISPLAY_FIRSTNAME #warning "This application will display the value of the First namefield." #else #warning "This application will display the values of both the First name and Surname fields." #endif
- 
接下来,我们需要修改我们的 btnTapHere方法,使其被#ifdef语句包围,在ViewController.m实现文件中,如下所示:- (IBAction)btnTapHere:(id)sender { #ifdef DISPLAY_FIRSTNAME NSLog(@"Using the Firstname field."); NSString *greeting = [NSString stringWithFormat:@"Welcome to Xcode 4 Cookbook series %@",txtFirstname.text]; #else NSLog(@"Using Firstname and Surname fields."); NSString *greeting = [NSString stringWithFormat:@"Welcome to Xcode 4 Cookbook series %@ %@",txtFirstname.text, txtSurname.text]; #endif lblOutput.text = greeting; lblOutput.font = [UIFont boldSystemFontOfSize:21]; lblOutput.textColor = [UIColorblueColor]; }
每次您只想显示用户的第一个名字时,您只需通过定义条件标志将其设置为YES。
它是如何工作的…
在 Objective-C 中使用编译器指令时,它们负责响应并执行#ifdef和#endif标签封装的关联代码片段。
如果您只想在测试期间强制使用一组NSLog消息或值,而不在应用程序的最终发布版本中显示,这尤其方便。这些类型可以使用的其他一些场景是,如果您正在创建一个游戏,并且希望在游戏的试用版或轻量版中禁用一些功能。
还有更多…
为了让您的应用程序使用此编译器指令,我们需要将此预处理器标志添加到预处理器宏部分,如下所示:
- 
从项目导航器窗口中选择HelloWorld项目。 
- 
接下来,选择HelloWorld项目,然后单击构建设置选项卡。 
- 
然后,滚动到Apple LLVM 编译器 4.0 – 预处理部分。 
- 
双击调试部分,然后添加 DISPLAY_FIRSTNAME=YES预处理器标志。
- 
通过在项目窗口外部单击来关闭此窗口。 ![还有更多…]() 这就是全部内容,很简单。如果您想关闭 DISPLAY_FIRSTNAME(例如,当您准备进行最终发布构建时),只需回到这个部分并从项目首选项中移除该标志。
- 
接下来,通过从 Product 菜单中选择 Run 或通过按 command + R 并输入 Firstname 和 Surname 的值来构建并运行应用程序。您应该看到一个只显示您名字的首字母的欢迎信息。 
参见
- 
编译您的项目 的配方 
- 
使用 Xcode 调试您的 iOS 应用程序 的配方 
- 
使用 iOS 模拟器测试您的应用程序 的配方 
使用 Xcode 调试您的 iOS 应用程序
在本配方中,我们将展示如何在 Xcode 和 iOS 模拟器中轻松调试应用程序。
准备工作
Xcode 4 为您提供了一个调试器,用于使用 iOS 模拟器或实际 iOS 设备调试您的应用程序。在本配方中,我们将看到调试 HelloWorld 应用程序是多么容易。
如何做到这一点…
使用 Xcode 开发环境调试您的应用程序是一个简单的过程,它允许您监控应用程序变量的更改,以及逐行执行源代码,可以通过执行以下简单步骤实现:
- 
从项目导航器中打开 ViewController.m实现文件。
- 
接下来,定位到 btnTapHere事件方法,并在如下截图所示的灰色区域中设置一个断点。
- 
您会注意到在您放置标记的行上出现了一个箭头。 ![如何做到这一点…]() 
- 
接下来,通过从 Product 菜单中选择 Run 或通过按 command + R 来构建并运行应用程序。 
- 
填充 Firstname 和 Surname 字段,并点击 Tap Here 按钮。 
- 
您会注意到我们的应用程序执行已暂停,调试器已停止在我们设置断点的行。 
- 
将鼠标悬停在断点行中的 greeting变量上,以查看 greeting 标签的内容。![如何做到这一点…]() 
- 
要继续应用程序的执行,请点击 继续程序执行 按钮。 
它是如何工作的…
虽然您可以使用调试器在任何时候暂停程序的执行并查看程序变量的状态,但在运行应用程序之前设置断点会更好。
断点基本上是一条指令,告诉应用程序在达到断点时 停止。在此阶段,您的代码执行暂停,等待进一步的指令以确定下一步操作。在此阶段,您有机会检查任何属性的当前值,或者逐行执行代码。
参见
- 如果您想了解更多关于 Xcode 调试功能的信息,您可以参考位于developer.apple.com/library/ios/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/060-Debug_Your_App/debug_app.html#//apple_ref/doc/uid/TP40010215-CH3-SW1.的 Apple 开发者文档。
使用 Clang 静态分析器检查您的代码
在本节中,我们将探讨如何使用 Clang 静态分析器工具检查我们的代码语法中的错误。
准备工作
有时候您可能想检查代码语法中的错误。这就是静态分析器工具派上用场的地方。这个工具最初是在 Xcode 3.x 中引入的,并在新窗口中打开并显示构建结果。
Xcode 4 允许您在 Xcode 4 工作区环境中执行代码分析、检查结果以及将修复应用到源文件中。
如何操作…
要运行静态分析器,请执行以下简单步骤:
- 
从项目导航器窗口中选择HelloWorld项目。 
- 
打开 ViewController.m实现文件。
- 
定位到 btnTapHere方法,并输入以下修改后的代码片段:- (IBAction)btnTapHere:(id)sender { UIColor *color; int colorIndex; #ifdef DISPLAY_FIRSTNAME NSLog(@"Using the Firstname field."); NSString *greeting = [NSString stringWithFormat: @"Welcome to Xcode 4 Cookbook series %@", txtFirstname.text]; #else NSLog(@"Using Firstname and Surname fields."); NSString *greeting = [NSString stringWithFormat: @"Welcome to Xcode 4 Cookbook series %@ %@", txtFirstname.text, txtSurname.text]; #endif if (colorIndex == 1) { lblOutput.textColor = [UIColor redColor]; } else if (colorIndex == 2) { lblOutput.textColor = [UIColor blueColor]; } else if (colorIndex == 2) { lblOutput.textColor = [UIColor purpleColor]; } else { lblOutput.textColor = [UIColor blueColor]; } lblOutput.text = greeting; lblOutput.font = [UIFont boldSystemFontOfSize:21]; }
- 
从产品菜单中选择分析,或者按住Shift + command + B键组合。 
它是如何工作的…
当分析器完成对您的代码进行问题检查后,问题导航器会自动打开,并显示在您的项目中找到的问题列表。这可以在以下屏幕截图中看到:

在左侧窗格中单击问题将打开相关文件,并显示用蓝色三角形标记的问题。单击此三角形将显示静态分析器识别和检测到的错误逻辑流程,如前一个屏幕截图所示。
静态分析器已标记colorIndex变量存在潜在错误。这是由于变量在声明时未初始化,并包含一些随机值。
您还会注意到,当您点击消息气泡时,分析器会提供更多详细信息,并显示控制流,如箭头所示。这为您提供了完整的错误诊断。静态分析器工具报告的许多问题都有这些信息,使得分析和修复这些错误变得容易得多。
注意
如果您想了解更多关于静态分析器的信息,您可以参考位于developer.apple.com/library/ios/#recipes/xcode_help-source_editor/Analyze/Analyze.html的 Apple 开发者文档。
参见
- 
编译您的项目食谱 
- 
使用 Xcode 调试您的 iOS 应用的配方 
- 
使用 iOS 模拟器测试您的应用的配方 
- 
注册您的 iOS 设备以进行测试的配方在第十章,打包和部署您的应用 
- 
创建开发配置文件的配方在第十章,打包和部署您的应用 
- 
使用配置文件在 iOS 设备上安装应用的配方在第十章,打包和部署您的应用 
第二章:用户界面 – 创建 UI
在本章中,我们将涵盖:
- 
添加和自定义视图 
- 
使用标签显示文本 
- 
通过按钮获取用户输入 
- 
在视图中显示图像 
- 
显示和编辑文本 
- 
使用 iOS 设备键盘 
- 
向用户显示进度 
- 
将工具栏对象添加到视图中 
- 
视图淡入淡出 
- 
创建自定义表格视图控制器 
- 
将表格视图添加到视图控制器 
- 
处理不同的 iOS 设备 
简介
应用程序的用户界面由视图和其他元素组成,当用户加载您的应用程序时,这是用户首先看到的,并为他们提供了一个与您的应用程序进行通信的简单方式。
用户界面由画布组成,充当用户可以与之通信的控件占位符。在大多数实际应用中,仅视图本身是不够的。Apple 为您提供了一个名为UIViewController的类,该类负责管理视图。
视图控制器可以响应设备通知,例如确定设备何时旋转,或者提供不同的方式来显示和关闭多个视图或甚至其他视图控制器。
我们还将了解如何使用一些最常用的视图控制器来创建我们自己的自定义类。在本章中,我们将探讨如何添加和自定义视图,以及将这些视图应用于淡入淡出效果。我们还将查看一些设备特定的方面,以确定其当前的方向。
添加和自定义视图
在本食谱中,我们将探讨如何使用 Interface Builder 将新的UIView对象添加到现有视图中,并查看如何自定义它。
准备工作
我们将首先使用 Xcode 创建一个新项目,然后使用 Interface Builder 帮助我们添加一个新的UIView对象,让我们开始吧。
如何做...
要开始创建新的 Xcode 项目,请执行以下简单步骤:
- 
从 /Developer/Applications文件夹中启动 Xcode。
- 
选择创建新的 Xcode 项目,或点击文件 | 新建项目。 
- 
从可用模板列表中选择单视图应用程序。 
- 
点击下一步按钮以继续向导的下一步。 
- 
接下来,将项目名称输入为 ViewObjectsExample。
- 
从设备下拉菜单中选择iPhone。 
- 
确保未勾选使用故事板复选框。 
- 
确保已勾选使用自动引用计数复选框。 
- 
确保未勾选包含单元测试复选框。 
- 
点击下一步按钮以继续向导的下一步。 
- 
指定您希望保存项目的位置。 
- 
然后,点击创建按钮以在指定位置保存您的项目。 
一旦创建项目,您将看到 Xcode 开发环境以及模板为您创建的项目文件。
接下来,我们需要开始构建我们的用户界面,这可以通过执行以下简单步骤实现:
- 
从项目导航窗口中选择 ViewController.xib文件。
- 
从对象库中,将一个视图对象拖放到主视图中。 
- 
接下来,为这个视图创建必要的出口和属性,并将其命名为 subView。
- 
接下来,选择我们刚刚添加的新视图,并从大小检查器选项卡中,将Y属性设置为44点,宽度属性设置为320点,高度属性设置为480点。 
- 
接下来,通过选择文件 | 保存,或通过按command + S来保存您的项目。 
- 
然后,通过从产品菜单中选择产品 | 运行来构建和运行应用程序,或通过按command + R。 
当编译完成后,iOS 模拟器将显示我们刚刚创建并显示的应用程序,其中包含一个白色背景的视图。
它是如何工作的...
我们刚刚创建了一个包含子视图的应用程序,它不提供任何功能。在 iOS 应用程序中,视图是构成应用程序用户界面的重要组件之一,它继承自UIView类层次结构。
值得注意的是,使用 Interface Builder 添加视图与通过代码动态创建视图相比会发生什么,我们很快就会看到。使用 Interface Builder 添加的视图在运行时实例化,并使用在大小检查器窗口中手动设置的值设置Frame属性。Frame属性的类型是CGRect,它定义了视图在主窗口中的位置,以及其大小(以点为单位)。
还有更多...
UIView类继承自负责响应和处理事件的UIResponder类。当一个视图被添加到现有视图时,它成为其响应链的一部分。UIView类公开了UIResponder类的属性和相关方法。
注意
如果您想了解更多关于UIResponder类的信息,可以参考位于developer.apple.com/library/ios/#documentation/UIKit/Reference/UIResponder_Class/Reference/Reference.html#//apple_ref/occ/cl/UIResponder的 Apple 开发者文档。
视图也可以通过使用UIView类的addSubView方法通过代码程序化地添加。以下代码行显示了如何实现这一点:
[self.view addSubview:self.subView];
如前一行代码片段所示,addSubview 方法将添加的视图的 Superview 对象设置为指向调用者。视图只有在使用 addSubview 方法添加到主父视图后才会显示。将相同的视图添加到另一个视图会导致 Superview 对象更改并指向新的视图的父窗口。这是因为视图在任何给定时间只能存在于一个 Superview 对象中。
注意
值得注意的是,当使用 Interface Builder 手动添加视图时,您不需要使用 addSubview: 方法来显示子视图,这取决于您是否已经将其添加到您想要放置的父视图的子视图中。
当通过代码程序化添加视图时,它们也可以通过代码删除。这可以通过在视图中调用removeFromSuperview方法来实现。以下代码行显示了如何实现这一点:
[self.subView removeFromSuperview];
参见
- 
使用标签显示文本的配方 
- 
在第一章的构建我们应用程序的用户界面配方中,第一章,获取和安装 iOS SDK 开发工具 
- 
在第一章的创建到 Interface Builder 对象的出口配方中,获取和安装 iOS SDK 开发工具 
使用标签显示文本
在这个配方中,我们将探讨我们如何使用标签向用户显示信息性文本。
准备工作
UILabel对象是我们让用户知道正在发生什么的一种方式。这可以是在我们请求用户输入他的/她的用户名或密码时,或者如果出现问题,通知他/她。
如何操作……
为了看到这是如何实现的,我们需要修改我们在上一个配方中创建的ViewObjectsExample应用程序。执行以下步骤:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中,将一个标签对象拖放到子视图中。 
- 
调整 Label控件的大小,使其内容填充视图的宽度。
- 
接下来,为这个 Label控件创建出口和属性,并将其命名为lblInfo。
- 
我们接下来的步骤是创建负责设置标签属性的代码功能,并使用一些默认值。 
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,创建 updateLabel方法,如下面的代码片段所示:-(void)updateLabel { lblInfo.text = @"Press button to change the background color"; lblInfo.textColor = [UIColor blueColor]; lblInfo.textAlignment = NSTextAlignmentLeft; lblInfo.adjustsFontSizeToFitWidth = TRUE; lblInfo.font = [UIFont fontWithName:@"Arial-Bold" size:17]; }
- 
然后,在 theviewDidLoad方法中添加以下代码片段,如下所示:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, // typically from a nib. [self updateLabel]; }
- 
然后,通过从产品菜单选择产品 | 运行,或者通过按command + R来构建和运行应用程序。 
它是如何工作的……
我们已经成功地将一个UILabel对象添加到我们的视图中,并添加了一些代码来更新内容。我们首先创建了一个负责自定义标签属性的方法,然后我们将需要显示的文本分配给标签控件的文本属性。
在我们的下一步中,我们使用UIColor类的值将Label控件的textColor属性设置为蓝色,然后我们设置textAlignment属性,以便我们的标签内容将显示为左对齐。
最后,我们将标签的adjustFontSizeToFitWidth属性设置为TRUE,以指示标签自动更改字体大小,以便它能够适应标签的宽度。接下来,我们在viewDidLoad方法中添加对这个方法的调用,以在运行时更新标签内容。
更多内容…
使用标签,你可以设置你想要文本显示的字体;这是通过使用UIFontfontWithName方法设置font属性来实现的。string参数表示要设置的字体名称和样式,而size属性参数确定字体大小。如果你看看以下代码行,你可以看到如何将标签的字体设置为Arial-Bold和大小为17点,如下所示:
 [UIFont fontWithName:@"Arial-Bold" size:17];
如果输入的字体名称未找到,fontWithName方法返回 nil,这将导致异常,如果标签的font属性设置为 nil。
注意
如果你想了解更多关于UIFont类的信息,你可以参考位于developer.apple.com/library/IOs/#documentation/UIKit/Reference/UIFont_Class/Reference/Reference.html的 Apple 开发者文档。
参见
- 
显示和编辑文本的配方 
- 
使用按钮获取用户输入的配方 
- 
在第一章的构建应用程序的用户界面配方中,获取和安装 iOS SDK 开发工具 
- 
在第一章的创建到 Interface Builder 对象的出口配方中,获取和安装 iOS SDK 开发工具 
使用按钮获取用户输入
在这个配方中,我们将看看我们如何使用按钮来更改我们在上一个配方中定义的UILabel控件的内容。
准备工作
在这个配方中,我们将首先创建一个UIButton对象,该对象将用于在点击时修改标签的背景颜色。
如何做…
为了了解如何实现这一点,我们需要修改我们在上一个配方中创建的ViewObjectsExample应用程序。执行以下步骤:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航窗口中选择 ViewController.xib文件。
- 
从 对象库 中拖动一个 圆形矩形按钮 对象到子视图中,并将其放置在我们之前添加的标签下方。 
- 
在 属性检查器 部分中,将 标题 属性修改为 点击此处。 
- 
接下来,为这个 圆形矩形按钮 对象创建出口和属性,并将其命名为 btnTapHere。
- 
接下来,为 点击此处 按钮创建动作方法,并将其命名为 btnTapHere。
- 
在添加按钮后,通过从菜单栏选择 文件 | 保存 或通过按 command + S 来保存文档。此时主视图应类似于以下截图: ![如何操作...]() 
我们的下一步是创建负责更改标签背景颜色的代码功能:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,创建 btnTapHere方法,如下面的代码片段所示:- (IBAction)btnTapHere:(UIButton *)sender { [self.lblInfo setBackgroundColor:[UIColor yellowColor]]; }
- 
然后,通过从 产品 菜单选择 产品 | 运行 或通过按 command + R 来构建并运行应用程序。 
它是如何工作的…
我们首先向用户界面添加了一个按钮,当按钮被按下时,它会改变标签的背景内容。然后我们调用 setBackgroundColor 方法将背景颜色应用到标签上。
注意
如果你想了解更多关于 UIButton 类的信息,可以参考位于 developer.apple.com/library/ios/#documentation/uikit/reference/UIButton_Class/UIButton/UIButton.html 的 Apple 开发者文档。
还有更多...
setBackgroundColor 方法提供的功能也可以通过在 Interface Builder 中的 属性检查器 选项卡中设置 背景 字段来实现。
相关内容
- 
添加和自定义视图 的配方 
- 
使用标签显示文本 的配方 
- 
在视图中显示图像 的配方 
- 
第一章 中的 构建应用程序的用户界面 配方,获取和安装 iOS SDK 开发工具 
- 
第一章 中的 创建到 Interface Builder 对象的出口 配方,获取和安装 iOS SDK 开发工具 
在视图中显示图像
在这个配方中,我们将探讨如何使用 UIImageView 类在视图中显示图像。
准备工作
在这个配方中,我们将首先创建一个 UIImageView 对象,它将用作显示图像的容器。在这个例子中,我们将使用名为 Blue-Aqua-Apple.png 的图像文件。
如何操作...
为了了解如何实现这一点,我们需要修改之前配方中创建的 ViewObjectsExample 应用程序。执行以下步骤:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从 对象库 中,将一个 UIImageView 对象拖放到子视图中,并将其放置在我们之前添加的 Round Rect Button对象下方。
- 
调整 UIImageView控件的大小,使其足够大以容纳图片。
- 
接下来,为这个 UIImageView创建出口和属性,并将其命名为imgPicture。
- 
然后,选择 文件 | 将文件添加到 "ViewObjectsExample"…,或者通过按 option + command + A 键。 
- 
选择要添加的 Blue-Aqua-Apple.png 图片文件,然后点击 添加 按钮。 
我们接下来的步骤是创建负责在 imageView 控件中显示图片的代码功能:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,创建 displayImage方法,如下面的代码片段所示:-(void)displayImage { [self.imgPicture setImage:[UIImage imageNamed:@"Blue-Aqua-Apple.png"]]; [self.imgPicture setContentMode:UIViewContentModeScaleAspectFit]; }
- 
然后,在 viewDidLoad方法中添加以下代码行:[self displayImage];
- 
然后,通过从 产品 菜单中选择 产品 | 运行,或者通过按 command + R 键来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现,显示我们刚刚添加到项目中的图片。
它是如何工作的…
UIImageView 类基本上是一个针对显示图片而定制的视图。然后,我们将 imageView 类的 contentMode 属性设置为 UIViewContentModeScaleAspectFit,这将缩放并填充图片以适应整个 UIImageView 的内容。
注意
如果你想了解更多关于 UIImageView 类的信息,可以参考位于 developer.apple.com/library/ios/#documentation/UIKit/Reference/UIImageView_Class/Reference/Reference.html 的 Apple 开发者文档。
contentMode 属性接受一个名为 UIViewContentMode 的枚举类型。以下表格解释了这些类型中的一些:
| 内容模式 | 描述 | 
|---|---|
| ScaleToFill | 这是默认值。它将内容缩放到适合视图的大小,必要时更改宽高比。 | 
| ScaleAspectFit | 这会将内容缩放到适合视图的大小,同时保持其宽高比。视图的剩余区域变为透明。 | 
| ScaleAspectFill | 这会将内容缩放到填充视图的大小,同时保持其宽高比。 | 
更多内容…
UIImage 类是表示图像相关信息的对象。以下表格显示了它目前支持的一些文件格式:
| 文件格式 | 文件扩展名 | 
|---|---|
| 可移植网络图形 | .png | 
| 联合图像专家小组 | .jpg,.jpeg | 
| 标签图像文件格式 | .tiff,.tif | 
| GIF 图像交换格式 | .gif | 
| Windows 位图格式 | .bmp | 
| 窗口图标格式 | .ico | 
参见
- 
添加和自定义视图菜谱 
- 
在第七章的从相册选择图片和视频菜谱中,多媒体资源 
- 
在第七章的使用相机捕获媒体菜谱中,多媒体资源 
显示和编辑文本
在这个菜谱中,我们将学习如何使用文本块方法来确定何时开始和结束编辑。
准备工作
在这个菜谱中,我们将讨论UITextField对象的用法以及我们如何在其中显示可编辑文本。
如何做到这一点…
为了了解如何实现这一点,我们需要修改我们在上一个菜谱中创建的ViewObjectsExample应用程序。执行以下步骤:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中拖动一个TextField对象到子视图中,并将其放置在我们之前添加的 UIButton下方。
- 
调整 TextField控件宽度,使其足够大,可以容纳足够的文本。
- 
接下来,为这个 TextField创建出口和属性,并将其命名为txtTextInput。
我们下一步是创建代码功能,该功能将负责在我们的TextField控件中显示一些文本:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,创建 populateTextBox方法,如下面的代码片段所示:-(void)populateTextBox { self.txtTextInput.text = @"This is some sample text"; self.txtTextInput.returnKeyType = UIReturnKeyDone; self.txtTextInput.delegate = self; }
- 
然后,在 viewDidLoad方法中添加以下行代码:[self populateTextBox];
我们下一步是修改我们的ViewController.h接口文件,以便我们可以访问我们的文本框的方法。这可以通过执行以下简单步骤来实现:
- 
从项目导航器打开 ViewController.h接口文件。
- 
接下来,输入以下代码片段: // ViewController.h // ViewObjectsExample // Created by Steven F Daniel on 15/09/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import<UIKit/UIKit.h> @interface ViewController : UIViewController <UITextFieldDelegate> { IBOutlet UITextField *txtTextInput; } @property (weak, nonatomic) IBOutlet UITextField *txtTextInput;
接下来,我们需要修改我们的ViewController.m实现文件,以包括我们的文本框的方法事件。这可以通过执行以下简单步骤来实现:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,输入以下代码片段: -(BOOL)textFieldShouldReturn:(UITextField *)textField { // Dismisses the keyboard when the "Done" button is clicked [textField resignFirstResponder]; return YES; }-(void) textFieldDidBeginEditing:(UITextField *)textField { lblInfo.text = @"TextField contents are being updated"; [self.lblInfo setBackgroundColor:[UIColor redColor]]; } -(void)textFieldDidEndEditing:(UITextField *)textField { lblInfo.text = @"TextField contents have now been updated."; [self.lblInfo setBackgroundColor:[UIColor greenColor]]; }
- 
然后,通过从产品菜单选择运行或按command + R键来构建和运行应用程序。 
编译完成后,iOS 模拟器将出现并显示带有我们填充的示例文本的文本框,如下面的截图所示:

它是如何工作的…
在以下代码片段中,我们更新了textField控件的文本属性,然后将returnKeyType属性设置为在点击完成按钮时关闭键盘,并设置控件的代理为我们的视图控制器:
-(void)populateTextBox {
  self.txtTextInput.text = @"This is some sample text";
  self.txtTextInput.returnKeyType = UIReturnKeyDone;
  self.txtTextInput.delegate = self;
}
接下来,UITextField 类提供了一个显示可编辑文本的对象。为了我们能够响应文本框的事件,我们需要定义一个名为 UITextBoxDelegate 的协议类,它将作为文本框的代理,这样我们就可以确定文本何时被修改。
@interface ViewController : UIViewController <UITextFieldDelegate>
{
 IBOutlet UITextField *txtTextInput;
}
@property (weak, nonatomic) IBOutlet UITextField *txtTextInput;
在下面的代码片段中,我们声明了我们的 TextField 控件的 textFieldShouldReturn: 方法。此方法处理在按下 完成 按钮时关闭键盘,这是通过在 TextField 控件上调用 resignFirstResponder 方法来实现的,导致控件失去焦点。
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
    // Dismisses the keyboard when the "Done" button is clicked
    [textField resignFirstResponder];
    return YES;
}
接下来,我们声明我们的 TextField 控件的 textFieldDidBeginEditing: 和 textFieldDidEndEditing: 方法。这些方法负责确定文本何时在文本字段中更新,以及何时编辑完成,这通常是在键盘关闭时完成的。
-(void) textFieldDidBeginEditing:(UITextField *)textField
{
   lblInfo.text = @"TextField contents are being updated";
   [self.lblInfo setBackgroundColor:[UIColor redColor]];
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
 lblInfo.text = @"TextField contents have now been updated.";
 [self.lblInfo setBackgroundColor:[UIColor greenColor]];
}
更多内容...
Objective-C 中的代理是一个符合特定协议的特定类型的对象。这意味着它是一个封装了一个或多个方法(以及/或其他成员)的对象,这些方法充当事件处理器。
相关内容
- 使用 iOS 设备键盘 的食谱
使用 iOS 设备键盘
在这个食谱中,我们将学习如何设置一些不同的虚拟键盘样式。
准备工作
在这个食谱中,我们将讨论 UITextBox 对象的用法以及我们如何在其中显示可编辑文本。
如何操作...
为了看到如何实现这一点,我们需要修改我们在上一个食谱中创建的 ViewObjectsExample 应用程序。执行以下步骤:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,修改 populateTextBox方法,如以下代码片段中突出显示的行所示:-(void)populateTextBox { self.txtTextInput.text = @"This is some sample text"; self.txtTextInput.keyboardType = UIKeyboardTypeNumbersAndPunctuation; self.txtTextInput.returnKeyType = UIReturnKeyDone; self.txtTextInput.delegate = self; }
- 
然后,通过从 产品 菜单中选择 产品 | 运行,或者通过按 command + R 来构建并运行应用程序。 
当编译完成后,iOS 模拟器将出现并显示带有我们填充的示例文本的文本框。点击 TextField 控件以显示我们指定的键盘类型。

工作原理…
在以下代码片段中,我们更新了 TextField 控件的 keyboardType 属性,并指定 UIKeyboardTypeNumbersAndPunctuation 作为要使用的键盘类型。
-(void)populateTextBox {
  self.txtTextInput.text = @"This is some sample text";
  self.txtTextInput.keyboardType = 
  UIKeyboardTypeNumbersAndPunctuation;
  self.txtTextInput.returnKeyType = UIReturnKeyDone;
  self.txtTextInput.delegate = self;
}
keyboardType 属性接受一个名为 UIKeyboardType 的枚举类型。以下表格解释了其中的一些类型:
| 键盘类型 | 描述 | 
|---|---|
| UIKeyboardTypeDefault | 当前输入方法的默认键盘 | 
| UIKeyboardTypeASCIICapable | 显示标准 ASCII 字符 | 
| UIKeyboardTypeNumbersAndPunctuation | 显示数字和标点符号键盘 | 
| UIKeyboardTypeURL | 显示针对 URL 输入优化的键盘 | 
| UIKeyboardTypeNumberPad | 显示为 PIN 输入设计的数字键盘 | 
| UIKeyboardTypePhonePad | 显示专为输入电话号码设计的键盘 | 
| UIKeyboardTypeNamePhonePad | 显示专为输入人名或电话号码设计的键盘 | 
| UIKeyboardTypeEmailAddress | 显示专为指定电子邮件地址优化的键盘 | 
| UIKeyboardTypeDecimalPad | 显示带有数字和小数点的键盘 | 
| UIKeyboardTypeTwitter | 显示专为 twitter 文本输入优化的键盘,易于访问 @和#字符 | 
| UIKeyboardTypeAlphabet | 这已被弃用,但使用显示标准 ASCII 字符的键盘 | 
注意
如果您想了解更多关于UIKeyboardType类的信息,可以参考位于developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITextInputTraits_Protocol/Reference/UITextInputTraits.html的 Apple 开发者文档。
相关内容
- 
添加和自定义视图配方 
- 
通过使用按钮获取用户输入配方 
- 
显示和编辑文本配方 
向用户显示进度
在本配方中,我们将学习如何显示任何给定长度的进度。
准备工作
在本配方中,我们将讨论UIProgressBar对象的用法以及如何使用相关属性来显示动画进度条。
如何做到这一点...
为了了解如何实现这一点,我们需要修改之前在配方中创建的ViewObjectsExample应用程序。执行以下步骤:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中拖动一个ProgressView对象到子视图中,并将其放置在之前添加的 UIImageView下方。
- 
调整 ProgressViewcontrol的大小,使其宽度与视图相同。
- 
接下来,为这个 ProgressView创建出口和属性,并将其命名为pgbProgress。
我们下一步是创建负责显示进度和动画条的功能代码:
- 
从项目导航器打开 ViewController.m实现文件。
- 
在以下代码片段中输入变量声明的高亮部分: #import "ViewController.h" @interface ViewController () @end @implementation ViewController @synthesize subView; @synthesize lblInfo; @synthesize pgbProgress; @synthesize imgPicture; @synthesize txtTextInput; float incrementBy = 0.0;
- 
接下来,修改 btnPressMe方法,如下面的代码片段中高亮显示的行所示:- (IBAction)btnTapHere:(UIButton *)sender { [self fillProgressBar]; [self.lblInfo setBackgroundColor:[UIColor yellowColor]]; }
- 
接下来,输入以下代码片段: - (void)fillProgressBar { [self.pgbProgress setProgress:(incrementBy = 0.0f)]; [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(incrementBar:) userInfo:nil repeats:YES]; } -(void)incrementBar:(NSTimer *)timer { incrementBy += 10.0f; [pgbProgress setProgress:(incrementBy / 100)]; if (incrementBy > 100.0) { self.lblInfo.text = @"Processing has been Completed"; [timer invalidate]; } else { self.lblInfo.text = [NSString stringWithFormat:@"Processing data records: %3.2f", (pgbProgress.progress * 100)]; } }
- 
然后,通过从产品菜单选择产品 | 运行,或者通过按command + R来构建并运行应用程序。 
编译完成后,iOS 模拟器将出现。点击点击此处按钮以查看进度条逐渐填充,如下面的代码片段所示:

它是如何工作的…
在以下代码行中,我们声明一个 float 变量 incrementBy。这个变量将由我们的方法用来逐渐增加并填充进度条。
float incrementBy = 0.0;
接下来,我们修改我们的 btnTapHere: 方法,包括对 fillProgressBarmethod 方法的调用,该方法将负责处理,并调用其他方法来逐渐填充条。
- (IBAction)btnTapHere:(UIButton *)sender {
 [self fillProgressBar];
 [self.lblInfo setBackgroundColor:[UIColor yellowColor]];
}
在我们的下一个代码片段中,fillProgressBar 方法将 incrementBy 变量初始化为 0,以确保条不会完全填充。然后我们使用 scheduledTimerWithTimeInterval 方法,这是 NSTimer 类的一个类方法,用于创建计时器。
NSTimer 是一个在运行循环中安排运行的对象,以便它可以定期检查经过的时间。当经过足够的时间后,它就会触发。如果计时器被设置为非重复的,那么它会取消安排自己,否则它会清除其经过的时间并等待再次触发。接下来,我们指定以秒为单位的持续时间来调用 incrementBar: 方法,如 selector 属性所指定,并持续调用此方法,直到计时器被无效化。
- (void)fillProgressBar {
 [self.pgbProgress setProgress:(incrementBy = 0.0f)];
 [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(incrementBar:) userInfo:nil repeats:YES];
}
在我们的最终代码片段中,我们首先将 incrementBar 变量设置为填充进度条值所需的增量步骤数,然后更新 ProgressView 的 progress 属性。接下来,我们检查是否达到了条阈值,然后相应地更新标签,最后使 timer 对象无效,以防止方法被调用。
或者,如果我们还没有达到我们的阈值,我们更新 Label 对象以显示进度条的当前进度。
-(void)incrementBar:(NSTimer *)timer
{
 incrementBy += 10.0f;
        [pgbProgress setProgress:(incrementBy / 100)];
 if (incrementBy > 100.0) {
     self.lblInfo.text = @"Processing has been Completed";
     [timer invalidate];
 }
 else
 {
     self.lblInfo.text = [NSString
stringWithFormat:@"Processing data records: %3.2f", (pgbProgress.progress * 100)];
 }
}
注意
如果你想了解更多关于 NSTimer 类的信息,你可以参考位于 developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html 的苹果开发者文档。
更多...
UIProgressView 类支持一种额外的样式。这可以通过将 progressViewStyle 属性设置为 UIProgressViewStyleBar 来指定。这样修改后,进度条将看起来像苹果邮件应用中可以看到的那样。
相关内容
- 
添加和自定义视图 的配方 
- 
使用标签显示文本 的配方 
- 
通过按钮使用来获取用户输入 的配方 
将工具栏添加到视图中
在本配方中,我们将学习如何在我们的应用程序中添加和使用工具栏。
准备工作
在本配方中,我们将讨论 Toolbar 对象的使用以及我们如何使用关联的属性来显示动画进度条。
如何操作...
为了看到这是如何实现的,我们需要修改我们在前一个配方中创建的 ViewObjectsExample 应用程序。执行以下步骤来完成此操作:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中拖动一个Toolbar对象到主视图控制器中,并将其放置在主视图的顶部。 
- 
接下来,选择默认包含的按钮,并将它的标题属性设置为淡入。 
- 
接下来,在淡入按钮右侧的工具栏中添加一个 Flexible Space Bar Button Item对象。
- 
然后,从对象库中拖动一个UIBarButtonItem对象到工具栏中,并将其放置在 Flexible Space Bar Button Item对象之后。
- 
接下来,选择按钮,并将它的标题属性设置为淡出。 
- 
接下来,为淡入和淡出按钮创建出口和属性,并将它们命名为 btnFadeIn和btnFadeOut。
- 
接下来,为淡入和淡出按钮创建动作方法,并将它们命名为 viewFadeIn和viewFadeOut。
- 
在创建按钮出口和属性后,通过从菜单栏中选择文件 | 保存来保存文档,或者通过按command + S键来保存。布局应类似于以下截图: ![如何操作...]() 
我们接下来的步骤是创建代码功能,该功能将负责更新标签以显示在工具栏中按下的按钮:
- 
从项目导航器中打开 ViewController.m实现文件。
- 
修改 viewFadeIn和viewFadeOut方法,如下面的代码片段所示:- (IBAction)viewFadeIn:(UIBarButtonItem *)sender { lblInfo.text = @"Fade In button clicked."; } - (IBAction)viewFadeOut:(UIBarButtonItem *)sender { lblInfo.text = @"Fade Out button clicked."; }
- 
然后,通过从产品菜单中选择产品 | 运行来构建和运行应用程序,或者通过按command + R键来运行。 
当编译完成后,iOS 模拟器将出现。点击工具栏中的两个按钮,以查看标签根据哪个按钮被点击而更新。
工作原理...
UIToolbar对象用于包含与UIBarButtonItem对象类型相关的项。这些类型的对象是特殊的按钮和间隔符。UIBarButtonItem对象可以是系统定义的或自定义类型,并且可以使用在 Interface Builder 中标识符属性中列出的任何预定义类型,并为按钮提供特定的图标。
UIBarButtonItem对象还可以自定义以包含图像,使您的应用程序更加专业和直观。这可以通过在 Interface Builder 中使用Image属性来实现。
更多内容...
UIBarButtonItem类有一个样式属性,用于确定按钮的样式。它只能在按钮项的标识符设置为自定义时使用。您可以将按钮的样式属性设置为UIBarButtonItemStyle类型中的任何一种。
参见
- 
添加和自定义视图菜谱 
- 
使用标签显示文本菜谱 
视图淡入和淡出
在本教程中,我们将探讨如何对视图执行一些复杂的动画。这些动画非常适合构建和增强用户体验,为您的应用程序产生平滑的动画效果。
准备工作
UIView动画是构建视图当前状态和更改状态之间视觉桥梁的完美构建块。使用视图,您可以创建连接这些视图的动画。以下是一些示例:
- 
在屏幕上移动视图 
- 
更新视图的框架和边界坐标 
- 
将视图的内容拉伸以填充屏幕区域 
- 
改变视图的 alpha 值以支持透明度 
- 
隐藏或显示一个视图 
- 
改变视图的顺序以显示哪个视图在前 
- 
对视图执行变换和旋转 
如何操作...
为了看到我们的视图淡入和淡出,我们需要修改我们在上一个教程中创建的ViewObjectsExample应用程序。执行以下步骤来完成此操作:
- 
打开 ViewObjectsExample.xcodeproj项目文件。
- 
从项目导航器中选择 ViewController.m实现文件。
- 
接下来,修改 viewFadeIn方法,如下面的代码片段所示:- (IBAction)viewFadeIn:(UIBarButtonItem *)sender { [UIView beginAnimations: @"Fade In" context:nil]; [UIView setAnimationDuration:2.0]; [self.subView setAlpha:1.0f]; [UIView commitAnimations]; // Disable our FadeIn Button since we are processing [self.btnFadeOut setEnabled:YES]; [self.btnFadeIn setEnabled:NO]; }
- 
接下来,修改 viewFadeOut方法,如下面的代码片段所示:- (IBAction)viewFadeOut:(UIBarButtonItem *)sender { [UIView beginAnimations: @"Fade Out" context:nil]; [UIView setAnimationDuration:2.0]; [self.subView setAlpha:0.0f]; [UIView commitAnimations]; [self.btnFadeOut setEnabled:NO]; [self.btnFadeIn setEnabled:YES]; }
- 
然后,将以下代码片段中高亮的代码行添加到 viewDidLoad方法中:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self updateLabel]; [self displayImage]; [self populateTextBox]; [self.pgbProgress setProgress:0.0 animated:YES]; [self.btnFadeOut setEnabled:YES]; [self.btnFadeIn setEnabled:NO]; }
- 
然后,通过从产品菜单中选择产品 | 运行,或者通过按command + R来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现。点击工具栏上的两个按钮,可以看到子视图开始淡入或淡出。
工作原理…
在下面的代码片段中,我们使用beginAnimations方法标记动画块的开始,并指定Fade In动画类型,在将图形上下文设置为 nil 之前。然后,我们使用setAnimationDuration指定动画的长度(以秒为单位),并通过将setAlpha设置为1.0来设置 alpha 透明度层,使其变得可见。
最后,我们使用commitAnimations方法来指示所有要动画化的属性都已设置,并开始动画,然后相应地启用/禁用工具栏按钮。
- (IBAction)viewFadeIn:(UIBarButtonItem *)sender {
 [UIView beginAnimations: @"Fade In" context:nil];
 [UIView setAnimationDuration:2.0];
 [self.subView setAlpha:1.0f];
 [UIView commitAnimations];
 // Disable our FadeIn Button since we are processing
 [self.btnFadeOut setEnabled:YES];
 [self.btnFadeIn  setEnabled:NO];
}
接下来,我们使用beginAnimations方法标记动画块的开始,并指定Fade Out动画类型,在将图形上下文设置为 nil 之前。然后,我们使用setAnimationDuration指定动画的长度(以秒为单位),并通过将子视图的setAlpha设置为不可见来设置 alpha 透明度层,使其逐渐淡出。
最后,我们使用commitAnimations方法来指示所有要动画化的属性都已设置,并开始动画,并在工具栏中启用我们的Fade In按钮。
- (IBAction)viewFadeOut:(UIBarButtonItem *)sender {
 [UIView beginAnimations: @"Fade Out" context:nil];
 [UIView setAnimationDuration:2.0];
 [self.subView setAlpha:0.0f];
 [UIView commitAnimations];
 [self.btnFadeOut setEnabled:NO];
 [self.btnFadeIn setEnabled:YES];
}
注意
如果您想了解更多关于 UIView 动画的信息,可以参考位于 developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW131 的 Apple 开发者文档。
参见
- 
添加和自定义视图 菜谱 
- 
向视图添加工具栏 菜谱 
创建自定义表格视图控制器
在这个菜谱中,我们将学习如何创建 UITableViewController 类的子类,并使用它来填充 UITableView 控制器。
准备工作
UITableViewController 类提供了一个完美的方法,让您能够灵活地管理表格视图,并为我们的基本控制器添加共享其继承类的能力。为了实现这一点,我们需要将 UITableView 类的方法纳入其中。
如何操作...
首先,我们需要创建一个新的 Xcode 项目。这可以通过执行以下简单步骤来实现:
- 
创建一个新的 单视图应用程序 项目,并将其命名为 TableViewExample。
- 
接下来,选择 TableViewExample文件夹,选择 文件 | 新建 | 新建文件… 或按 command + N。
- 
从模板列表中选择 Objective-C 类 模板。 
- 
点击 下一步 按钮继续向导的下一步。 
- 
将要创建的文件命名为 TableViewController。
- 
确保您已从 子类 下拉菜单中选择创建子类的类型为 UITableViewController。 
- 
确保未选中 针对 iPad 复选框。 
- 
确保未选中 使用 XIB 进行用户界面 复选框。 
- 
点击 下一步 按钮继续向导的下一步。 
- 
点击 创建 按钮将文件保存到指定的文件夹位置。 
工作原理…
我们在这里所做的是向我们的 TableViewExample 应用程序添加一个新的 TableViewController 类,它为我们提供了必要的表格视图方法,我们可以使用这些方法与 TableView 控件进行交互。
参见
- 
向视图控制器添加表格视图 菜谱 
- 
在 第一章 的 使用 Xcode 创建 iOS 项目 菜谱中,获取和安装 iOS SDK 开发工具 
向视图控制器添加表格视图
在这个菜谱中,我们将学习如何向现有的视图控制器添加 UITableView 对象。
准备工作
UITableView 对象为您提供了以列表形式显示数据的接口。
如何操作...
为了向我们的视图控制器添加表格视图,我们需要修改我们在上一个菜谱中创建的 TableViewExample 应用程序。执行以下步骤来完成此操作:
- 
打开 TableViewExample.xcodeproj项目文件。
- 
接下来,更改 ViewController类的继承方式,如图中突出显示的代码行所示:@interface ViewController : UITableViewController
- 
接下来,从项目导航窗口中选择 ViewController.xib 文件。
- 
然后,通过选择它并按删除键来删除视图控制器中的当前视图。 
- 
接下来,从对象库中拖动一个TableView对象到绘图区域。 
- 
按住控制键,从文件所有者对象拖动到 UITableView,如图所示:![如何操作...]() 
- 
从弹出面板中选择视图并释放按钮。这将我们将刚刚添加的 TableView连接到文件所有者对象的视图输出。
我们的下一步是修改ViewController类,以使用我们在前一个食谱中创建的自定义表格视图控制器。这可以通过执行以下简单步骤实现:
- 
点击并选择视图中的文件所有者控制器对象。 
- 
然后,选择标识检查器部分,并将自定义类属性的值更改为 CustomTableView,如图所示:![如何操作...]() 
- 
通过从菜单栏选择文件 | 保存或按command + S来保存文档。 
- 
接下来,通过从产品菜单选择产品 | 运行或按command + R来构建和运行应用程序。 
工作原理...
当我们使用 Interface Builder 将UITableView对象添加到现有视图时,其视图会显示一些预定义的数据,这些数据仅在设计时可见,而在运行时不显示。
我们然后将我们的TableView对象的视图连接到我们的File's Owner对象,最后我们修改了视图控制器的类,以使用我们创建的自己的CustomTableView类。关于如何在UITableView中填充数据的讨论将在下一章中介绍。
参见
- 
Creating a custom table view controller食谱 
- 
在第一章的Creating an iOS Project with Xcode食谱中,获取和安装 iOS SDK 开发工具,第一章 
- 
在第八章的Displaying data within the table view食谱中,数据管理,第八章 
处理不同的 iOS 设备
在本食谱中,我们将学习如何检测我们是否在 iPhone 或 iPad 设备上运行应用程序。
准备工作
UIDevice对象提供了各种方法,允许您获取有关特定设备的信息;它可以帮助您计算出设备剩余的电量,以及确定设备类型及其方向。
如何操作...
要开始,我们需要创建一个新的 Xcode 项目。这可以通过执行以下简单步骤实现:
- 
创建一个新的单视图应用程序项目,并将其命名为 UniversalApp。
- 
接下来,从项目导航器窗口中选择 ViewController.xib文件。
- 
接下来,从对象库中将一个标签对象拖放到主视图中。 
- 
调整 标签控件的大小,使其内容填充视图的宽度。
- 
接下来,为这个 标签控件创建出口和属性,并将其命名为lblMessage。
我们接下来的步骤是创建代码功能,该功能将负责确定我们正在使用哪种 iOS 设备,以及更新标签属性。这可以通过执行以下简单步骤实现:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,修改 viewDidLoad方法,如下代码片段所示:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { self.view.frame = CGRectMake(0, 0, 768, 1024); self.lblMessage.text = @"I'm running on the iPad"; self.view.backgroundColor = [UIColor yellowColor]; self.lblMessage.font = [UIFont systemFontOfSize:18.5f]; self.lblMessage.adjustsFontSizeToFitWidth = TRUE; } else { self.lblMessage.text = @"I'm running on an iPhone"; self.view.backgroundColor = [UIColor blueColor]; self.lblMessage.font = [UIFont systemFontOfSize:18.5f]; self.lblMessage.adjustsFontSizeToFitWidth = TRUE; } }
- 
通过从菜单栏选择文件 | 保存来保存文档。 
- 
接下来,从项目导航器中选择通用应用。 
- 
在摘要选项卡中,从设备下拉菜单中选择通用,如下截图所示: ![如何操作...]() 
- 
接下来,通过点击高亮部分在不同设备之间切换,如下截图所示: ![如何操作...]() 
- 
通过从产品菜单选择产品 | 运行来构建和运行应用程序,或者通过按command + R。 
它是如何工作的...
在以下代码片段中,我们通过检查UIDevicecurrentDevice属性的userInterfaceIdiom属性来查看应用程序正在运行的设备:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
如果已经确定是 iPad,我们使用frame属性调整视图大小以适应 iPad 屏幕尺寸,然后更新我们的标签属性,并设置视图的背景,如下代码片段所示:
self.view.frame = CGRectMake(0, 0, 768, 1024);
self.lblMessage.text = @"I'm running on the iPad";
self.view.backgroundColor = [UIColor yellowColor];
self.lblMessage.font = [UIFont systemFontOfSize:18.5f];
self.lblMessage.adjustsFontSizeToFitWidth = TRUE;
或者,如果我们确定应用程序正在 iPhone 上运行,我们更新标签属性,然后设置视图的背景,如下代码片段所示:
 else {
  self.lblMessage.text = @"I'm running on an iPhone";
  self.view.backgroundColor = [UIColor blueColor];
  self.lblMessage.font = [UIFont systemFontOfSize:18.5f];
  self.lblMessage.adjustsFontSizeToFitWidth = TRUE;
 }
注意
如果你想了解更多关于UIDevice类的信息,可以参考位于developer.apple.com/library/ios/#documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html的 Apple 开发者文档。
参见
- 
添加和自定义视图 菜谱 
- 
使用标签显示文本 菜谱 
- 
在第一章的创建到 Interface Builder 对象的出口菜谱中,获取和安装 iOS SDK 开发工具 
- 
在第一章的使用 Xcode 创建 iOS 项目菜谱中,获取和安装 iOS SDK 开发工具 
第三章。使用 Storyboards
在本章中,我们将介绍:
- 
为项目配置故事板 
- 
创建 Twitter 应用程序 
- 
创建故事板场景 
- 
配置故事板场景 
- 
将转换应用到故事板 
- 
配置推文 
- 
向推文中添加照片 
- 
准备转换到另一个视图控制器 
- 
以编程方式呈现故事板视图控制器 
简介
从 Xcode 4.2 和 iOS 5 的发布开始,开发者和设计师现在可以使用 Xcode 中作为 XIB 编辑器一部分的新故事板功能来布局他们应用程序的工作流程。
而不是创建大量的界面文件,你现在可以开始在一个地方拖动并编辑所有视图,同时可以指定屏幕之间的转换以及触发它们的关联动作。
在本章中,我们将了解故事板实际上是什么,以及它们为什么需要 iOS 5 或更高版本,并熟悉 Xcode 中 XIB 编辑器内实施的新工作流程。
为项目配置故事板
准备工作
在这个菜谱中,我们将学习如何使用 Xcode 配置应用程序的项目属性,以便正确设置以使用故事板文件。
如何操作...
要开始,请执行以下简单步骤:
- 
从项目导航器窗口中选择你的项目。 
- 
然后,从 TARGETS 组下选择你的项目目标,并选择 摘要 选项卡。 ![如何操作...]() 
- 
从 主故事板 下拉菜单中选择 MainStoryboard,如前图所示。 
它是如何工作的...
在这个菜谱中,我们了解了什么是故事板,以及它们与过去创建的用户界面有何不同,即对于你的应用程序中的每个 XIB 文件,都需要创建一个新的视图。
不论你是为 iPad 还是 iPhone 创建应用程序,你的故事板中创建的每个视图控制器都代表单个屏幕的内容,由多个场景的内容组成。
视图控制器中包含的每个对象都可以链接到实现另一个场景的另一个视图控制器。
在我们的最后几步中,我们查看如何配置项目属性,以便应用程序设置以使用故事板用户界面文件。
还有更多…
你也可以选择手动将新的 Storyboard 模板添加到你的项目中。这可以通过执行以下简单步骤实现:
- 
从项目导航器窗口中选择你的项目。 
- 
选择 文件 | 新建 | 文件… 或按 command + N。 
- 
从 iOS 部分下的 用户界面 子部分中选择 Storyboard 模板,如前图所示。 ![还有更多…]() 
- 
点击 下一步 按钮继续到向导的下一步。 
- 
确保你已从 设备家族 下拉菜单中选择 iPhone。 
- 
点击 下一步 按钮继续向导的下一步。 
- 
在 另存为 字段中指定故事板文件的名称,即为要创建的文件命名。 
- 
点击 创建 按钮将文件保存到指定的文件夹。 
- 
最后,当我们使用故事板创建项目时,我们需要修改应用程序的代理文件 AppDelegate.m,如下代码片段所示:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after // application launch. return YES; }注意关于在应用程序中使用故事板的更多信息,你可以参考位于 developer.apple.com/library/ios/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/InterfaceBuilder/InterfaceBuilder的 Apple 开发者文档。
参见
- 
创建 Twitter 应用程序 菜谱 
- 
创建故事板场景 菜谱 
- 
在第二章(用户界面 – 创建 UI 
- 
接下来,将一个 标签 控件拖到视图中,并将标签的文本属性更改为 关于 Twitter 应用。 
- 
接下来,将一个 圆形矩形按钮 控件拖到我们将在后续部分中用于调用调用视图的视图中。在按钮的属性中,将文本更改为 返回。 
- 
接下来,在第一个视图控制器上,将一个 圆形矩形按钮 控件拖到视图中。在按钮的属性中,将文本更改为 关于 Twitter 应用。这将用于调用我们上一步添加的新视图。 
- 
接下来,在第一个视图控制器上,将一个 圆形矩形按钮 控件拖到视图中,位于我们上一步创建的 关于 Twitter 应用 按钮下方。在按钮的属性中,将文本更改为 撰写推文。 
- 
接下来,通过选择菜单栏中的 文件 | 保存 或按 command + S 保存你的项目。 
一旦你将控件添加到每个视图,你的最终界面应该看起来像下面屏幕截图所示:

注意
如果你想要刷新如何创建动作的记忆,这些在 第一章 的 创建响应用户动作的动作 配方中进行了讨论,获取和安装 iOS SDK 开发工具。
下一步是为我们的 撰写推文 按钮创建 Action 事件,以便它能够发布推文。要创建一个动作,请执行以下步骤:
- 
通过选择 导航 | 在辅助编辑器中打开 或按 option + command + , 打开辅助编辑器。 
- 
确保显示 ViewController.h接口文件。
- 
选择Compose Tweet按钮;按住控制键,并将其从Compose Tweet按钮拖动到 ViewController.h接口文件中的@interface和@end标签之间。
- 
从连接下拉菜单中选择要创建的连接Action。 
- 
为要创建的函数命名 composeTweet。
- 
从类型下拉菜单中选择要创建的函数类型UIButton。 ![如何操作...]() 
在以下代码片段中高亮显示的行显示了完成的ViewController.h接口文件,其中包含我们将负责调用和显示我们的推文表的函数。
//  ViewController.h
//  TwitterExample
//
//  Created by Steven F Daniel on 21/09/12.
//  Copyright (c) 2012 GenieSoft Studios. All rights reserved.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
// Create the action methods
- (IBAction)composeTweet:(UIButton *)sender;
@end
现在我们已经创建了场景、按钮和动作,我们的下一步是配置场景,这将在下一个配方中展示。
它是如何工作的...
在这个配方中,我们研究了如何向我们的故事板添加新的视图控制器,然后开始向每个视图控制器添加控件并自定义它们的属性。
接下来,我们研究了如何为我们的Compose Tweet按钮创建一个Action事件,该事件将负责响应并执行其背后的相关代码以显示我们的推文表。
我们没有将事件处理程序连接到按钮的TouchUpInside事件,而是决定简单地给它添加一个动作,并自行处理输出。这类动作被称为“实例方法”。在这里,我们基本上创建了一个Action方法,它将负责允许用户编写并发送 Twitter 消息。
参见
- 
配置故事板场景配方 
- 
将转场应用到故事板中配方 
- 
准备过渡到另一个视图控制器配方 
- 
以编程方式呈现故事板视图控制器配方 
- 
创建与 Interface Builder 对象接口的出口中的创建与 Interface Builder 对象接口的出口配方,获取和安装 iOS SDK 开发工具 
配置故事板场景
当你想从一个视图控制器过渡到另一个视图控制器时,你可以按住控制键并点击一个按钮、表格视图单元格或任何其他对象,然后将其拖动到新视图控制器以创建不同的场景。这种在视图控制器之间拖动的技术创建了一个所谓的转场。
转场是一个可配置的对象,它支持在UIKit类参考中提供的所有类型的转场,例如模态转场和导航转场。
准备工作
你还有能力定义自定义转场,用另一个视图控制器替换一个视图控制器。
如何操作…
为了了解如何实现这一点,我们需要修改我们在之前的配方中创建的TwitterExample应用程序。执行以下步骤:
- 
选择“关于 Twitter 应用”( 圆形矩形按钮)控件,然后按住控制键,将其拖动到包含“返回”按钮的视图控制器。
- 
释放鼠标按钮,然后从“操作转场”弹出菜单中选择“模态”选项。 ![如何操作…]() 你会注意到一个灰色箭头连接了你的两个视图控制器。当按下“关于 Twitter 应用”按钮时,它将显示包含“返回”按钮的页面。 
- 
接下来,我们需要对我们第二个视图做同样的操作,以便当按下“返回”按钮时,它将返回到我们的第一个视图。 
- 
重复步骤 1 到 2,但将“返回”按钮替换为“关于 Twitter 应用”按钮。 
以下表格包含了 Xcode 4 中包含的故事板转场的解释:
| 转场名称 | 描述 | 
|---|---|
| 模态 | 模态视图控制器不是 UIViewController类的一个特定子类,因为任何类型的视图控制器都可以通过你的应用程序以模态方式呈现。然而,就像标签栏和导航视图控制器一样,当你想要传达前一个视图层次结构和新呈现的视图层次结构之间的特定含义时,你可以以模态方式呈现你的视图控制器。 | 
| 推送 | 推送转场允许你将一个新的视图控制器推送到导航堆栈中,就像你堆叠盘子一样。堆栈顶部的视图是渲染的视图。 | 
| 自定义 | 这些允许你使用 prepareForSegue方法自定义并从代码中调用视图控制器,是你用来呈现应用程序内容的方式。视图控制器的工作是管理某些内容的呈现,并协调该内容与应用程序底层数据对象的更新和同步。在自定义视图控制器的情况下,这涉及到创建一个视图来呈现内容,并实现将那个视图的内容与你的应用程序数据结构同步所需的基础设施。 | 
完成这些操作后,你的视图控制器应该看起来像以下屏幕截图所示。你可以为每个视图控制器应用多种过渡,以便它们在显示到视图时执行动画。

要了解如何将过渡应用到你的视图控制器中,请参考本章中关于过渡的“将过渡应用到故事板”配方。
现在你已经将每个 segues 应用到两个视图控制器上,我们的最后一步是编译、构建和运行我们的应用程序。
从产品菜单中选择运行。或者,你可以按command + R键来编译、构建和运行应用程序。
以下屏幕截图显示了我们的应用程序在 iOS 模拟器中运行,以及它们各自关联的屏幕显示:

工作原理...
所以,这就是全部内容。在这个菜谱中,我们学习了如何创建并将新场景添加到我们的主故事板中,以及如何通过使用 segues 来链接这些场景并在按钮按下时配置每个场景的过程。
还有另一种方法,我们可以通过编程方法将场景过渡到我们的故事板中。当我们开始本章的 以编程方式呈现故事板视图控制器 菜谱时,我们将更深入地探讨这一点。
相关内容
- 
将过渡应用到故事板 菜谱 
- 
准备过渡到另一个视图控制器 菜谱 
- 
以编程方式呈现故事板视图控制器 菜谱 
将过渡应用到故事板
在这个菜谱中,我们将探讨如何将过渡动画应用到视图上,以及与故事板一起提供的所有可用过渡。
准备工作
您可能已经在应用程序中看到过这样的过渡,例如 iPhone 和 iPad 中包含的照片应用,您可以在其中应用过渡并开始幻灯片放映。
如何操作…
为了配置 segues 以指定在不同场景之间使用的一种过渡,请执行以下简单步骤:
- 
点击以下截图中的大圆圈所示的第一视图控制器的 segues 链接。 
- 
点击 属性检查器,然后点击 过渡 下拉菜单。 ![如何操作…]() 
您可以选择仅适用于 Modal 风格的各种过渡类型;这些类型在以下表格中解释:
| 过渡名称 | 描述 | 
|---|---|
| 默认 | 当选择此过渡时,它使用 垂直覆盖 过渡样式。 | 
| 垂直覆盖 | 当视图控制器呈现时,其视图从屏幕底部向上滑动。当视图消失时,它滑回底部。 | 
| 水平翻转 | 当视图控制器呈现时,当前视图从右侧开始水平 3D 翻转至左侧,从而揭示新视图,仿佛它位于前一个视图的背面。当此视图消失时,翻转从左侧开始至右侧,返回到原始视图。 | 
| 交叉溶解 | 当视图控制器呈现时,当前视图淡出,同时新视图同时淡入。当视图消失时,使用类似类型的交叉淡入淡出返回到原始视图。 | 
| 部分卷曲 | 当视图控制器呈现时,当前视图的一个角落卷曲起来以显示下面的模态视图。当视图被取消时,卷曲的页面会自己展开回到模态视图的顶部。使用此过渡方式呈现的模态视图本身被阻止呈现任何额外的模态视图。此过渡样式仅在父视图控制器呈现全屏视图且您使用 UIModalPresentationFullScreen模态呈现样式时才受支持。尝试使用不同的父视图形式因子或不同的呈现样式将触发异常。 | 
这些过渡是作为从 UIKit 框架继承的 UIViewController 类的一部分提供的。UIViewController 类为您的所有 iOS 应用提供了基本的视图管理模型。
注意
更多关于前面提到的切换类型的信息,请参考 Apple 开发者文档中的 UIViewController 框架参考,位于 developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html。
它是如何工作的...
Xcode 提供了在故事板中从一个场景到另一个场景改变过渡视觉效果的选择。这些通常被称为切换。
使用切换可以使您为要渲染和显示到视图中的每个视图控制器应用不同的样式,并且由视图控制器之间的箭头表示。默认情况下,执行的是 覆盖垂直 切换,其中新场景从视图底部垂直滑动到顶部覆盖当前显示的场景。
您有权定义自定义切换,这使您能够提供一个自定义切换类方法来处理切换。这可以通过选择切换的 自定义 样式,并填写要使用的自定义切换类名称来实现。如果您想使用任何标准切换类,您可以在 UIKit 类中找到这些类。
注意
关于标准切换类的信息,您可以参考 Apple 开发者文档中的 UIKitframework 参考信息,位于 developer.apple.com/library/ios/#documentation/uikit/reference/UIKit_Framework/_index.html。
参见
- 
准备过渡到另一个视图控制器 的配方 
- 
以编程方式呈现故事板视图控制器 的配方 
编写推文
在这个配方中,我们将探讨如何使用 iOS 5 的 Twitter API 并有效地与之交互。
准备工作
Twitter 为我们提供了一些非常简单的 API 来遵循,使其与它们交互变得非常简单。在本教程中,我们将探讨如何使用这些 API 通过 Twitter 组合表发布推文。
如何做到这一点...
为了在您的应用程序中使用 Twitter,我们需要将 Twitter 框架添加到您的项目中。这可以通过执行以下简单步骤实现:
- 
在项目导航器中选择您的项目。 
- 
然后,从目标组下选择你的项目目标。 
- 
选择构建阶段选项卡。 
- 
展开链接二进制库展开箭头。点击+按钮,从列表中选择Twitter.framework。 
- 
最后,点击添加按钮将框架添加到您的项目中。 
我们下一步是创建负责将我们的推文发布到 Twitter 的代码功能:
- 
从项目导航器打开 ViewController.m实现文件。
- 
输入以下 import语句:#import <Twitter/Twitter.h>
- 
接下来,修改 composeTweet方法,如下面的代码片段所示:- (IBAction)composeTweet:(id)sender { TWTweetComposeViewController *myTwitter = [[TWTweetComposeViewController alloc] init]; BOOL isSUCCESS = TWTweetComposeViewController.canSendTweet; if (isSUCCESS == YES) { [myTwitter setInitialText:@"Welcome to 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:@"Tweet not posted." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; [self dismissModalViewControllerAnimated:YES]; } }; } }
工作原理...
从 iOS 5 开始,每次您想要提交 Twitter 消息时,都需要使用TWTweetComposeViewController类实例。TWTweetComposeViewController类处理所有事情,并显示推文组合表,以便我们可以开始输入我们的推文。
TWTweetComposeViewController类还允许您设置要使用的初始 Twitter 文本信息,包括如何添加图片和 URL。接下来,我们声明了一个myTwitter变量,指向我们的TWTweetComposeViewController类的一个实例。
然后,我们使用了TWTweetComposeViewController类的canSendTweet类方法来检查用户是否正确安装并设置了 Twitter。如果没有这样做,这个语句将失败,并将返回一个NO(或FALSE)值给isSuccess变量。
接下来,我们通过设置setInitialText方法将一些文本分配到我们的组合表格中,并将其显示到视图中。最后,我们设置了一个处理程序,使用completionHandler方法在用户完成撰写推文时通知我们,并根据方法返回的结果显示相关的警告信息。
注意
关于TWTweetComposeViewController类的更多信息,您可以参考位于developer.apple.com/library/ios/#documentation/Twitter/Reference/TWTweetSheetViewControllerClassRef/Reference/Reference.html的 Twitter 框架参考文档。
更多内容...
随着 iOS 6 的发布,社交网络的集成是通过使用UIActivityViewController类,或者 iOS 6 SDK 中包含的新社交框架的类来实现的。对于通用社交网络集成,推荐使用UIActivityViewController类。
当使用此类时,用户将看到一个屏幕,提供社交网络服务的选择。一旦应用程序选择了目标服务,该类随后将向用户展示一个空白消息预览面板,用户可以在其中查看消息然后发布。
接下来,我们将探讨UIActivityViewController和SLComposeViewController这两个类,看看它们各自的不同之处。
使用 UIActivityViewController 类
UIActivityViewController类是在准备将帖子发布到社交网络时在应用程序中实例化的。以下代码片段展示了如何使用UIActivityViewController类来处理发布到 Twitter 的帖子:
NSString *postText = @"My first Twitter Post from iOS 6";
NSArray *activityItems = @[postText];
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
[self presentViewController:activityController animated:YES completion:nil];
前面的代码片段实例化了一个新的UIActivityViewController实例,并将要包含在对话框中的文本传递给用户。有一个选项可以包含与帖子一起的图片,我们将在稍后探讨这一点。
当用户决定在应用程序内执行发布更新操作时,将显示以下屏幕:

一旦从选择列表中选择了一个目标社交网络,将显示一个预览表单。如果用户尚未在设置应用程序中为所选社交网络配置账户,将出现一个对话框,提示用户设置账户或简单地取消发布。
注意
关于UIActivityViewController类的更多信息,您可以参考位于developer.apple.com/library/ios/#documentation/UIKit/Reference/UIActivityViewController_Class/Reference/Reference.html的 Apple 开发者参考文档。
使用 SLComposeViewController 类
为了使用SLComposeViewController类,需要执行一系列步骤。首先,我们需要验证消息是否可以发送到特定的社交网络服务,以确保设备已正确配置以使用该特定服务。
这是通过将服务类型作为参数传递给isAvailableForserviceType方法来实现的,如下面的代码片段所示:
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) {
      // Device has been set up to use Twitter
}
isAvailableForServiceType方法接受以下参数:
- 
SLServiceTypeFacebook
- 
SLServiceTypeTwitter
- 
SLServiceTypeSinaWeibo
下一步是创建一个SLComposeViewController类的实例,并提供一个可选的完成处理程序,当用户取消编辑表单或使用它来发送消息时将被调用。最后,当消息准备好向用户展示时,通过调用父视图控制器的presentViewController方法,以模态方式展示SLComposeViewController对象。
以下代码片段展示了如何配置和展示用于发布到 Twitter 的SLComposeViewController类实例:
SLComposeViewController *composeController = [SLComposeViewController
composeViewControllerForServiceType:SLServiceTypeTwitter];
[composeController setInitialText:@"Posting using Twitter"];
[composeController addURL: [NSURL URLWithString:
@"http://www.packtpub.com/xcode-4-cookbook/book/"]];
[self presentViewController:composeController
               animated:YES completion:nil];
上述代码片段展示了如何使用方法调用预配置默认文本的 Twitter 编辑表单。完成处理程序可以设置以返回一个值,指示用户在编辑表单视图中采取的操作。这些值在以下表中解释:
| 编辑表单值 | 描述 | 
|---|---|
| SLComposeViewControllerResultCancelled | 用户通过触摸取消按钮取消了编辑会话。 | 
| SLComposeViewControllerResultDone | 用户通过触摸发送按钮发送了编辑好的消息。 | 
iOS 6 附带的社会框架类包含两个类,这些类被设计用来提供更多关于社交网络集成的灵活性。
与UIActivityViewController类不同,SLComposeViewController类允许您在应用代码中向特定的社交网络服务发布消息,而无需用户从可用服务列表中进行选择。
用户随后会看到一个适合应用已设置的具体服务的预览表单。在您的应用中使用社交框架非常简单,可以通过执行以下简单步骤实现:
- 
将Social.framework添加到项目中,就像我们添加Twitter.framework一样。 
- 
从项目导航器打开 ViewController.m实现文件。
- 
输入以下导入语句: #import <Social/Social.h>
- 
接下来,修改 composeTweet方法,如下面的代码片段所示:- (IBAction)composeTweet:(id)sender { if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) { // Device is able to send a Twitter message SLComposeViewController *composeController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; SLComposeViewControllerCompletionHandler __block myHandler = ^(SLComposeViewControllerResult result) { if (result == SLComposeViewControllerResultDone) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Twitter" message:@"Yourtweet was posted successfully." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } else if (result == SLComposeViewControllerResultCancelled) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Twitter" message:@"Your Tweet was not posted." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } [composeController dismissViewControllerAnimated:YES completion:nil]; }; [composeController setCompletionHandler:myHandler]; [composeController setInitialText:@"Welcome to iOS 6"]; [composeController addURL: [NSURL URLWithString: @"http://www.packtpub.com/xcode-4-cookbook/book/"]]; [self presentViewController:composeController animated:YES completion:nil]; } }注意关于 SLComposeViewController类的更多信息,您可以参考位于developer.apple.com/library/ios/#documentation/NetworkingInternet/Reference/SLComposeViewController_Class/Reference/Reference.html的 Apple 开发者参考文档。
参见
- 
添加照片到推文配方 
- 
程序化地展示故事板视图控制器配方 
- 
在附录的理解 Core Motion 框架部分,探索多点触控界面 
添加照片到推文
在本配方中,我们将学习如何将图像嵌入到 Twitter 消息中。
准备工作
在这个菜谱中,我们将探讨如何使用 Twitter 编辑表单将图片添加到推文中。
如何做到这一点…
向 Twitter 消息添加图片非常简单,可以通过在TwitterExample项目中执行以下简单步骤来实现:
- 
从项目导航器中打开 ViewController.m实现文件。
- 
接下来,修改 composeTweet方法,并在以下代码片段中添加高亮代码:- (IBAction) composeTweet: (UIButton *) sender { // Attach an image to our Tweet TWTweetComposeViewController *myTwitter = [[TWTweetComposeViewController alloc] init]; [myTwitter addImage:[UIImage imageNamed:@"Blue-Aqua- Apple.png"]]; [self presentModalViewController:myTwitteranimated:YES]; }
在这个代码片段中,我们声明一个myTwitter变量为我们的TWTweetComposeViewController类的一个实例。然后我们使用addImageinstance方法,向推文添加图片,然后将带有图片的视图展示给用户。无论何时你想向 Twitter 消息添加图片以提交,你都需要使用TWTweetComposeViewController类实例。这个类处理所需的一切,并为我们提供一个推文编辑表单,以便我们可以添加图片和 URL。
现在我们已经将最终代码添加到TwitterExample应用程序中,首先我们需要在构建和运行应用程序之前配置我们的 Twitter 账户信息。执行以下步骤以设置和配置 Twitter:
- 
从 iOS 主屏幕打开设置应用程序。 
- 
从设置菜单中选择Twitter选项。 
- 
输入你的用户名和密码凭据,然后点击登录按钮。如果你没有 Twitter 账户,你可以通过选择创建新账户选项在此屏幕上创建一个。 ![如何做到这一点…]() 
- 
接下来,通过从产品菜单中选择产品 | 运行,或者通过按command + R来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现,显示我们的 Twitter 应用程序。

当你开始撰写推文时,你可以选择将你的当前地理位置添加到你的消息中。这个功能基本上使用 Google Maps API 将你的位置与推文一起发送,然后允许其他人查看 Google Maps 上的精确位置。
也可以将附件添加到编辑的消息中,这可以是任何有效的图像(PNG、JPG 等)。
点击发送按钮将消息提交到你的 Twitter 账户,你将收到一条消息,表明推文已成功发布。
它是如何工作的…
在这个菜谱中,我们探讨了如何将类似 Twitter 的功能集成到我们的应用程序中。通过包含 Twitter,应用程序可以以多种方式变得更加社交。例如,你可以在解锁游戏中的特殊物品时自动推文,或者在完成游戏时,或者只是想上传你的高分成就。
这样可以让他们的所有朋友都知道他们正在玩你的游戏,这反过来又让你获得更多的曝光。另一个例子可能是一个商业应用程序,它可能允许用户推文他们已完成的成功项目数量。鉴于 Twitter 最近受到如此多的关注,不将某种 Twitter 集成到自己的 iOS 应用程序中简直是疯了。
还有更多...
使用SLComposeViewController类通过以下代码片段将图像添加到推文中:
// Device is able to send a Twitter message
SLComposeViewController *composeController = 
[SLComposeViewController
composeViewControllerForServiceType:SLServiceTypeTwitter];
[composeController setInitialText:@"
My first Twitter Post from iOS 6"];
[composeController addImage:
[UIImage imageNamed:@"Blue-Aqua-Apple.png"]];
[composeController addURL: [NSURL URLWithString:
  @"http://www.packtpub.com/xcode-4-cookbook/book/"]];
[self presentViewController:composeController
               animated:YES completion:nil];
在前面的代码片段中,我们使用了SLComposeViewController方法来配置和显示推文编辑表单。然后我们使用显示默认文本和设置默认图片的方法调用配置我们的编辑视图。
参见
- 
将视图控制器呈现给另一个视图控制器的配方 
- 
以编程方式呈现故事板视图控制器的配方 
准备转换到另一个视图控制器
在这个配方中,我们将学习如何使用故事板功能通过代码过渡到另一个视图控制器。
准备工作
在本节中,我们将探讨如何通过使用 segue 以编程方式过渡到另一个视图控制器。
如何做…
为了使用 segue 以编程方式执行到另一个视图控制器的转换,请执行以下简单步骤:
- 
从项目导航器中选择 MainStoryboard.storyboard文件。
- 
选择关于 Twitter 应用程序按钮的 segue 关系。 
- 
点击属性检查器按钮。 
- 
将标识符属性更改为 secondViewController。
- 
将样式属性更改为模态。 
- 
将过渡属性更改为垂直覆盖。 ![如何做…]() 
接下来,我们需要修改我们的视图控制器以包含负责在故事板中处理视图控制器之间转换的prepareForSegue:sender方法:
- 
从项目导航器中打开 ViewController.m实现文件。
- 
创建 prepareForSegue:sender方法,如下面的代码片段所示:-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Ensure we are processing the correct segue within the // Storyboard. if ([segue.identifier isEqualToString:@"secondViewController"]) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Twitter Example" message:@"Currently displaying View #2" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } }
- 
然后,通过从产品菜单中选择产品 | 运行,或者通过按command + R来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现;点击关于 Twitter 应用程序按钮以查看转换到第二个视图控制器。

它是如何工作的...
当用户在当前场景中触发 segue 时,故事板运行时会调用当前视图控制器的prepareForSegue:sender方法。此方法给当前视图控制器一个机会,将任何所需的数据传递给即将显示的视图控制器。
然后,我们执行与使用其标识符的控制相关联的 segue 调用,并在显示视图之前检查我们是否正在处理正确的 segue,然后显示一个警报。
以这种方式处理允许我们自定义切换,并将任何过渡应用到位于您故事板中的场景,只要标识符是唯一的。
注意
关于如何实现UIViewController类的方法的信息,请参阅位于developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/cl/UIViewController的 Apple 开发者文档。
参考以下内容
- 以编程方式呈现故事板视图控制器教程
以编程方式呈现故事板视图控制器
在本教程中,我们将学习如何使用故事板功能以编程方式在故事板中呈现视图控制器。
准备工作
在本教程中,我们将探讨如何通过使用其标识符以编程方式在故事板中调用另一个视图控制器。
如何操作…
为了以编程方式确定我们处于哪个视图,我们需要为我们的第二个视图控制器创建一个新的UIViewController子类。这可以通过执行以下简单步骤实现:
- 
从项目导航器中选择 TwitterExample文件夹。
- 
选择文件 | 新建 | 文件…或按command + N。 
- 
从模板列表中选择Objective-C 类模板。 
- 
将创建的类名称输入为 SecondViewController。
- 
确保您已从子类下拉列表中选择UIViewController作为要创建的子类的类型。 
- 
确保未勾选针对 iPad 优化复选框。 
- 
确保未勾选使用 XIB 进行用户界面复选框。 
- 
点击下一步继续向导的下一步。 
- 
指定保存类文件的位置,然后点击创建按钮。 
完成此操作后,我们需要更新第二个视图控制器的类方法以使用我们的SecondViewController子类:
- 
在项目导航器中,从 TwitterExample文件夹打开位于SecondViewController.h接口文件。
- 
通过添加以下代码片段中显示的高亮代码部分来修改接口文件: // SecondViewController.h // TwitterExample // // Created by Steven F Daniel on 21/09/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> @interface SecondViewController : UIViewController <UIAlertViewDelegate> @end
- 
在项目导航器中,从 TwitterExample文件夹打开位于SecondViewController.m实现文件。
- 
创建 prepareForSegue:sender方法,如下代码片段所示:-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Ensure we are processing the correct segue // within the Storyboard if ([segue.identifier isEqualToString:@"firstViewController"]) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Twitter Example" message:@"Currently displaying View #1" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } }
- 
选择第二个视图控制器,并点击身份检查器按钮。 
- 
接下来,将自定义类属性更改为读取 SecondViewController。![如何操作…]() 
- 
接下来,我们需要为我们的第一个视图控制器应用相同的 storyboard 切换。 
- 
选择属性检查器部分,然后在StoryboardSegue部分中输入 FirstViewController作为要使用的唯一标识符。
- 
重复我们对 SecondViewController所做的相同步骤。
- 
接下来,通过从 产品 菜单中选择 产品 | 运行,或者通过按 command + R 来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现,显示我们故事板中定义的每个视图控制器之间的编程过渡。
它是如何工作的...
当你点击 关于应用 按钮时,它过渡到第二个视图控制器,然后根据 prepareForSegue:(UIStoryboardSegue*)segue 方法调用显示消息,确定正在显示的当前视图控制器的标识符。
点击 返回 按钮将控制权转交给第一个视图控制器;会调用 prepareForSegue 方法来确定当前视图的当前标识符。
更多信息...
虽然故事板运行时通常处理视图控制器之间的过渡,但你也可以直接在代码中编程触发转场。你可能选择在 Xcode 中的 XIB 编辑器内设置转场时这样做,或者你可能想使用加速度计事件来触发过渡并显示图形动画。
如果你查看以下代码片段,它展示了你如何通过使用 UIStoryboard 类的 instantiateViewControllerWithIdentifier: 方法在故事板中编程显示任何视图控制器。然后我们使用 presentViewController: 方法将视图控制器推入视图控制器导航堆栈。
SecondViewController *mvc = [self.storyboard
     instantiateViewControllerWithIdentifier:@"secondViewController"];
注意
关于如何实现 UIStoryboard 类的方法的信息,请参阅位于 developer.apple.com/library/ios/#documentation/UIKit/Reference/UIStoryboard_Class/Reference/Reference.html 的 Apple 开发者文档。
参见
- 
编写推文 的食谱 
- 
将照片添加到推文 的食谱 
- 
将过渡应用到故事板 的食谱 
- 
准备过渡到另一个视图控制器 的食谱 
第四章:使用 Xcode Instruments
在本章中,我们将介绍:
- 
介绍 Xcode Instruments 
- 
跟踪 iOS 应用程序 
- 
运行和配置 iOS 项目 
- 
检测虚拟内存故障 
- 
检测内存泄漏 
- 
处理运行时错误 
- 
处理编译时错误 
- 
添加和配置 Instruments 
简介
在本章中,我们将专注于如何在我们的应用程序中有效使用 Xcode Instruments 来追踪可能影响整体性能的 iOS 应用程序中的区域。
这些类型的问题可能会导致应用程序运行缓慢,甚至在使用者的 iOS 设备上崩溃。我们将查看不同类型的内置 Instruments,这些 Instruments 是 Instruments 应用程序的一部分。我们还将了解如何使用系统跟踪 Instruments 来帮助追踪代码中的系统调用、内存使用和线程,这些可能影响应用程序的整体性能。
介绍 Xcode Instruments
在本食谱中,我们将学习如何使用 Instruments 帮助收集有关应用程序性能的重要信息。
准备工作
在本节中,我们将学习如何启动 Instruments 应用程序。
如何操作...
要开始,请按照以下简单步骤操作:
- 
从 /Developer/Applications文件夹启动 Xcode。
- 
从 Xcode 菜单中选择 Instruments,位于 打开开发者工具 子菜单下,如以下截图所示: ![如何操作...]() 
- 
从可用模板中选择 活动监视器 选项。 ![如何操作...]() 
- 
点击 选择 按钮以继续向导的下一步。 
- 
接下来,从下拉菜单中选择 所有进程,如以下截图中的矩形所示: ![如何操作...]() 
- 
点击 记录 按钮开始分析和监控系统进程。 
工作原理...
在本食谱中,我们学习了如何轻松启动 Instruments 应用程序来分析应用程序。我们学习了如何使用 活动监视器 选项来展示我们如何使用此工具来监控当前计算机系统的整体 CPU、内存、磁盘和网络活动。
更多内容…
通过使用 Instruments 应用程序,您可以根据各种不同类型的数据收集信息,并同时在同一时间查看这些信息。这使得您能够发现其他情况下难以发现的趋势,并且这可以用来查看程序运行的代码以及相应的内存使用情况。
以下表格提供了 Instruments 应用程序中每个功能的简要描述:
| Instruments 功能 | 描述 | 
|---|---|
| 仪器面板 | 此面板列出了所有已添加的仪器,供您针对要分析的项目使用。可以通过选择并从仪器库中将每个仪器拖动到该面板中添加新仪器。此面板中的项目也可以被删除。 | 
| 跟踪面板 | 此面板显示当前仪器返回数据的图形摘要。每个仪器都有自己的跟踪,提供该仪器收集的数据图表。此面板中的信息为只读。 | 
| 详细面板 | 此面板显示每个仪器收集的数据的详细信息。它显示收集并用于在跟踪面板内创建图形视图的事件集。根据仪器的类型,此面板表示的信息可以自定义以不同方式表示数据。 | 
| 扩展详细面板 | 此面板显示当前在详细面板中选中的项目的详细信息。此面板显示完整的堆栈跟踪、时间戳以及其他为给定事件收集的特定于仪器的数据。 | 
| 导航栏 | 此面板显示您当前的位置以及您到达那里的步骤。它包括两个菜单:活动仪器菜单和详细视图菜单。您可以通过点击导航栏中的条目来选择活动仪器,以及详细视图中的级别和信息类型。 | 
仪器跟踪文档工具栏允许您添加和控制仪器、打开视图以及配置跟踪面板,如下面的截图所示:

以下表格为工具栏上每个不同控制项的解释:
| 工具栏项 | 描述 | 
|---|---|
| 暂停/继续按钮 | 此按钮在记录过程中暂停跟踪数据的收集。当在跟踪面板中按下暂停按钮时,它将在跟踪数据中显示一个间隙,以突出显示这一点。 | 
| 记录/停止按钮 | 此按钮开始或停止记录过程。您使用此按钮开始收集应用程序的跟踪数据。 | 
| 循环按钮 | 此按钮允许您设置记录器在播放时是否应该循环,以连续重复记录的步骤。如果您想收集给定步骤集的多次运行,这可能很有用。 | 
| 目标 | 此按钮用于选择文档的跟踪目标。这是数据收集的过程。 | 
| 检查范围 | 此功能允许您在跟踪面板中选择时间范围。当设置完成后,仪器将仅显示指定时间期间收集的数据。使用此控制项的按钮可以设置检查范围的起始和结束点,并清除当前范围。 | 
| 时间 / 运行控制 | 显示当前文档跟踪所经过的时间。如果跟踪文档包含与它关联的多个数据运行,您可以使用箭头控件选择您想在跟踪面板中显示的运行数据。 | 
| 视图 控制器 | 隐藏或显示 Instruments 面板、详细 面板和 扩展 视图 面板。这些控件使您更容易只关注您感兴趣的区域。 | 
| 库 | 隐藏或显示 Instruments 库窗口。 | 
| 搜索 | 此选项根据您输入的搜索词过滤 详细 面板中的信息。 | 
Instruments 应用程序现在位于 /Developer/Applications 文件夹内的 Xcode 包中,可以通过 Xcode 开发 IDE 启动。
注意
有关使用 Instruments 应用程序调试应用程序的更多信息,您可以参考位于以下位置的 Apple 开发者文档:
参见
- 
跟踪 iOS 应用程序 菜谱 
- 
运行和性能分析项目 菜谱 
- 
添加和自定义视图 菜谱在 第二章,用户界面 – 创建应用 UI 
跟踪 iOS 应用程序
在这个菜谱中,我们将学习如何打开一个用于性能分析的项目。
准备工作
在本节中,我们将首先打开我们在上一章中创建的 TwitterExample 应用程序。
如何操作...
要开始跟踪应用程序,请按照以下概述的简单步骤操作:
- 
从 /Developer/Applications文件夹启动 Xcode。
- 
导航到 文件 | 打开,或者按 Command + O。 
- 
然后,从对话框中选择 TwitterExample.xcodeproj文件。![如何操作...]() 
- 
点击 打开 按钮继续,并将您的项目加载到 Xcode 工作区环境中。 
它是如何工作的...
在这个菜谱中,我们选择了我们在上一章中创建的 TwitterExample 应用程序,它将被用于使用 Instruments 应用程序来分析其性能。
在我们接下来的菜谱中,我们将探讨如何使用系统跟踪工具来性能分析我们的应用程序,以追踪可能影响您 iOS 应用程序整体性能的系统调用、内存和线程调用。
参见
- 
如何在第二章 中运行和性能分析 iOS 项目 *菜谱 
- 
如何检测虚拟机故障 菜谱 
运行和性能分析 iOS 项目
在这个菜谱中,我们将学习如何使用系统跟踪工具来监控您应用程序的性能。
准备工作
在本节中,我们将学习如何在 Xcode 环境中运行 Instruments 应用程序以分析我们的TwitterExample应用程序。
如何操作...
要开始分析我们的TwitterExample,请按照以下步骤操作:
- 
从项目导航器窗口中选择TwitterExample选项。 
- 
从产品菜单中选择为 | 配置文件,或者使用键盘快捷键Shift + Command + I。 
- 
接下来,从产品菜单中选择配置文件以启动 Instruments 应用程序。同样,您也可以使用键盘快捷键Command + I。 ![如何操作...]() 
- 
一旦选择了此选项,您最终将在屏幕上看到Instruments应用程序窗口。以下截图显示了这一过程: ![如何操作...]() 
- 
接下来,从可用模板列表中选择系统跟踪选项。 
- 
点击配置文件按钮以开始分析我们的 TwitterExample应用程序。您的应用程序将被分析,并对所有已对内存进行的系统调用和线程进行分析。这些也包括虚拟内存(VM)操作。 ![如何操作...]() 
- 
经过几秒钟后,您的跟踪信息将显示出来,这将包含有关线程和系统调用及其持续时间的详细信息。其他信息,如 VM 故障,也如以下截图所示记录: ![如何操作...]() 
您可以选择通过点击记录按钮停止应用程序的分析,因为 Instruments 应用程序已经完成了全面的分析。
工作原理...
在本教程中,我们学习了如何在 Xcode 开发环境中直接运行 Instruments 应用程序。然后我们看到了如何使用系统跟踪工具来分析我们的应用程序,这使我们能够监控 Instruments 应用程序遇到的系统调用、线程以及虚拟内存故障。
还有更多...
除了系统跟踪工具外,Instruments 应用程序还包含其他模板,您可以使用这些模板来分析您的应用程序。以下表格概述了 iOS 开发中可用的每个模板:
| 模板 | 描述 | 
|---|---|
| 空白 | 创建一个空白的跟踪文档,您可以添加自己的 Instruments 组合。 | 
| 时间分析器 | 对一个或所有进程进行低开销和时间采样。 | 
| 系统跟踪 | 提供您对可能影响应用程序性能的操作系统不同方面的分析能力。 | 
| 活动监视器 | 监控整体 CPU、内存、磁盘和网络活动。 | 
| 自动化 | 在您的应用程序内自动执行用户界面测试。 | 
| 能源诊断 | 显示有关设备上用于 GPU 活动、显示亮度、睡眠/唤醒、蓝牙、Wi-Fi 和 GPS 的能耗的诊断信息。 | 
| 网络连接 | 使用此工具,可以查看每个应用程序的每个连接上的数据流量,以及一些有趣的统计数据,如往返时间和重传请求。您可以使用这些信息帮助减少网络流量和能耗。 | 
| 分配 | 监控程序中的内存和对象分配模式。 | 
| 泄漏 | 检测应用程序中的内存泄漏。 | 
| 线程 | 分析进程内线程状态转换,包括运行和终止的线程、线程状态以及相关的回溯信息。 | 
| 文件活动 | 监控应用程序与文件系统的交互。 | 
参见
- 
运行和配置 iOS 项目食谱 
- 
检测虚拟内存故障食谱 
检测虚拟内存故障
在本食谱中,我们将学习如何使用活动监视器工具来监控操作系统和计算机硬盘的性能。
准备工作
在本节中,我们将学习如何使用 Instruments 检查虚拟内存,以查看是否发生颠簸(过度分页)或虚拟内存大小过度增长,这表明过度保留的自动释放对象。
如何操作...
首先,按照以下概述的简单步骤操作:
- 
从项目导航器窗口中选择TwitterExample选项。 
- 
通过产品 | 构建为 | 配置从产品菜单导航,或者使用键盘快捷键Shift + Command + I。 
- 
接下来,从产品菜单中选择配置,以启动 Instruments 应用程序。同样,您也可以使用键盘快捷键Command + I。 
- 
接下来,从可用模板列表中选择活动监视器选项。 
- 
点击配置文件按钮开始分析我们的TwitterExample应用程序。 经过几秒钟后,您的跟踪信息将显示出来,这将包含有关应用程序使用的整体 CPU、内存、磁盘和网络活动信息,如下面的截图所示: ![如何操作...]() 
您可以通过点击停止按钮停止应用程序的分析,因为 Instruments 应用程序已经完成了全面的分析。
它是如何工作的...
在本食谱中,我们学习了如何使用活动监视器组件,该组件使我们能够检查可能导致计算机系统性能缓慢的操作系统虚拟内存。上一张截图显示了系统当前正在承受的整体负载信息,以及用户应用程序的性能和当前分配给缓存这些操作的总体虚拟内存。
参见
- 运行和配置 iOS 项目的食谱
检测内存泄漏
在本食谱中,我们将学习如何使用 Leak 工具检查 iOS 应用程序中的内存泄漏。
准备工作
在本节中,我们将学习如何快速确定在我们的应用程序中何时发生内存泄漏。
如何操作...
要开始创建一个新的 Xcode 项目,请按照以下简单步骤进行:
- 
从 /Developer/Applications文件夹中启动 Xcode。
- 
选择创建一个新的 Xcode 项目,或者导航到文件 | 新建项目。 
- 
从可用模板列表中选择单视图应用程序选项。 
- 
点击下一步按钮以继续向导的下一步。 
- 
接下来,输入 MemoryLeaks作为你的项目名称。
- 
在设备下拉菜单中选择iPhone。 
- 
确保未勾选使用故事板复选框。 
- 
确保未勾选使用自动引用计数复选框。 
- 
确保未勾选包含单元测试复选框。 
- 
点击下一步按钮以继续向导的下一步。 
- 
指定你想要保存项目的地方,然后点击创建按钮以在指定位置保存项目。 我们下一步是创建负责分配我们的字符串对象的代码功能。 
- 
从项目导航器窗口中打开 ViewController.m文件实现文件。
- 
修改 viewDidLoad方法,如下面的代码片段中突出显示的代码部分所示:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, //typically from a nib. NSLog(@"Starting...."); // Loop for 5000 times for (int i = 1; i <= 5000; i++){ NSString *status = [[NSString alloc]initWithString:@"Memory Leaking...."]; NSLog(@"Value of i:%i and status:%@", i, status); } NSLog(@"Completed..."); }我们下一步是开始分析我们的项目,这将帮助我们检查内存泄漏。 
- 
从产品菜单中选择产品 | 为...构建 | 分析,或者使用键盘快捷键Shift + Command + I。 
- 
然后,从产品菜单中选择分析以启动 Instruments 应用程序。同样,你也可以使用键盘快捷键Command + I。 
- 
接下来,从内存部分标题下的可用模板列表中选择Leak选项。 ![如何操作...]() 
- 
点击分析按钮以开始分析我们的 MemoryLeaks应用程序。![如何操作...]() 
你可以选择通过点击停止按钮停止应用程序的分析,因为 Instruments 应用程序已经完成了全面的分析。
工作原理...
在本食谱中,我们创建了一个包含在循环中分配 5000 个字符串的代码片段的应用程序,以演示内存泄漏可能发生的方式。代码为每次通过循环时新创建的每个字符串Status分配内存,并让每个分配的字符串的指针超出作用域,永远不会被释放。
你会注意到分配的内存从未被释放,这是由于紧密的for循环耗尽了run循环的执行,导致你的应用程序运行缓慢,可能引发崩溃或简单地挂起。
截图显示了有关当前分配的内存的信息,以及用户应用程序的性能和当前正在分配的总虚拟内存。
参见
- 
运行和配置 IOS 项目 的食谱 
- 
在 第一章 的 构建我们应用程序的用户界面 食谱中,获取和安装 iOS SDK 开发工具 
处理运行时错误
在这个食谱中,我们将学习如何在 iOS 应用程序中处理运行时错误。
准备工作
在本节中,我们将学习我们如何处理运行时错误,以及我们如何使用 @try…@catch…@finally 子句来改进错误处理,以防止我们的应用程序崩溃。
如何做这件事...
要开始,请按照以下简单步骤操作:
- 
打开 MemoryLeaks.xcodeproj项目文件。
- 
从 项目导航器 窗口中打开 ViewController.m实现文件。
- 
修改 viewDidLoad方法,如下面的代码片段中突出显示的代码部分所示:- (void)viewDidLoad { [super viewDidLoad]; [self causeRuntimeError]; }
- 
接下来,创建 causeRuntimeError方法,如下面的代码片段所示:-(void)causeRuntimeError { NSLog(@"Runtime Error section started"); NSMutableArray *myArray = [NSMutableArray array]; for (int x = 0; x < 30; x++) { // Add the value to our Array [myArray addObject:[NSNumber numberWithInt:x]]; } // This will cause a Runtime error, as we don't have any // item at this index within the array. [myArray removeObjectAtIndex:30]; NSLog(@"Runtime Error section completed."); }
- 
然后,通过导航到 产品 | 运行 从 产品 菜单,或按 Command + R 键来构建和运行应用程序。 你会注意到一旦程序执行,就会抛出异常,代码执行停止。以下截图显示了 Xcode IDE 中的异常错误详细信息: ![如何做这件事...]() 
它是如何工作的...
在这个食谱中,我们创建了一个新的方法 causeRuntimeError,它分配了一个数组以容纳最大容量为 30 项。然后我们遍历循环,并将从 0 到 29 的整数值添加到这个数组中。一旦所有项目都已添加,循环退出,然后我们尝试从数组中移除第 31 个位置的项。
由于我们在数组中未存储第 31 个对象,这导致代码执行停止,如前一个截图所示,异常错误信息显示。
注意
如果你想要了解更多关于 NSMutableArray 类的信息,你可以参考以下链接中的 Apple 开发者文档:
更多...
Objective-C 提供了一个安全的方法,可以帮助你处理可能防止你的应用程序崩溃的异常错误。请记住,这仍然会导致错误,但异常错误将被你的应用程序处理,防止你的应用程序崩溃,并允许你的代码继续执行。
异常是中断程序正常执行流程的特殊条件。异常可能由硬件和软件的多种原因生成(通常说异常是被引发或抛出的)。
示例包括算术错误,如除以零、调用未定义的指令(如尝试调用未实现的方法)以及尝试访问超出范围的集合元素。
Objective-C 异常支持涉及四个编译器指令:@try、@catch、@throw和@finally,这些将在下表中解释:
| Objective-C 异常处理器 | 描述 | 
|---|---|
| @catch | 这个代码块包含在 @try {}块中抛出的异常的处理逻辑。你可以有多个@catch {}块来捕获不同类型的异常。 | 
| @throw | 这个编译器指令允许你抛出一个异常,本质上是一个 Objective-C 对象。你通常使用 NSException对象,但不是必须使用它。 | 
| @finally | 无论是否抛出异常,此代码块都必须执行,并在捕获语句完成后执行。 | 
让我们看看如何修改我们的causeRuntimeError方法以利用这些编译器指令,并防止我们的代码崩溃:
- 
修改 causeRuntimeError方法,如下面的代码片段中突出显示的代码部分所示:-(void)causeRuntimeError { NSLog(@"Runtime Error section started"); NSMutableArray *myArray = [NSMutableArray array]; for (int x = 0; x < 30; x++) { // Add the value to our Array [myArray addObject:[NSNumber numberWithInt:x]]; } // This will cause a Runtime error, as we don't have any // item at this index within the array. @try { [myArray removeObjectAtIndex:30]; } @catch (NSException *exception) { NSLog(@"Error: %@", exception); } @finally { NSLog(@"Runtime Error section completed."); } }
- 
接下来,通过导航到产品 | 运行从产品菜单,或者按Command + R来构建和运行应用程序。 
你会注意到一旦程序执行,异常就会被@catch代码块捕获,并将异常错误信息记录到控制台窗口,然后代码执行继续正常。
注意
如果你想了解更多关于 Objective-C 异常处理的信息,可以参考以下链接的 Apple 开发者文档:
参见
- 
运行和性能分析 iOS 项目配方 
- 
为我们的应用程序构建用户界面中的构建用户界面配方,获取和安装 iOS SDK 开发工具 
处理编译时错误
在这个配方中,我们将学习如何在 iOS 应用程序中处理编译时错误。
准备工作
在本节中,我们将了解编译时错误以及如何纠正这些错误。
如何做...
首先,按照以下简单步骤操作:
- 
打开 MemoryLeaks.xcodeproj项目文件。
- 
从项目导航器窗口打开 ViewController.m实现文件。按照以下代码片段中高亮显示的代码部分修改 viewDidLoad方法:- (void)viewDidLoad { [super viewDidLoad]; [self causeCompileTimeError]; }
- 
接下来,创建 causeCompileTimeError方法,如下面的代码片段所示:-(void)causeCompileTimeError { NSLog(@"Compile-time Error section started"); // This will cause a Compile-time error, as we don't have // a color called BlueColor self.view.backgroundColor = [UIColor BlueColor]; NSLog(@"Compile-time Error section completed."); }你会注意到编译器通知你找不到不存在的方法。下面的截图显示了 Xcode IDE 中的编译器错误详情。 ![如何做...]() 
它是如何工作的...
在本配方中,我们创建了一个新的方法causeCompiletimeError,将视图的背景色设置为蓝色。我们使用UIColor类来设置和指定我们希望视图背景色采用的颜色。
我们收到了一个编译器警告,告知我们使用了不存在的方法。这类错误最明显,通常这些错误来自打字错误,但可以使用 Xcode 中的代码自动完成轻松修复。
Objective-C 编译器是区分大小写的,这意味着UIcolor和UIColor被处理为不同的。例如,在 Objective-C 中,编译器可以理解以下代码行:
self.view.backgroundColor = [UIColor blueColor];
但是,如果你像我们在方法定义中做的那样输入以下内容,编译器会警告你一个潜在的(但不是确定的)问题,因为你指定了一个它无法识别的语言特定语法,因此没有与UIColor类关联:
self.view.backgroundColor = [UIColor BlueColor];
注意
如果你想要了解更多关于UIColor类的信息,你可以参考以下链接中的 Apple 开发者文档:
相关内容
- 
运行和剖析 iOS 项目的配方 
- 
第一章中“为我们的应用程序构建用户界面”的配方,获取和安装 iOS SDK 开发工具 
- 
第二章中“添加和自定义视图”的配方,用户界面 – 创建 UI 
添加和配置 Instruments
Instruments 应用程序包含一系列内置的 Instruments,可以帮助你更轻松地工作,通过它们从一个或多个进程中收集数据。大多数这些 Instruments 使用时配置很少,只需将它们添加到你的跟踪文档中即可开始收集跟踪数据。在本配方中,我们将探讨如何将 Instruments 添加和配置到现有的跟踪文档中。
准备工作
在本节中,我们将学习如何添加和配置 Instruments。
如何做...
要了解如何添加和配置 Instruments,请按照以下步骤操作:
- 
打开 MemoryLeaks.xcodeproj项目文件。
- 
从项目导航器窗口打开 ViewController.m实现文件。
- 
按照以下代码片段修改 viewDidLoad方法:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, // typically from a nib. NSLog(@"Starting...."); // Loop for 5000 times for (int i = 1; i <= 5000; i++){ NSString *status = [[NSString alloc]initWithString:@"Memory Leaking...."]; NSLog(@"Value of i: - %i and status - %@", i, status); } NSLog(@"Completed..."); }
- 
从产品菜单导航到产品 | 构建为 | 分析,或者使用键盘快捷键Shift + Command + I。 
- 
然后,从产品菜单中选择分析以启动仪器应用程序。同样,您可以使用键盘快捷键Command + I。 
- 
接下来,从内存部分下的可用模板列表中选择空白选项。 
- 
最后,点击分析按钮开始分析。 
Instruments 库列表显示您可以使用并添加到跟踪文档的所有仪器。库包含与 Xcode 4 安装一起提供的所有内置仪器,以及您已经创建的任何自定义仪器。
要打开仪器窗口,请按照以下简单步骤操作:
- 
在跟踪文档窗口中点击库按钮,或者从菜单栏导航到窗口 | 库。或者,您可以使用Command + L键盘快捷键。 
- 
接下来,从下拉列表中选择内存选项,如图下截图所示: ![如何操作...]() 
- 
要添加仪器,从仪器库列表中选择仪器,然后将其拖动到仪器窗格,或者直接拖动到跟踪文档的跟踪窗格,如图下截图所示: ![如何操作...]() 
- 
然后,点击记录按钮开始分析内存泄漏应用程序。 
- 
要配置仪器,从仪器窗格中选择仪器,然后点击位于仪器右侧的仪器检查器图标,如图下截图所示: ![如何操作...]() 
- 
要关闭检查员,点击由X标记的高亮关闭按钮。您同样可以使用Command + I键或导航到文件 | 获取信息来打开和关闭此窗口。 
工作原理...
在本菜谱中,我们学习了如何在跟踪文档中使用仪器检查器轻松添加和配置仪器。列出的选项根据正在配置的仪器类型而有所不同。大多数仪器包含配置跟踪窗格内容的选项,而只有少数仪器包含确定仪器收集的信息类型的附加功能。
根据正在配置的仪器类型,它们可以在跟踪文档中的数据记录之前、期间或之后进行配置。缩放控件可以在您配置的大多数仪器检查器控件中找到。此功能控制显示在跟踪窗格中的跟踪数据的放大倍数,并调整仪器在跟踪窗格中的高度。
参见
- 运行和分析 iOS 项目菜谱
第五章:使用位置服务和 MapKit 框架
在本章中,我们将涵盖:
- 
添加 CoreLocation 和 MapKit 框架 
- 
构建简单的 CoreLocation 应用程序 
- 
确定当前 GPS 位置 
- 
添加和使用 MapView 控件 
- 
向地图添加叠加区域 
- 
向地图添加注释占位符 
- 
反向地理编码地址信息 
- 
使用不同的地图类型 
简介
在本章中,我们将更深入地探讨如何使用 CoreLocation 和 MapKit 框架来确定用户的位置。MapKit 框架基于 Google Earth 和 Google Maps 数据,以及为开发者提供简单机制将详细和交互式地图功能集成到他们自己的应用程序的 API。
MapKit 框架的核心元素是MKMapView类。这个类是UIView的子类,它提供了一个画布,用于向用户展示地图和/或卫星信息。我们还将学习如何直接在地图上添加占位符并为这些占位符创建注释。
最后,我们将学习如何使用CLGeoLocation类来应用地图坐标的反地理编码以显示位置信息,以及应用不同类型的地图视图。
添加 CoreLocation 和 MapKit 框架
在本菜谱中,我们将学习如何将 CoreLocation 和 MapKit 框架包含到我们的项目中。
准备工作
在本节中,我们将学习如何创建一个新的应用程序并将框架添加到我们的项目中。
如何做...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从 /Xcode4/Applications文件夹启动 Xcode。
- 
选择创建新的 Xcode 项目,或转到文件 | 新建项目。 
- 
从可用模板列表中选择单视图应用程序模板。 
- 
点击下一步按钮,继续向导的下一步。 
- 
接下来,输入 CoreLocation作为您项目的名称。
- 
从设备下拉列表中选择iPhone。 
- 
确保未选中使用故事板复选框。 
- 
确保选中使用自动引用计数复选框。 
- 
确保未勾选包含单元测试复选框。 
- 
点击下一步按钮,继续向导的下一步。 
- 
指定您希望保存项目的位置。 
- 
然后,点击创建按钮继续并显示 Xcode 工作空间。 
我们下一步是向项目中添加 CoreLocation 框架。这可以通过执行以下简单步骤实现:
- 
在项目导航器部分选择CoreLocation项目。 
- 
然后,从TARGETS组下选择您的项目。 
- 
选择构建阶段标签并展开链接二进制与库展开箭头。 
- 
点击+按钮,并从列表中选择CoreLocation.framework。 
- 
然后,点击 Add 按钮将框架添加到您的项目中,如图所示: ![如何操作...]() 
- 
重复步骤 4 和 5,将 MapKit.framework添加到您的项目中。注意如果您找不到所需的框架,还可以直接从可用的框架列表中搜索此框架。 
工作原理...
在本食谱中,我们学习了如何将 CoreLocation 和 MapKit 框架添加到我们的项目中,以便我们可以使用这些来确定我们的当前位置并处理地理编码信息以向地图添加占位符。
参见
- 构建 CoreLocation 简单应用程序 食谱
构建 CoreLocation 简单应用程序
在本食谱中,我们将学习如何构建我们的 Core Location 项目的用户界面。
准备工作
在本节中,我们将首先构建将构成我们 CoreLocation 应用程序用户界面的组件。
如何操作...
首先,按照以下顺序遵循以下简单步骤:
- 
从 Project Navigator 部分选择 ViewController.xib文件。
- 
从 Object Library 中选择并拖动一个 (UIToolbar) 工具栏控件,并将其添加到视图的顶部,并选择我们之前添加的工具栏中的 Item 按钮。 
- 
从 Attributes Inspector 部分更改 Title 的值为 Change Map。 
- 
接下来,从 Object Library 中选择并拖动一个 (MKMapView) 地图视图控件到 View Controller 的中心,并调整 Map View 部分的尺寸以填充整个屏幕: ![如何操作...]() 
以下截图显示了添加了 UIToolbar 和按钮以及 MapView 控件的完成用户界面:

我们的下一步是创建 Change Map 按钮 和 MKMapView 控件的 Outlet 和 property 事件。创建这些将允许我们在代码中直接访问相关方法和控制属性。要创建 Outlet 事件,请按照以下简单步骤操作:
- 
通过选择 Navigate | Open in Assistant Editor 或按住 Option + Command 键来打开 Assistant Editor。 
- 
确保在 Assistant Editor 窗口中显示 ViewController.h接口文件。
- 
选择 Change Map ( UIBarButtonItem) 控件,按住 Ctrl 键,并将其拖入ViewController.h接口文件。注意为了创建 IBOutlet连接,这些需要在@interface指令下的花括号内创建,因为这些不是默认创建的。
- 
从 Connection 下拉菜单中选择 Outlet 以创建连接。 
- 
输入 changeMap作为要创建的 Outlet 事件的名称。
- 
从 Storage 下拉菜单中选择 Strong。 
- 
重复步骤 4 到 6,为 MKMapView控件创建Outlet属性,使用名称mapView。
现在我们已经为我们的控件创建了实例变量出口,我们需要为changeMap按钮创建关联的动作。创建这些动作允许在按钮按下时触发事件。要创建一个动作,请按照以下简单步骤操作:
- 
在辅助编辑器窗口中仍然显示 ViewController.h接口文件的情况下,选择更改地图(UIBarButtonItem)控件,并按住Ctrl键将其拖入ViewController.h接口文件。
- 
从连接下拉列表中选择动作以创建连接。 
- 
在名称中输入 changeMapType以创建方法。为了使我们的应用程序正确地在视图中显示地图,我们需要导入 <MapKit/MapKit.h>接口头文件,以便我们可以利用其方法。
- 
打开位于 Classes文件夹中的ViewController.h接口文件,并添加以下突出显示的代码:// ViewController.h // CoreLocation // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <MapKit/MapKit.h>
以下代码片段显示了完成的ViewController.h接口文件,如下所示:
//  ViewController.h
//  CoreLocation
//  Created by Steven F Daniel on 23/10/12.
//  Copyright (c) 2012 GenieSoft Studios. All rights reserved.
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface ViewController : UIViewController 
{
  IBOutlet UIBarButtonItem *changeMap;
}
@property (strong, nonatomic) IBOutlet MKMapView *mapView;
// Declare our class instance methods
-(IBAction)changeMapType:(id)sender;
@end
它是如何工作的...
在这个菜谱中,我们首先通过向视图控制器添加UIToolbar和MKMapView控件来构建用户界面。然后,我们创建了在按下changeMapType按钮时需要执行必要的出口和动作事件方法。然后,我们将MapKit.h接口文件导入到ViewController.h接口文件中,以便我们可以访问MKMapView类的类方法。
在我们的下一个菜谱中,我们将探讨如何使用MKMapView类和CLLocationManager类来确定我们的当前地理 GPS 位置。
参见
- 
确定当前 GPS 位置菜谱 
- 
显示和使用地图菜谱 
- 
在第一章的使用 Xcode 创建 iOS 项目和创建到 Interface Builder 对象的出口菜谱中,获取和安装 iOS SDK 开发工具 
确定当前 GPS 位置
在这个菜谱中,我们将学习如何确定我们 iOS 设备的当前 GPS 位置。
准备工作
在本节中,我们将看到如何使用 CoreLocation 框架确定我们的纬度和经度 GPS 位置,并在alertView对话框中输出此信息。
如何操作...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器部分打开 ViewController.h接口文件。
- 
接下来,修改接口文件,如下面的代码片段中突出显示的代码部分所示: // ViewController.h // CoreLocation // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> @interface ViewController : UIViewController < MKMapViewDelegate, CLLocationManagerDelegate> { CLLocationManager *locationManager; IBOutlet UIBarButtonItem *changeMap; BOOL foundLocation; // Obtain the user's current Longitude/Latitude float curLat; float curLong; } @property (strong, nonatomic) IBOutlet MKMapView *mapView; @property float curLat; @property float curLong; // Declare our class instance methods -(IBAction)changeMapType:(id)sender; @end
- 
从项目导航器部分打开 ViewController.m实现文件,并在@implementation指令语句下添加以下突出显示的代码语句:// // ViewController.m // CoreLocation // // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. // #import "ViewController.h" @implementation ViewController @synthesize mapView, curLat, curLong;
- 
接下来,修改 viewDidLoad方法,如下面的代码片段中突出显示的代码部分所示:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, // typically from a nib. foundLocation= NO; // Make this controller the delegate for the map view. self.mapView.delegate = self; // Instantiate a new location object. locationManager = [[CLLocationManager alloc] init]; // Make this controller the delegate for the location // manager. [locationManager setDelegate:self]; // Set some parameters for the location object. [locationManager setDistanceFilter:kCLDistanceFilterNone]; [locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; // Determine the current geographic location coordinates // of the user on the map [locationManager startUpdatingLocation]; }
- 
接下来,创建 didUpdateToLocation:方法,如下面的代码片段所示:#pragma mark Work out the current user location on the map - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { // If this is the first launch of the app, then set the // center point of the map to the user's location. if (!foundLocation) { // Determine the current geographic location coordinates self.curLat = mapView.userLocation.coordinate.latitude; self.curLong = mapView.userLocation.coordinate.longitude; // Show the current user location within an Alert dialog UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"You are located at" message:[NSString stringWithFormat:@"Latitude: %f and Longitude: %f", self.curLat, self.curLong] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; // Display the alert view dialog [alert show]; // Reset so that this does not get called again. foundLocation = YES; } } #pragma mark If an error occurred, we need to tell our location Manager to stop updating location - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { [locationManager stopUpdatingLocation]; }
- 
然后,通过选择产品 | 运行从产品菜单,或者按Command + R来构建和运行应用程序。 
您会注意到一旦程序执行,核心位置将与 iOS 设备通信以确定您的位置,并在UIAlertview对话框中显示此信息。
工作原理...
在这个菜谱中,我们导入了MapView.h头文件信息到我们的CoreLocation.h接口文件,以便我们可以访问其类方法。然后我们需要扩展我们的类以包含CLLocationManagerDelegate的类协议,这样我们就可以访问协议的相关方法。然后初始化我们超类的继承成员,并将foundLocation设置为NO第一次运行,并将视图控制器设置为mapView的代理,这样我们就可以在地图移动时传递通知。
接下来,我们实例化locationManager类,并将其代理属性设置为我们的mapView,并将locationManager类对象的desiredAccuracy属性设置为kCLLocationAccuracyBest,这指定了locationManager类提供的位置和航向信息应尽可能精确,作为一个测试,最好在实际的 iOS 设备上进行测试。
最后,我们声明了didUpdateToLocation方法来处理CLLocationManger类更改 iOS 设备当前位置的情况。从这里,我们然后获取当前的纬度和经度坐标,并在UIAlertview对话框中显示这些信息,最后将foundLocation变量设置为YES。
注意
关于CLLocationManager类的更多信息,您可以参考以下 URL 的 Apple 开发者文档:
还有更多...
我们实现了locationManager:didFailWithError类,该类在无法使用位置服务或无法立即检索位置时会被调用。我们使用error属性来确定发生的错误类型,然后调用locationManager对象的stopUpdatingLocation方法。
以下表格显示了每个有效的错误代码及其描述,这些描述由locationManager:didFailWithError方法返回:
| 核心位置错误代码 | 位置管理器错误描述 | 
|---|---|
| kCLErrorLocationUnknown | 此错误告诉您位置管理器目前无法获取位置。 | 
| kCLErrorDenied | 此错误通知您用户拒绝了位置服务的访问。 | 
| kCLErrorNetwork | 此错误告诉您网络不可用或发生了网络错误。 | 
| kCLErrorHeadingFailure | 此错误告诉您无法确定航向位置。 | 
| kCLErrorRegionMonitoringDenied | 此错误告诉您用户拒绝了区域监控服务的访问。 | 
| kCLErrorRegionMonitoringFailure | 此错误告诉您无法监控已注册的区域。 | 
| kCLErrorRegionMonitoringSetupDelayed | 此错误告诉您核心位置无法立即初始化区域监控服务。 | 
注意
关于核心位置类didFailWithError错误代码的更多信息,您可以参考位于developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/CLLocationManagerDelegate/CLLocationManagerDelegate.html的 Apple 开发者文档。
相关内容
- 
添加和使用 MapView 控件配方 
- 
反转地理编码地址信息配方 
添加和使用 MapView 控件
在本配方中,我们将学习如何使用位置模拟器来提供您在应用程序中测试基于位置的功能的能力,而无需离开您的办公桌。
准备工作
在本节中,我们将了解如何使用位置模拟器让您在 iOS 模拟器中选择预设的位置和路线,并选择具有精度的自定义纬度和经度。
如何操作...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从项目导航部分打开 ViewController.m实现文件。
- 
接下来,修改如下代码片段中突出显示的 viewDidLoad方法:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, // typically from a nib. foundLocation = NO; // Ensure that you can view your own location // in the map view. [self.mapView setShowsUserLocation:YES]; // Instantiate a location object. locationManager = [[CLLocationManager alloc] init]; // Make this controller the delegate for // the location manager. [locationManager setDelegate:self]; // Set some parameters for the location object. [locationManager setDistanceFilter:kCLDistanceFilterNone]; [locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; // Determine the current geographic location // coordinates of the user on the map [locationManager startUpdatingLocation]; }
- 
然后,通过从产品菜单选择运行或按Command + R来构建并运行应用程序。 
您会注意到,一旦程序执行,核心位置会与 iOS 设备通信以确定您的位置,并使用蓝色闪烁标记显示此信息。
您还可以选择在 iOS 设备上运行应用程序时使用 Xcode 导航到不同的位置。为此,请按照以下简单步骤操作:
- 
从 Xcode 调试控制台,点击如下截图所示的模拟位置图标。这将显示一个可用位置的列表: ![如何操作...]() 
- 
从显示的位置列表中选择中国香港或类似选项。 
- 
然后,iOS 设备将更新以反映所选位置。您需要放大并手动滚动到该位置以查看更改,如下截图所示: ![如何操作...]() 
它是如何工作的...
在本食谱中,我们学习了如何通过使用地图视图控制器的 setShowsUserLocation 属性来设置 MapView 控制器以在地图上显示我们的当前位置,以及如何使用 Xcode 允许我们的 iOS 设备通过使用 模拟位置 功能来模拟不同的位置。
参见
- 
确定当前 GPS 位置 食谱 
- 
添加覆盖区域到地图 食谱 
添加覆盖区域到地图
在本食谱中,我们将学习如何创建一个覆盖物来标记我们的地图视图控制器对象上的一个区域。这些覆盖对象基本上是包含表示地图区域所需地理数据的对象。例如,覆盖物可以采用常见的形状,如矩形和圆形。
准备工作
在本节中,我们将了解如何使用 MKOverlayView 类和 MKOverlay 协议来允许我们在地图上表示一个点和一个区域。
如何操作...
首先,按照以下顺序遵循以下简单步骤:
- 
从 项目导航器 部分打开 ViewController.m实现文件。
- 
接下来,创建如以下代码片段所示的 overlayCurrentUserLocation函数:#pragma mark draws an overlay showing the current user's location -(void)createOverlayArea { // Show the current user location with 100km radius MKCircle *circle = [MKCircle circleWithCenterCoordinate:mapView.userLocation.coordinate radius:100]; circle.title = @"Current Location"; [self.mapView addOverlay:circle]; // Zoom into the current user's location using a 2 km span MKCoordinateRegion mapRegion; mapRegion.center = mapView.userLocation.coordinate; mapRegion.span = MKCoordinateSpanMake(0.2, 0.2); [mapView setRegion:mapRegion animated: YES]; }
- 
接下来,创建如以下代码片段所示的 mapView:viewForOverlay:方法:-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay { MKCircleView* circleView = [[MKCircleView alloc] initWithOverlay:overlay]; circleView.strokeColor = [UIColor redColor]; circleView.lineWidth = 1.0; circleView.fillColor = [UIColor colorWithRed:0.0 green:255.0 blue:0.0 alpha:0.1]; return circleView; }
- 
修改 locationManager:didUpdateToLocation:方法,并输入以下突出显示的代码部分,如代码片段所示:#pragma mark Work out the current user location on the map - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { // If this is the first launch of the app, then set the // center point of the map to the user's location. if (!foundLocation) { // Determine the current geographic location coordinates self.curLat = mapView.userLocation.coordinate.latitude; self.curLong = mapView.userLocation.coordinate.longitude; // Show the current user location within an Alert dialog UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"You are located at" message:[NSString stringWithFormat:@"Latitude: %f and Longitude: %f", self.curLat, self.curLong] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; // Display the alert view dialog [alert show]; // Display our overlaid area on the map [self createOverlayArea]; // Reset so that this does not get called again. foundLocation = YES; } }
- 
然后,通过从 产品 菜单中选择 产品 | 运行 或按 Command + R 键来构建和运行应用程序。 
你会注意到一旦程序执行,核心位置服务会与 iOS 设备通信以确定你的位置,然后放大并显示一个圆圈,显示我们指定的区域的半径:

它是如何工作的...
在本食谱中,我们创建了一个名为 createOverlayArea 的函数,该函数基于当前用户位置创建了一个半径为 100 公里的圆圈,然后使用 MKCircle 类的标题属性为该位置创建了一个标题,并使用 addOverlay 方法将我们的圆覆盖并添加到 mapView 控制器中。
在我们的下一步中,我们使用 MKCoordinateRegion 创建了一个 2 公里的区域范围,然后使用 setRegion 设置器方法放大到由半径分配的区域。然后我们创建了 mapView:viewForOverlay: 方法,该方法在对象被覆盖到地图视图控制器上时被调用,并使用 MKCircleView 类填充覆盖区域的圆圈,然后通过设置圆的 strokeColor、lineWidth 和 fillColor 属性来设置圆的外观和感觉。在我们的最后部分,我们修改了 locationManager:didUpdateToLocation: 方法,以包含对 ceateOverlayArea 函数的调用。
注意
关于 MKOverlayView 类的更多信息,你可以参考位于以下 URL 的 Apple 开发者文档:
参见
- 
向地图添加注释占位符配方 
- 
反转地理编码地址信息配方 
向地图添加注释占位符
在本配方中,我们将学习如何创建我们自己的自定义类,这将使我们能够向地图视图控制添加注释。
准备工作
在本节中,我们将了解如何使用MKAnnotation和MKAnnotationView类,使我们能够根据地理纬度和经度值应用一系列基于地理的注释标记到我们的mapView控制。
如何做到这一点…
要开始,请按照以下顺序遵循以下简单步骤:
- 
选择CoreLocation组,选择文件 | 新建 | 文件…或按Command + N。 
- 
从可用模板列表中选择 Objective-C类模板。
- 
点击下一步按钮继续向导的下一步。 
- 
输入 MapViewAnnotation作为要创建的类的名称。
- 
确保您已从子类为下拉列表中选择NSObject作为要创建的子类的类型: ![如何做到这一点…]() 
- 
点击下一步按钮继续向导的下一步。 
- 
然后,点击创建按钮将文件保存到指定的文件夹位置。 
我们下一步是实现此类使用的功能和方法。
- 
从项目导航器部分打开 MapViewAnnotation.h接口文件,并输入以下代码片段:// MapViewAnnotation.h // CoreLocation // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface MapViewAnnotation : NSObject <MKAnnotation> { NSString *title; NSString *subtitle; CLLocationCoordinate2D coordinate; } @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *subtitle; @property (nonatomic, readonly) CLLocationCoordinate2D coordinate; - (id)initWithTitle:(NSString *)header subtitle:(NSString *)subtitle andCoordinate:(CLLocationCoordinate2D)coord2D; @end
- 
从项目导航器部分打开 MapViewAnnotation.m实现文件,并输入以下代码片段:// MapViewAnnotation.m // CoreLocation // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "MapViewAnnotation.h" @implementation MapViewAnnotation @synthesize title, subtitle, coordinate; - (id)initWithTitle:(NSString *)header subtitle:(NSString *)subtitles andCoordinate:(CLLocationCoordinate2D)coord2D { title = header; subtitle = subtitles; coordinate = coord2D; return self; } @end
- 
接下来,从项目导航器部分打开 ViewController.m实现文件,并输入以下代码片段中的高亮代码部分:// ViewController.m // CoreLocation // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "ViewController.h" #import "MapViewAnnotation.h" @interface ViewController () @end @implementation ViewController @synthesize mapView, curLat, curLong;
- 
接下来,创建如以下代码片段所示的 plotGeographicalData方法:#pragma mark Plots the geographic locations onto the Map -(void)plotGeographicalData { // Remove any existing custom annotations but not the user // location blue dot. for (id <MKAnnotation> annotation in mapView.annotations) { if ([annotation isKindOfClass:[MapViewAnnotation class]]) { [mapView removeAnnotation:annotation]; } } // Add the annotation to our map view NSString *pinName; MapViewAnnotation *newAnnotation; pinName = @"Kuala Lumpur"; newAnnotation = [[MapViewAnnotation alloc] initWithTitle:pinName subtitle:@"Malaysia" andCoordinate: CLLocationCoordinate2DMake(3.13900,101.68685)]; [self.mapView addAnnotation:newAnnotation]; // Add the annotation to our map view pinName = @"Western Australia"; newAnnotation = [[MapViewAnnotation alloc] initWithTitle:pinName subtitle:@"Perth, WA" andCoordinate: CLLocationCoordinate2DMake(-31.93285, 115.86194)]; [self.mapView addAnnotation:newAnnotation]; // Zoom in so that all pins fit within the map region [self zoomToFitMapAnnotations]; }
- 
然后,创建如以下代码片段所示的 mapView:viewForAnnotation:方法:#pragma mark sets up the properties for each of the annotations on the map -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { // Define the identifier for the MapViewAnnotation Class. static NSString *identifier = @"MapViewAnnotation"; // Place the current user location onto the Map at the // location determined using the MKUserLocation class. if ([annotation isKindOfClass:[MKUserLocation class]]) { MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; if (annotationView == nil) { annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier]; } else { annotationView.annotation = annotation; } annotationView.enabled = YES; annotationView.canShowCallout = YES; annotationView.animatesDrop = NO; annotationView.pinColor = MKPinAnnotationColorGreen; return annotationView; } return nil; }
- 
然后,通过选择产品 | 运行从产品菜单或按Command + R来构建和运行应用程序。 
它是如何工作的...
在本配方中,我们创建了一个新的自定义MapViewAnnotation类,然后扩展此类以使用MKAnnotation协议,这样我们就可以访问其关联的类方法。接下来,我们在MapViewAnnotation.m实现文件中创建了一个名为initWithTitle:的方法,用于存储我们的标题和坐标以放置标记。
在下一步中,我们修改了ViewController.m实现文件,并创建了一个名为plotGeographicalData的新函数,该函数用于使用CLLocationCoordinate2DMake创建数组中每个地理位置的纬度和经度值,以创建多个不同的地理位置。
然后,我们将地理坐标传递给MapViewAnnotation类的addAnnotation方法,以将注释添加到地图上。最后,我们通过使用我们的MapViewAnnotation类实例创建mapView:viewForAnnotation方法,将注释对象放置到地图上。
注意
想要了解更多关于MKAnnotationView类的信息,您可以参考以下网址的 Apple 开发者文档:
developer.apple.com/library/ios/#documentation/MapKit/Reference/MKAnnotationView_Class/Reference/Reference.html#//apple_ref/occ/cl/MKAnnotationView
还有更多…
为了确保地图注释发生,我们需要修改locationManager:didUpdateToLocation方法。该方法在 iOS 设备在地图上找到用户的当前位置或地图位置发生变化后调用。
- 
修改 locationManager:didUpdateToLocation:方法,并输入以下高亮显示的代码部分,如代码片段所示:#pragma mark Work out the current user location on the map - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { // If this is the first launch of the app, then set the // center point of the map to the user's location. if (!foundLocation) { // Determine the current geographic location coordinates self.curLat = mapView.userLocation.coordinate.latitude; self.curLong = mapView.userLocation.coordinate.longitude; // Show the current user location within an Alert dialog UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"You are located at" message:[NSString stringWithFormat:@"Latitude: %f and Longitude: %f", self.curLat, self.curLong] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; // Display the alert view dialog [alert show]; // Display our overlaid area on the map [self createOverlayArea]; // Plot the geographic positions on the map [self plotGeographicalData]; // Reset so that this does not get called again. foundLocation = YES; } }
- 
接下来,创建 zoomToFitMapAnnotations函数,如以下代码片段所示:#pragma mark Zooms in to the map so that all visible annotations are displayed - (void)zoomToFitMapAnnotations { if ([self.mapView.annotations count] == 0) return; int i = 0; MKMapPoint points[[self.mapView.annotations count]]; // Cycle through all annotations on the map and // build array of annotation points for (id<MKAnnotation> annotation in [self.mapView annotations]) { points[i++] = MKMapPointForCoordinate(annotation.coordinate); } // Create a rectangle view around the visible region // that the user can move around in. MKPolygon *pv= [MKPolygon polygonWithPoints:points count:i]; [self.mapView setRegion:MKCoordinateRegionForMapRect([pv boundingMapRect]) animated:YES]; }在之前的代码片段中,使用 zoomToFitMapAnnotations方法创建了一个围绕可见区域的可滚动区域,用户可以在其中移动。这是通过使用MKPolygon类创建围绕地图上每个注释的一系列点来实现的。
- 
然后,通过从产品菜单中选择产品 | 运行,或通过按Command + R来构建和运行应用程序。 ![还有更多…]() 注意想要了解更多关于 MKPolygon类的信息,您可以参考以下网址的 Apple 开发者文档:
参考信息
- 
向地图添加覆盖区域教程 
- 
反向地理编码地址信息教程 
反向地理编码地址信息
在本教程中,我们将学习如何将地址信息反向地理编码成可读信息。
准备工作
在本节中,我们将了解“反向地理编码”这个短语,以及如何将经纬度信息转换为人类可识别的地址信息。
如何操作...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器部分打开 ViewController.m实现文件。
- 
修改 viewDidLoad方法,如以下代码片段中高亮显示的代码部分所示:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, // typically from a nib. foundLocation= NO; // Make this controller the delegate for the map view. self.mapView.delegate = self; // Ensure that you can view your own location // in the map view. [self.mapView setShowsUserLocation:YES]; // Instantiate a new location object. locationManager = [[CLLocationManager alloc] init]; // Make this controller the delegate for the location // manager. [locationManager setDelegate:self]; // Set some parameters for the location object. [locationManager setDistanceFilter:kCLDistanceFilterNone]; [locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; // Determine the current geographic location coordinates // of the user on the map [locationManager startUpdatingLocation]; [self getGeocodingInformation]; }
- 
接下来,创建 getGeocodingInformation方法,如以下代码片段所示:#pragma mark Reverse Geocoding location details -(void)getGeocodingInformation { // Declare the coordinates to reverse geocode CLGeocoder *geoCoder = [[CLGeocoder alloc] init]; CLLocationCoordinate2D placeCoord = CLLocationCoordinate2DMake(3.13900, 101.68685); CLLocation *location = [[CLLocation alloc] initWithLatitude:placeCoord.latitude longitude:placeCoord.longitude]; [geoCoder reverseGeocodeLocation:location completionHandler: ^(NSArray *placemarks, NSError *error) { // Get nearby address and print it out to the Console CLPlacemark *placemark = [placemarks objectAtIndex:0]; // Show the placemark information within an Alert dialog UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Placemark Information" message:[placemark.addressDictionary description] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; // Display the alert view dialog [alert show]; }]; return; }
- 
然后,通过从产品菜单中选择产品 | 运行,或通过按Command + R来构建和运行应用程序。 
你会注意到一旦程序执行,提供的纬度和经度信息已经被转换成人类可识别的地址信息。

它是如何工作的...
在这个菜谱中,我们在viewDidLoad方法中添加了一个新的方法调用,getGeocodingInformation,它将负责在UIAlertView对话框窗口中显示我们的地理位置信息。然后我们创建了一个新的对象变量geoCoder,它是CLGeoCoder类,然后传递一组纬度和经度值到CLLocation类,然后它被传递到CLGeoCode类的reverseGeocodeLocation方法以确定坐标的地理位置细节。
一旦reverseGeocodeLocation方法做出判断,结果将返回到placemarks数组对象中,该对象包含实际数据。最后,我们调用CLPlacemark类,使用placemark对象的addressDictionary检索位置信息。
下面的表格简要描述了CLPlacemark类返回的一些不同类型的信息:
| 地标属性 | 描述 | 
|---|---|
| Name | 这个属性包含地标的名称。 | 
| Country | 这个属性是自解释的,只是简单地存储国家名称。 | 
| CountryCode | 这个属性为你提供了国家缩写名称。 | 
| Thoroughfare | 这包含与地标相关的街道地址。 | 
| Sublocality | 这个属性为你提供了地标的城市级别额外信息。 | 
| SubAdministrativeArea | 这个属性为你提供了地标额外的信息。 | 
| ZIP | 这通常是地标的邮政编码。 | 
除了之前显示的这些属性之外,地址字典为你提供了一个FormattedAddressLines条目,它存储了有关地址的预格式化字符串数组。
你可以使用这些字符串来显示地址,例如:“Taman Batu Metropolitan, Jalan Damansara, 50470 Kuala Lumpur KL, Malaysia”,下面的代码片段展示了如何实现这一点:
NSLog(@"Location: %@",[[placemark.addressDictionary valueForKey:@"FormattedAddressLines"] componentsJoinedByString:@", "]);
还有更多…
一个CLPlacemark对象存储了与给定纬度和经度相关的地标数据,并包含国家、州、城市以及与指定坐标相关的街道地址信息。此类还包括兴趣点和地理相关数据。地标通常是通过CLGeocoder对象生成的。
注意
关于CLGeoCoder和CLPlacemark类的更多信息,你可以参考位于以下 URL 的 Apple 开发者文档:
- 
developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLPlacemark_class/Reference/Reference.html#//apple_ref/occ/cl/CLPlacemark
参见
- 确定当前 GPS 位置配方
使用不同的地图类型
在本配方中,我们将学习如何在不同的地图视图之间切换。
准备工作
在本节中,我们将开始实现一个方法,该方法将负责处理 MapKit 框架提供的不同地图视图的更改。
如何做...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从项目导航器部分打开 ViewController.h接口文件。
- 
接下来,修改接口文件,如下面的代码片段中突出显示的代码部分所示: // ViewController.h // CoreLocation // Created by Steven F Daniel on 23/10/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> @interface ViewController : UIViewController <UIActionSheetDelegate, MKMapViewDelegate, CLLocationManagerDelegate> { CLLocationManager *locationManager; IBOutlet UIBarButtonItem *changeMap; BOOL foundLocation; // Obtain the user's current Longitude/Latitude float curlatitude; float curlongitude; } @property (strong, nonatomic) IBOutlet MKMapView *mapView; @property float curlatitude; @property float curlongitude; // Declare our class instance methods -(IBAction)changeMapType:(id)sender; @end
- 
从项目导航器部分打开 ViewController.m实现文件。
- 
接下来,修改 changeMapType:方法,如下面的代码片段所示:// Called when the user presses the Map Type button - (IBAction)changeMapType:(id)sender{ // Define an instance of our action sheet UIActionSheet *actionSheet; // Initialize our action sheet with the // differen't mapping types actionSheet = [[UIActionSheet alloc] initWithTitle:@"Select a Map Type from the list below" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Close" otherButtonTitles:@"Map View",@"Satellite View", @"Hybrid View", nil]; // Set our ActionSheet style and display it to the user actionSheet.actionSheetStyle = UIBarStyleBlackTranslucent; [actionSheet showInView:self.view]; }
- 
接下来,创建以下 clickedButtonAtIndex:方法,如下面的代码片段所示:// Delegate that handles the chosen action sheet options -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { // Determine the chosen item switch (buttonIndex) { case 1: mapView.mapType = MKMapTypeStandard; break; case 2: mapView.mapType = MKMapTypeSatellite; break; case 3: mapView.mapType = MKMapTypeHybrid; break; default: break; // Catch the Close button and exit. } }
- 
通过选择产品菜单中的运行或按Command + R来构建和运行应用程序。 
工作原理...
在本配方中,我们首先扩展了我们的接口文件类,以便我们可以包含我们的UIActionSheetDelegate协议,这样我们就可以访问相应的方法。然后我们声明并实例化了一个基于UIActionSheet类的actionSheet对象,然后初始化我们的actionSheet对象以显示可选择的不同的地图类型。然后我们使用UIActionSheet类的actionSheetStyle属性设置actionSheet的样式,然后使用showInView:self.view方法将actionSheet显示在当前视图中。
在下一部分中,我们声明了一个代理方法,用于从actionSheet对象中确定被按下的按钮,使用acionSheet属性的clickedButtonIndex方法。最后,我们检查buttonIndex变量的值以确定被按下的按钮索引。在使用buttonIndex变量时,请注意,起始值始终为 0。
下表简要描述了每种不同的地图视图:
| 地图类型 | 描述 | 
|---|---|
| MKMapTypeStandard | 这是默认的地图类型,如果没有指定,则显示。此设置将显示包含街道和道路名称的正常地图。 | 
| MKMapTypeSatellite | 此类型的地图将显示卫星视图信息。 | 
| MKMapTypeHybrid | 此类型的地图将显示卫星视图与叠加在地图上的道路和街道信息的组合。 | 
以下截图显示了在 iOS 设备上运行的 CoreLocation 应用程序,显示了当前的位置以及绘制到地图上的不同地理位置,以及当按下地图类型按钮时可选的地图类型。

注意
关于MapKit类的更多信息,您可以参考以下 URL 处的 Apple 开发者文档:
参见
- 
将注释占位符添加到地图中的配方 
- 
确定当前 GPS 位置的配方 
第六章:在云中存储文档
在本章中,我们将涵盖:
- 
在 iCloud 中存储和使用文档 
- 
使用 iCloud 存储 API 进行操作 
- 
使用 iCloud 文档存储 
- 
在 iCloud 中存储键值数据 
- 
在 iCloud 中检测文件版本冲突 
- 
构建 iCloud应用程序
- 
请求 iCloud 存储权限 
- 
配置 iOS 设备以使用 iCloud 
简介
iCloudExample 应用程序允许您为日常需求创建简单的提醒的视觉记录。该应用程序记录这些信息,然后使用iCloud 存储 API将这些信息添加到您的 iCloud 账户存储库中。
在本章中,我们将探讨 iCloud 的功能和存储 API,并了解如何将这些功能集成到我们的应用程序中,以便它可以与 iCloud 服务器交互,读取、写入和编辑文档,并为我们提供从所有 iOS 设备访问这些项的能力,无需同步或传输这些文件。
在用户的 iCloud 账户中存储文档为我们提供了额外的安全层,因此即使用户丢失了设备,这些文档也可以轻松检索,并提供了在设备之间同步数据的能力,前提是它们包含在 iCloud 存储中。
在 iCloud 中存储和使用文档
在本食谱中,我们将了解可以用于在 iCloud 中存储和使用文档的不同方法。
准备工作
在本节中,我们将了解文件协调器和文件展示类,以及如何将UIDocument类注册为在 iCloud 数据更新时接收更新。
如何操作...
以下示例展示了如何使用NSFileCoordinator类:
myDocument = [[myDocument alloc] initWithFileURL:ubiquityURL];
myDocument.delegate = self;
coordinator = [[NSFileCoordinator alloc]    
               initWithFilePresenter:myDocument];    
               [NSFileCoordinator addFilePresenter:myDocument];
文件协调器的任务是协调应用程序和同一文档上的同步****守护进程执行的读取和写入操作。例如,您的应用程序和守护进程可能同时读取文档,但任何时候只能有一个写入文件。
注意
关于NSFileCoordinator类的更多信息,您可以参考以下链接位置的 Apple 开发者文档:developer.apple.com/library/mac/#documentation/Foundation/Reference/NSFileCoordinator_class/Reference/Reference.html
以下示例展示了如何将文档从本地存储移动到 iCloud:
if (![[NSFileManager defaultManager] setUbiquitous:YES
    itemAtURL:localURL destinationURL:ubiquityURL error:&error]){
    NSLog(@"Error making local file ubiquitous. %@",    
           [error localizedFailureReason]);    
    return;    
}
注意
关于NSFilePresenter协议参考的更多信息,您可以参考以下链接位置的 Apple 开发者文档:developer.apple.com/library/mac/#documentation/Foundation/Reference/NSFilePresenter_protocol/Reference/Reference.html
如以下截图所示,它显示了在一个设备上做出更改的过程,并在将更改推回到 iCloud 服务之前将其存储在本地,使用本地守护进程。当我们查看本章的示例应用程序时,我们将了解 NSFileCoordinator 类和云存储文档。

注意
当你的应用程序将文档存储到 iCloud 时,它必须指定一个或多个容器,这些容器将存储文档内容,通过在你的应用程序的 entitlements 文件中包含 com.apple.developer.ubiquity-container-identifiers 键值条目来实现。
它是如何工作的...
在这个菜谱中,我们学习了如何将 UIDocument 文档作为一个文件展示者来建立,这样你就可以通过告诉 NSFileCoordinator 将文档添加为 展示者 来注册这个类,以便在它的云数据更新时接收更新。这意味着展示者类是对知道给定文件外部变化发生有强烈兴趣的类。
每次你注册更改时,你首先创建一个文档和一个协调器,然后将文档作为其表演者初始化协调器,如第一个代码片段所示。以这种方式处理,意味着使用 NSFileCoordinator 类的新文档或展示者可以接收关于这些更改的警报,并允许你更新应用程序的 UI 以处理这些更改发生的情况。
接下来,我们使用 NSFileManager 类,该类允许你使用 setUbiquitous:itemAtURL:destinationURL:error: 方法在本地文件和云之间移动。此方法除了将文件安全地从你的沙盒移动到中央 iCloud 文件夹并返回外,不做任何其他操作。
该方法接受三个参数。第一个参数确定移动的方向。YES 将项目从沙盒移动到云。第二个参数必须是源 URL,第三个是目标。如果三个参数放置顺序错误,该方法将失败。本地沙盒 URL 的形式如下,如以下 URL 所示:
file://localhost/private/var/mobile/Library/Mobile%20Documents/TEAMID~com~geniesoftstudios~iCloudExample/Document.doc;
参见
- 
与 iCloud 存储 API 一起工作 菜谱 
- 
请求 iCloud 存储权限 菜谱 
与 iCloud 存储 API 一起工作
在这个菜谱中,我们将探讨在使用 iCloud 时我们可用的不同存储方法,以及这些方法如何被纳入你自己的应用程序中。
准备工作
在 iCloud 中存储文档允许你的应用程序将文档和数据写入一个共同的中央位置。这也提供了同步和访问项目以及从用户丢失的 iOS 设备上的所有其他 iOS 设备和 Mac OS X 计算机中恢复文档的能力。
如何做...
要充分利用 iCloud 存储,需要完全理解实现此功能两种方式之一以及如何访问信息。以下表格解释了各种存储类型及其描述:
| 存储类型 | 描述 | 
|---|---|
| iCloud 文档存储 | 使用此功能在用户的 iCloud 账户中存储和共享用户文档和数据。 | 
| iCloud 键值数据存储 | 使用此功能在您的应用程序实例之间存储和共享少量数据。 | 

如前一个屏幕截图所示,这显示了在您的应用程序沙盒内的本地 iCloud 存储中创建信息时涉及的整个过程。
使用 iCloud 文档存储
在 iCloud 中存储文档是最容易实现的,可以通过对名为UIDocument的类进行子类化来实现。此类处理读取和写入文件所需的一切,包括与 iCloud 守护进程的协调等。
// Point to our iCloud Documents container.
  NSURL *ubiq = [[NSFileManager defaultManager] 
  URLForUbiquityContainerIdentifier:nil];
  NSURL *ubiquitousPackage = [[ubiq
  URLByAppendingPathComponent:@"Documents"]
  URLByAppendingPathComponent:@"/Document.doc"];
  Document *doc = [[Document alloc]    
  initWithFileURL:ubiquitousPackage];
  doc.docContent = @"Welcome to iCloud Programming.";
  // Check to see if we are editing a currently opened note
  [doc saveToURL[doc fileURL] forSaveOperation:UIDocumentSaveForCreatingcompletionHandler:^(BOOL success) {
    if (success) {
      NSLog(@"Saved Successfully.");
    }
  }];
当我们开始构建我们的 iCloud 应用程序以在云中存储和检索文档内容时,我们将更深入地探讨如何创建UIDocument类的子类。
在 iCloud 中存储键值数据
在 iCloud 中存储数据为您提供了使您的应用程序在运行在其他 Mac OS X 计算机和其他 iOS 设备上的其他应用程序副本之间共享数据的方法。
以下代码片段显示了如何设置云,以便您能够使用键值方法将数据写入用户的 iCloud:
  NSFileManager *fileManager = [NSFileManager defaultManager];
  NSURL *iCloudURL = [fileManager
  URLForUbiquityContainerIdentifier:
  @"TEAMID.com.yourcompany.applicationname"];
  // Log the iCloud URL to the console window
  NSLog(@"%@", [iCloudURL absoluteString]);
  // If iCloud is supported or enabled
  if(iCloudURL)
  {
    NSUbiquitousKeyValueStore *iCloudStore = 
    [NSUbiquitousKeyValueStore defaultStore];      
    [iCloudStore setString:@"Success" 
    forKey:@"iCloudStatus"];
    // For Synchronizing with iCloud Server
    [iCloudStore synchronize]; 
    // Retrieve our stored status from the iCloud Server
    NSLog(@"iCloud status : %@", [iCloudStore
    stringForKey:@"iCloudStatus"]);
}
当您的应用程序使用 iCloud 键值数据存储方法时,用户将永远不会看到这一过程,因为您的应用程序将使用此方法来共享仅由您的应用程序使用的非常小的信息。
注意
当使用NSUbiquitousKeyValueStore类时,您必须确保在项目首选项中勾选了键值存储复选框。
单个键值存储可用的空间限制为 64 千字节,并且写入容器中单个键值的数据大小不得超过 4 千字节。这适用于存储有关您应用程序的少量数据,但不建议用它来存储文档或大量数据。
注意
有关 iCloud 的更多信息,请参阅 Apple 开发者文档developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/iCloud/iCloud.html#//apple_ref/doc/uid/TP40007072-CH5-SW1。
它是如何工作的...
在这个菜谱中,我们了解了可以用于在 iCloud 中存储信息的不同方法。当处理文档存储时,我们需要设置一个“ubiq”(ubiquitous 的简称),变量来指向我们 iCloud 账户中的当前文档容器,然后使用ubiquitousPackage类,然后将我们的文件名附加到 iCloud 文档容器的位置。然后我们使用一些默认内容初始化我们的UIDocument类文档,然后使用forSaveOperation方法的UIDocumentSaveForCreating属性创建一个全新的文档。
或者,当与key-value数据一起工作时,您需要使用NSUbiquitousKeyValueStore类,它使您能够在您的每个设备之间共享少量数据。然后我们使用NSUserDefaults类以编程方式与系统默认值交互,这样我们就可以使用setString:方法将字符串Success存储为iCloudStatus键,该键由forKey:方法确定。最后,我们调用synchronize方法将我们指定的值存储回 iCloud 服务器,然后使用stringForKey:方法检索我们的键的值并将此值显示在控制台窗口中。
参见
- 
构建 iCloud 应用程序菜谱 
- 
在 iCloud 中存储和使用文档菜谱 
- 
请求 iCloud 存储权限菜谱 
在 iCloud 中检测文件版本冲突
在这个菜谱中,我们将看看如何在您的应用程序中实现和处理文件版本冲突是多么容易。
准备工作
处理文件版本冲突是软件开发中常见的问题。使用 iCloud 时,当多个实例在多个设备上运行并尝试修改同一文档时,需要有效地和高效地处理。当两个设备同时尝试上传对文档所做的更改时,这将导致冲突。在这种情况下,iCloud 将最终拥有同一文件的两个不同版本,并必须决定如何处理它们。
如何做到这一点...
- 
为了确定何时发生文档文件冲突,您需要注册一个观察者对象,在每次对文档进行更改时,定期检查 UIDocumentStateChangedNotification通知对象的documentState属性,以检查冲突的存在并相应地采取行动:-(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Add observer calls to monitor document state changes [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(documentChanged:) name:UIDocumentStateChangedNotification object:self.document]; }
- 
接下来,我们需要创建 documentChanged:方法。这个方法作为一个通知处理程序运行,等待UIDocumentStateChangedNotification通知发生,以便在处理完成后能够检查对象的documentState。#pragma mark routine is called whenever a change to the document is encountered - (void)documentChanged:(NSNotification *)notification { if ([notification.object documentState] & UIDocumentStateInConflict) { NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"/Snippet.doc"]; SnippetDocument *doc = [[SnippetDocument alloc] initWithFileURL:ubiquitousPackage]; NSURL *documentURL = [doc fileURL]; NSArray *conflictVersions = [NSFileVersion unresolvedConflictVersionsOfItemAtURL:documentURL]; for (NSFileVersion *fileVersion in conflictVersions) { [fileVersion setResolved:YES]; } [NSFileVersionremoveOtherVersionsOfItemAtURL:documentURL error:nil]; } }
它是如何工作的...
在这个菜谱中,我们学习了如何通过UIDocumentStateChangedNotification通知对象注册和使用通知处理程序,以便在文档状态发生变化时通知我们。
我们使用NSFileManager对象的URLForUbiquityContainerIdentifier方法,并通过将nil作为参数传递来默认选择权限文件中列出的第一个容器。建议的方法是所有文档都必须存储在Documents子目录中,并且需要附加到 URL 路径上。
最后,我们使用NSFileVersion对象将文档的所有未解决版本替换为当前文档。接下来,我们遍历包含表示文档冲突版本的NSFileVersion对象的数组,并将每个对象的resolved属性设置为YES,以删除与文档文件关联的所有冲突版本。
以下表格为您提供了不同文档状态的描述:
| 文档状态 | 描述 | 
|---|---|
| UIDocumentStateNormal | 表示文档已打开并启用用户编辑。 | 
| UIDocumentStateClosed | 表示文档当前已关闭,或读取文档时发生错误。 | 
| UIDocumentStateConflict | 表示已检测到当前文档的冲突。 | 
| UIDocumentStateSavingError | 表示在尝试保存文档时发生错误。 | 
| UIDocumentStateEditingDisabled | 表示文档正忙,目前不适合编辑。 | 
注意
有关UIDocumentStateChangedNotification框架的更多信息,请参阅位于以下 URL 的 Apple 开发者文档:
参见
- 
使用 iCloud 存储 API 进行工作的配方 
- 
在 iCloud 中检测文件版本冲突的配方 
构建 iCloud 应用程序
在本配方中,我们将学习如何使用 iCloud 存储 API 构建一个感知 iCloud 的应用程序。
准备工作
在本节中,我们将学习如何创建一个应用程序,使我们能够在我们的 iCloud 存储库中创建新的文档。首先,按照以下简单步骤进行操作:
如何操作...
首先,按照以下顺序执行以下简单步骤:
- 
从 /Xcode4/Applications文件夹启动 Xcode。
- 
选择创建一个新的 Xcode 项目,或导航到文件 | 新建项目。 
- 
从可用模板列表中选择单视图应用程序选项。 
- 
点击下一步按钮,继续向导的下一步。 
- 
接下来,将 iCloudExample作为项目名称输入。
- 
从设备下拉列表中选择iPhone。 
- 
确保未选中使用故事板复选框。 
- 
确保已选中使用自动引用计数复选框。 
- 
确保没有勾选包含单元测试复选框。 
- 
点击下一步按钮继续到向导的下一个步骤。 
- 
指定您希望保存项目的位置。 
- 
然后,点击创建按钮继续并显示 Xcode 工作区。 现在我们已经创建了 iCloudExample项目,我们可以开始构建用户界面,这将允许我们创建和修改文档,并将这些文档保存回 iCloud。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库窗口中选择并拖动一个工具栏,将其添加到视图的顶部。 
- 
选择我们之前添加到工具栏中的项目按钮。 
- 
在属性检查器部分,将标识符属性的值更改为保存,并将样式属性的值更改为带边框。 
- 
从对象库中选择并拖动一个 Bar Button Item对象,并将其添加到我们之前添加的保存按钮旁边。
- 
在属性检查器部分,将标识符属性的值更改为自定义,并将标题属性的值更改为加载。 
- 
最后,从对象库中选择并拖动一个 Text View对象到视图控制器的中心,并调整大小以填充整个屏幕区域。以下截图显示了添加了工具栏、按钮和 Text View控制器的完成用户界面。![如何做...]() 我们下一步是创建每个按钮以及 Text View控制器的出口和属性事件。创建这些事件将允许我们在代码中直接访问相关方法和控制属性。要创建出口,请按照以下简单步骤操作:
- 
通过导航到导航 | 在辅助编辑器中打开,或按住选项 + 命令键打开辅助编辑器页面。 
- 
确保在辅助编辑器窗口中显示 ViewController.h接口文件。
- 
选择保存( Bar Button Item)控件,然后按住控制键,将其拖入ViewController.h接口文件。
- 
在创建连接时,从连接下拉菜单中选择出口。 
- 
输入 btnSave作为要创建的出口属性的名称。
- 
从存储下拉菜单中选择strong。 
- 
重复步骤 3 到 6 来创建加载按钮项和Text View控制器的出口属性,同时提供以下命名 btnLoad和docContents。现在我们已经为我们的控件创建了实例变量出口,我们需要为保存按钮创建相关动作。创建这些动作允许在按钮按下时触发事件。要创建动作,请按照以下简单步骤操作: 
- 
在 Assistant Editor 窗口中仍然显示 ViewController.h接口文件时,选择 Save (Bar Button Item) 控件,并按住 control 键将其拖入ViewController.h接口文件。
- 
从 Connection 下拉列表中选择 Action 以创建连接。 
- 
将方法的 Name 值输入为 btnSave。
- 
重复步骤 1 到 3 以创建 Load 和 Text View 控件的输出属性,同时提供以下命名 btnLoad和docContents。以下代码片段显示了完成的 ViewController.h接口文件:// ViewController.h // iCloudExample // Created by Steven F Daniel on 8/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> @interface ViewController : UIViewController // Declare the properties for each of our objects. @property (strong,nonatomic) IBOutlet UIBarButtonItem *btnSave; @property (strong,nonatomic) IBOutlet UIBarButtonItem *btnLoad; @property (strong,nonatomic) IBOutlet UITextView *docContents; @end我们下一步是使用 iOS 5 中包含的 UIDocument类,使我们能够更容易地处理 iCloud 文档。这个类充当文件和它包含的实际数据之间的中间件,并实现了NSFilePresenter协议来处理整个文档处理的后台操作,这样在文件打开或保存时应用程序不会被阻塞。
- 
从 Xcode 菜单栏中,导航到 File | New | File... 或按 Command + N。 
- 
从模板列表中选择 Objective-C class 模板。 
- 
点击 Next 按钮继续向导中的下一步。 
- 
输入 Snippet作为要创建的文件名。
- 
确保您已从 Subclass of 下拉菜单中选择 UIDocument作为创建的类型。![如何操作...]() 注意如果您在下拉列表中看不到 UIDocument类,只需手动输入即可。
- 
点击 Next 按钮继续向导的下一步。 
- 
点击 Create 按钮将文件保存到指定的文件夹位置。 
- 
打开位于 iCloudExample文件夹中的Snippet.h接口文件,并输入以下代码片段:// Snippet.h // iCloudExample // Created by Steven Daniel on 08/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> @interface Snippet : UIDocument @property (nonatomic, strong) NSString *docContent; @end
- 
打开位于 iCloudExample文件夹中的Snippet.m实现文件,并输入以下代码片段:// Snippet.m // iCloudExample // Created by Steven Daniel on 08/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "Snippet.h" @implementation Snippet @synthesize docContent; // Called whenever the application reads data from the file. - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError{ // Initialize our document content self.docContent = @""; // Check to see if any text associated for the document. if ([contents length] > 0) { self.docContent = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding]; } return YES; } // Called whenever the application saves the content. - (id)contentsForType:(NSString *)typeName error:(NSError **)outError { // Ensure we have content to save for our document. if ([self.docContent length] == 0) { self.docContent = @""; } // Save the document contents and return back the data. return [NSData dataWithBytes:[self.docContent UTF8String] length:[self.docContent length]]; } @end现在我们已经完成了 UIDocument子类的创建,我们可以开始实现负责将内容保存到云的方法。
- 
修改位于 iCloudExample文件夹中的ViewController.h接口文件;输入以下高亮显示的代码片段,如图所示:// ViewController.h // iCloudExample // Created by Steven F Daniel on 8/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import "Snippet.h" @interface ViewController : UIViewController // Declare the Getters and Setters for each of our objects. @property (strong,nonatomic) IBOutlet UIBarButtonItem *btnSave; @property (strong,nonatomic) IBOutlet UIBarButtonItem *btnLoad; @property (strong,nonatomic) IBOutlet UITextView *docContents; @property (strong, nonatomic) Snippet *document; @property (strong, nonatomic) NSMetadataQuery *docQuery; @end
- 
修改位于 iCloudExample文件夹中的ViewController.m实现文件,并添加以下synthesize方法:// ViewController.m // iCloudExample // Created by Steven F Daniel on 8/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "ViewController.h" @interface ViewController() @end @implementation ViewController @synthesize document; @synthesize docQuery; @synthesize docContents = m_docContents; @synthesize btnSave = m_btnSave; @synthesize btnLoad = m_btnLoad;
- 
接下来,修改 ViewDidLoad方法,如图所示的高亮代码片段:- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; if (!ubiq) { NSLog(@"iCloud not currently available"); self.btnSave.enabled = NO; self.btnLoad.enabled = NO; } // Set the background color and font attributes for our note. UIFont * font = [UIFont fontWithName:@"Helvetica-Bold" size:[UIFont systemFontSize]]; [self.docContents setFont:font]; [self.docContents setBackgroundColor:[UIColor colorWithRed:1.0f green:1.0f blue:0.6f alpha:1.0f]]; // Initialize control and button attributes [self btnLoad:nil]; }
- 
接下来,修改 viewDidUnload方法,如图所示的高亮代码片段:- (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. self.btnSave = nil; self.btnLoad = nil; // Turn off our notifications [docQuery disableUpdates]; [[NSNotificationCenter defaultCenter] removeObserver:self]; }
- 
接下来,修改 viewDidAppear方法,如图所示的高亮代码片段:-(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Add observer calls to monitor document and // document state changes [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(documentChanged:) name:UIDocumentStateChangedNotification object:self.document]; }接下来,我们需要实现当用户点击 Save 按钮时将文档保存到 iCloud 的方法。 
- 
打开 viewController.m实现文件,定位btnSave方法,并输入以下代码片段:#pragma mark Saves the document to our iCloud repository - (IBAction)btnSave:(UIBarButtonItem *)sender { // Points to our iCloud Documents container. NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"/Snippet.doc"]; Snippet *doc = [[Snippet alloc] initWithFileURL:ubiquitousPackage]; doc.docContent = self.docContents.text; // Check to see if we are editing a currently opened note [doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { if (success) { NSLog(@"Document saved successfully"); } }]; }
- 
接下来,定位 btnLoad方法,并输入以下代码片段:#pragma mark Reloads our document from cloud Storage - (IBAction)btnLoad:(UIBarButtonItem *)sender { NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; if (ubiq) { docQuery = [[NSMetadataQuery alloc] init]; [docQuerysetSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K Like 'Snippet.doc'", NSMetadataItemFSNameKey]; [docQuerysetPredicate:pred]; NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(processQuery:) name:NSMetadataQueryDidFinishGatheringNotification object:docQuery]; [center addObserver:self selector:@selector(processQuery:) name:NSMetadataQueryDidUpdateNotification object:docQuery]; if (![self.docQuery isStarted]) [self.docQuery startQuery]; [self.docQuery enableUpdates]; } else { self.btnSave.enabled = NO; self.btnLoad.enabled = NO; } }
- 
接下来,创建一个名为 processQuery:的方法,如下面的代码片段所示:#pragma mark Retrieve the contents of our document stored within iCloud and update our UITextView Control - (void)processQuery:(NSNotification *)notification { [docQuery disableUpdates]; if ([[docQuery results] count] == 1) { NSURL *url = [[[docQuery results] objectAtIndex:0] valueForAttribute:NSMetadataItemURLKey]; Snippet *contents = [[Snippet alloc] initWithFileURL:url]; [contentsopenWithCompletionHandler:^(BOOL success) { if (success) { [self.docContents setText:contents.docContent]; } }]; } [docQuery enableUpdates]; }
- 
接下来,最后,创建一个名为 documentChanged:的方法,如下面的代码片段所示:#pragma mark routine is called whenever a change to the document is encountered - (void)documentChanged:(NSNotification *)notification { if ([notification.object documentState] & UIDocumentStateInConflict) { // Wait to find out what user wants first UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Conflict Detected" message:@"Document modified on another iOS device." delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alertView show]; // Point to our iCloud Documents container. NSURL *documentURL = [notification.objectfileURL]; NSArray *conflictVersions = [NSFileVersion unresolvedConflictVersionsOfItemAtURL:documentURL]; for (NSFileVersion *fileVersion in conflictVersions) { [fileVersion setResolved:YES]; } [NSFileVersion removeOtherVersionsOfItemAtURL:documentURL error:nil]; // Trigger an Auto-Save and re-enable our Query Updates [document updateChangeCount:UIDocumentChangeDone]; } }
- 
然后,通过从 产品 菜单中选择 产品 | 运行 或按 Command + R 键来构建和运行应用程序。 注意关于 NSMetadataQuery对象的更多信息,请参阅以下链接:
下面的截图显示了我们的 iCloudExample 应用程序在两个不同的 iOS 设备上运行。当在第二个设备上做出更改,然后按下 保存 按钮时,iCloud 守护进程服务检测到这一变化。第一个设备显示一个 alertview 对话框,通知文档在另一台设备上已被修改。

下面的截图显示了我们的片段文档存在于我们应用程序的容器中,在 iCloud 内:

注意
可以通过 设置 应用程序访问上一个屏幕,然后导航到 设置 | iCloud | 存储与备份 | 管理存储。
它是如何工作的...
在这个菜谱中,我们学习了如何构建和创建 iCloudExample 应用程序的用户界面,以便在 iCloud 中存储和检索文档。然后,我们为每个按钮创建了出口属性,以便在按下时能够保存和加载文档。在下一步中,我们创建了一个 UIDocument 子类,并声明了一个 NSString 属性变量 docContent,该变量将用于存储在创建或修改文档时文档的内容。然后我们继续合成文档内容属性,以便我们的类可以访问与其相关的对象。然后我们继续重写 loadFromContents: 方法,从文件中读取数据到我们的 UIDocument 子类。这里要注意的最重要参数是 contents;这是一个 NSData 对象,包含你在创建或更新文档模型时输入的实际数据。
背景队列NSFilePresenter在读取操作完成时调用此方法。如果文档在没有输入任何数据的情况下保存,我们分配一个空字符串的默认值。然后,我们继续重写contentsForType:方法,该方法在NSFilePresenter的背景队列请求UIDocument子类的内容快照时使用。在这里,我们检查文档是否包含内容,如果是,我们将我们的文档数据转换为NSData对象,并将其作为NSData实例返回。接下来,我们声明我们的Snippet对象,该对象将用于保存创建的文档,以及我们的NSMetadataQuery对象,该对象将用于在我们的应用程序的 iCloud 存储库中查询和查找文档,并访问每个文件的相关对象属性。在我们的下一步操作中,我们首先检查我们是否可以访问我们的应用程序的 iCloud 存储库,然后继续设置docContents控件的后台颜色和字体名称和大小,最后调用reload:方法从我们的 iCloud 存储库中检索文档。
我们接下来注册一个观察者对象,以便我们能够使用UIDocumentStateChangedNotification通知对象定期检查文档状态的变化。然后,我们声明我们的btnSave:方法,并设置一个ubiq变量来指向我们 iCloud 账户中的文档容器。接着,我们使用ubiquitousPackage类,并将我们的文件名追加到 iCloud 文档容器的位置。然后,我们使用一些默认内容初始化我们的UIDocument类文档,并使用forSaveOperation方法的UIDocumentSaveForCreating属性来创建一个全新的文档。
对于 btnLoad: 方法,我们首先确保可以将其连接到 iCloud 数据存储,然后设置并初始化我们的 docQuery 查询谓词,使用谓词类方法 NSMetadataQueryUbiquitousDocumentScope 来查找我们的文档,然后设置一个观察者 queryDidFinishGathering 通知,当元数据搜索完成收集所有项目时会被调用。接下来,在我们的 processQuery: 方法中,我们从结果查询中获取文档项,并将提取的文档内容写入我们的 docContent 对象。最后,在我们的 documentChanged: 方法中,我们使用通知对象的 documentState 属性来确定是否检测到任何文件冲突,如果是,我们使用 UIAlertView 类显示一个警告消息;然后指向我们的 iCloud 文档容器以获取文件的 URL,并使用 NSFileVersion 对象将所有未解决的文档版本替换为当前文档,然后遍历包含表示文档所有冲突版本的 NSFileVersion 对象的数组,并将每个对象的 resolved 属性设置为 YES,并删除与文档文件 URL 相关的所有冲突版本。
参见
- 
请求 iCloud 存储权限 菜单 
- 
在 iCloud 中存储和使用文档 菜单 
- 
使用 iCloud 存储 API 菜单 
请求 iCloud 存储权限
在本菜谱中,我们将探讨如何使用 Xcode 添加和配置权限,以便使您的应用程序能够与 iCloud 服务通信。
准备工作
为了保护应用程序创建的数据,需要在构建时创建一些特定的权限,以便使用 iCloud 存储。您需要确保已选择为应用程序的 App ID 启用 iCloud 的选项。
如何操作...
在您的应用程序能够与 iCloud 服务通信之前,您需要从 iOS 配置文件门户中创建一个新的 App ID,该门户位于 developer.apple.com/ios/manage/bundles/index.action/。
如果您正在使用现有的 App ID,则它不能包含通配符 ID,并且必须包含您将要创建的应用程序的完整名称,例如,com.yourcompany.*。为了使您的 App ID 能够与 iCloud 服务通信,请按照以下简单步骤操作:
- 
创建一个 new App ID值或编辑您之前创建的一个。
- 
通过在“配置 App ID”屏幕中简单地勾选“启用 iCloud”复选框来设置您的配置文件以用于 iCloud: ![如何操作...]() 
- 
接下来,您将看到一个弹出对话框,解释说明您使用所选 App ID 创建的任何新配置文件都将启用 iCloud 服务: ![如何操作...]() 
- 
一旦点击了确定按钮,您将返回到配置 App ID屏幕,并且启用 iCloud按钮将变为绿色,如以下截图所示: ![如何操作...]() 
- 
点击完成按钮关闭此屏幕。 
- 
接下来,点击配置选项卡,然后点击开发选项卡,下载如以下截图所示的开发配置配置文件: ![如何操作...]() 
- 
接下来,从配置选项卡,点击分发选项卡,下载如以下截图所示的分发配置配置文件: ![如何操作...]() 
- 
接下来,从项目导航器窗口中,点击您的项目,在目标部分,然后点击摘要页面,并向下滚动到权限部分。 
- 
勾选启用权限和启用 iCloud复选框。这将向您的项目添加一个名为 iCloudExample.entitlements的文件。![如何操作...]() 
- 
接下来,点击+按钮来自动填充通用容器和密钥链组部分。 注意每次您向项目添加权限时,这些权限将直接绑定到用于将您的应用程序文档和数据存储库与其他应用程序分开的应用程序配置文件。 
根据应用程序需要使用的 iCloud 功能,应用程序可以请求两种权限。以下表格中对此进行了说明:
| 权限 | 描述 | 
|---|---|
| com.apple.developer.ubiquity-container-identifiers | 使用此选项请求 iCloud 文档存储权限。此键的值是一个容器标识符字符串数组。(数组中的第一个字符串不得包含任何通配符字符。) | 
| com.apple.developer.ubiquity-kvstore-identifier | 使用此选项请求 iCloud 键值数据存储权限。此键的值是一个单独的容器标识符字符串。 | 
当处理Key-Value存储数据时,您需要确保已勾选使用具有标识符的存储选项。这必须保持一致,并采用以下形式 <TEAMID>.<CUSTOM_STRING>,其中 <TEAMID> 是与您的开发团队关联的唯一 10 个字符标识符。<CUSTOM_STRING>标识符是反向 DNS 字符串,用于标识存储您的应用程序文档的容器。
注意
要定位与您的开发团队关联的唯一标识符,请登录到 Apple 开发者连接网站,然后转到位于developer.apple.com/membercenter的会员中心页面。
选择您的账户标签,然后从标签左侧的列中选择组织配置文件(如果您已将您的配置文件设置为用作组织),您的团队标识符位于公司/组织 ID字段中。
使用 iCloud 文档存储的应用程序可以指定多个容器来存储文档和数据。com.apple.developer.ubiquity-container-identifiers键是一个字符串数组。
以下截图显示了iCloudExample.Entitlements权限文件在项目导航器中的属性列表视图:

TEAMID值(如前一个截图所示),可以从您的开发者账户的账户摘要页面获取,并使用以下截图中的Individual ID值:

注意
在您的权限文件中指定的字符串也是您在请求用户 iCloud 存储中目录位置时传递给URLForUbiquityContainerIdentifier:方法的字符串。
工作原理...
在本食谱中,我们学习了如何设置和配置应用程序 ID,以便它具有与 iCloud 服务通信的能力。每当创建或修改新的或现有的应用程序 ID 时,它将直接绑定到您的开发和分发配置文件,并且需要在您的计算机上下载和重新安装以避免在 iOS 设备上部署时出现问题。
您也可以选择使用 Xcode 开发环境通过组织者界面来完成此操作。在接下来的步骤中,我们需要设置权限,这将通过启用启用 iCloud复选框并自动填写通用容器和密钥链组部分来使我们的应用程序能够访问 iCloud 目录。
当这些设置完成时,它将在设备上设置目录位置,您可以在其中找到和创建 iCloud 文件。然后,iCloud 守护进程服务将自动获取新文件到该目录,当它们可用时更新现有文件,并监视您放入该目录的文件状态的变化。
配置 iOS 设备以使用 iCloud
在本食谱中,我们将看看如何轻松地将您的 iOS 设备配置为使用 iCloud,以便它具有存储文档的能力。
准备工作
在任何应用程序开始将数据存储在 iCloud 之前,我们需要正确配置和设置我们的应用程序以使用 iCloud,并将文档存储到 iOS 设备上。为此,设备必须运行 iOS 5 或更高版本。iOS 模拟器中不会工作。
如何操作...
以下步骤显示了设置 iCloud 账户有多简单:
- 
从您的 iOS 设备中的设置面板选择iCloud。以下截图显示了这一操作: ![如何操作...]() 
- 
接下来,使用您的 Apple ID 和密码登录,然后点击以下截图所示的登录按钮。 
- 
您必须同意 iCloud 的条款和条件,然后点击同意按钮以关闭弹出对话框。 
- 
接下来,点击存储与备份选项,进入下一屏幕。 ![如何操作...]() 
- 
接下来,从备份部分面板中将备份到 iCloud选项设置为开启。这将自动开始同步您的邮件、联系人、日历、提醒、书签或笔记,并将您的账户信息推送到或从 iCloud 中拉取。 注意如果您愿意,也可以通过任何网络浏览器登录到您的 iCloud 账户,网址为 www.iCloud.com/,使用您在 iOS 设备上输入的相同信息。一旦成功登录,您可以选择联系人或日历来查看已同步到云中的数据。通过网页界面进行编辑将直接推送到您的 iOS 设备。
它是如何工作的...
在这个菜谱中,我们学习了如何在 iOS 设备上轻松设置和配置 iCloud。iCloud 是一项免费服务,在成功注册后提供初始 5GB 的免费存储空间。这允许您同步所有联系信息、电子邮件和文档。
如果您需要额外的存储空间,Apple 通过存储与备份菜单下的iCloud 设置为您提供。
当您在应用程序中使用 iCloud 存储 API 时,您应用程序明确存储在 iCloud 中的任何文档都不会与您的应用程序一起备份,因为这些文档已经存储在您的 iCloud 账户中,因此不需要单独备份。
第七章.处理不同的多媒体资源
在本章中,我们将涵盖:
- 
从相册选择图片和视频 
- 
使用相机捕获媒体 
- 
使用 AppleTV 集成播放视频 
- 
使用 MediaPlayer 框架播放音乐 
- 
使用 iOS 设备麦克风进行录音 
- 
动画视图 
- 
绘制自定义文本 
- 
绘制线条和曲线 
- 
绘制和填充形状 
- 
使用 Core Image 应用颜色效果 
- 
应用过渡效果 
简介
Core Image框架是一个硬件加速框架,它提供了一种更简单的方法,让您可以使用几个内置过滤器(颜色效果、扭曲和过渡)以及一些高级功能(自动增强、红眼减少和面部识别)来增强您的照片和视频。
在本章中,我们将更深入地探讨这些框架以及如何使用CIFilter类应用图像过滤效果,以及实现 Airplay,使我们能够使用 Apple TV 将内容输出到另一台设备。我们将学习如何使用AVFoundation框架通过 iOS 设备的内置麦克风录制和播放音频内容。最后,我们将学习如何使用CoreGraphics框架使我们能够绘制形状和文本信息到视图中。
从相册选择图片和视频
在本食谱中,我们将学习如何让用户能够从 iOS 设备的相册中选择图片和视频。
准备工作
创建一个新的单视图应用程序,并将其命名为MediaPickerExample。
如何做到这一点...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中选择并拖动一个 UIToolbar对象,并将其添加到我们的视图中,并选择工具栏中的项目按钮。
- 
在属性检查器部分,将标识符属性更改为组织。 
- 
为此 UIBarButtonItem创建一个出口,并将其命名为btnBrowse。
- 
为组织按钮创建一个动作方法,并将其命名为 btnBrowse。
- 
从对象库中选择并拖动一个 UIImageView对象,并将其添加到我们的视图中,并调整大小以填充屏幕区域。
- 
为此 UIImageView创建一个出口,并将其命名为imageView。
- 
我们的下一步是创建代码功能,该功能将负责在点击组织按钮时允许选择图片和视频。 - 
从项目导航器打开 ViewController.h接口文件。
- 
接下来,根据以下代码片段中突出显示的代码部分修改界面文件: // ViewController.h // MediaPickerExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import<UIKit/UIKit.h> @interface ViewController : UIViewController<UIImagePickerControllerDelegate, UINavigationControllerDelegate> { } // Declare the Getters and Setters for each of our objects. @property (strong, nonatomic) IBOutlet UIBarButtonItem *btnBrowse; @property (strong, nonatomic) IBOutlet UIImageView *imageView; @property (nonatomic, retain) UIImagePickerController *imagePicker; // Declare our class Instance methods - (IBAction)btnBrowse:(id)sender;
- 
从项目导航器打开 ViewController.m实现文件,并输入以下突出显示的代码部分。@synthesize btnBrowse; @synthesize imagePicker; @synthesize imageView;
- 
接下来,根据代码片段修改 btnBrowse:方法:#pragma mark called when the user presses the Organize button - (IBAction)btnBrowse:(id)sender { // Create image picker controller imagePicker = [[UIImagePickerController alloc] init]; // Checks to make sure that the Photo Library is available. if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypePhotoLibrary]) { // Set source to the Photo Library self.imagePicker.delegate = self; self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; self.imagePicker.mediaTypes =[UIImagePickerController availableMediaTypesForSourceType: self.imagePicker.sourceType]; self.imagePicker.allowsEditing = NO; [self presentViewController:imagePickeranimated:YES completion:nil]; } else { NSLog(@"Unable to access the Photo Library."); } }
- 
然后,根据代码片段中的指定创建以下代码部分: #pragma mark - Image Picker Delegate Methods - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:nil]; } #pragma mark Method is called when the user has chosen an item from the image picker. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // Determine the media type of the chosen item NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType]; // Determine if we have chosen a image file from library. if ([mediaType isEqualToString:@"public.image"]) { UIImage *photoImage = [info objectForKey:UIImagePickerControllerOriginalImage]; self.imageView.image = photoImage; self.imageView.contentMode = UIViewContentModeScaleAspectFit; } else if ([mediaType isEqualToString:@"public.movie"]) { NSURL *movieURL = [info valueForKey:UIImagePickerControllerMediaURL]; NSLog(@"Movie URL: %@", movieURL); } // Dismiss the imagePicker Dialog [picker dismissViewControllerAnimated:NO completion:nil]; }
- 
然后,通过从产品菜单中选择产品 | 运行,或者按Command + R来构建和运行应用程序。 
 
- 
当编译完成后,点击组织按钮以显示图片选择器,通过点击其缩略图来选择一个图片。图片将在图像视图中显示。或者,如果您选择了一个视频,完整路径将在控制台窗口中显示。
它是如何工作的...
在这个菜谱中,我们首先扩展我们的类,包括以下每个类协议:UIImagePickerControllerDelegate、UINavigationControllerDelegate,以便我们可以访问它们各自的属性和方法。我们可以通过检查UIImagePickerController类的isSourceTypeAvailable属性来查看我们是否能够访问 iOS 设备的照片库,并将ImagePicker类的属性初始化为仅显示来自我们照片库的图片。接下来,我们将代理指向自身,然后将sourceType属性设置为使用相机,并将cameraDevice属性的值设置为使用后置相机并显示相机界面,然后关闭UIImagePickerController对象。
在下一节中,我们为我们的图片选择器控制器声明一个代理方法imagePickerControllerDidCancel,它将负责处理和照顾关闭相机会话,不进行图像选择,或者当点击取消按钮时拍照。最后,我们通过使用UIImagePickerControllerMediaType属性检查UIImagePickerController中选择的媒体类型,并确定我们是否选择了图像或电影。
如果我们选择了一张图片,我们将创建一个视频的缩略图图像表示,并将imageView控制的image属性设置为该图片。如果选择了一个视频,我们使用UIImagePickerControllerMediaURL方法获取所选文件的文件位置,并在我们的控制台窗口中显示这个位置。
参见
- 使用相机捕捉媒体菜谱
使用相机捕捉媒体
在这个菜谱中,我们将学习如何使用 iOS 设备的相机来捕捉媒体。
准备工作
在我们之前的菜谱基础上,创建一个新的单视图应用程序,并将其命名为CameraPickerExample。
如何做到这一点...
首先,按照以下顺序执行以下简单步骤:
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中选择并拖动一个 UIToolbar对象,并将其添加到我们的视图中,然后点击工具栏中的项目按钮。
- 
在属性检查器部分,将标识符属性更改为Camera。 
- 
为这个 UIBarButtonItem创建一个出口,并将其命名为btnCamera。
- 
为Camera按钮创建一个动作方法,并将其命名为 btnCamera。
- 
我们接下来的步骤是创建代码功能,该功能将在点击相机按钮时负责显示相机视图。 
- 
从项目导航器打开 ViewController.h接口文件。
- 
接下来,按照以下代码片段中突出显示的代码部分修改接口文件: // ViewController.h // CameraPickerExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import<UIKit/UIKit.h> @interface ViewController : UIViewController<UIImagePickerControllerDelegate, UINavigationControllerDelegate> { } // Declare the Getters and Setters for each of our objects. @property (strong, nonatomic) IBOutlet UIBarButtonItem *btnCamera; @property (nonatomic, retain) UIImagePickerController *imagePicker; // Declare each of our Class methods - (IBAction)btnCamera:(id)sender;
- 
从项目导航器窗口打开 ViewController.m实现文件,并输入以下突出显示的代码部分:@synthesize btnCamera; @synthesize imagePicker;
- 
接下来,按照代码片段中显示的修改 btnCamera:方法:#pragma mark called when the user presses the camera button - (IBAction)btnCamera:(id)sender { // Checks device to make sure that the Camera is available. if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { // Create image picker controller imagePicker = [[UIImagePickerController alloc] init]; // Set our source to the Camera self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; self.imagePicker.delegate = self; self.imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear; self.imagePicker.allowsEditing = NO; [self presentViewController:imagePicker animated:YES completion:nil]; } else{ NSLog(@"Use of the Camera is not available."); } }
- 
然后,根据代码片段中的说明创建以下代码部分: #pragma mark - Image Picker Delegate Methods - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:nil]; } #pragma mark method is called when the user has finished taking a photo with the camera. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // Access the uncropped image and Save the image UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"]; UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); // Then dismiss the imagePicker Dialog [picker dismissViewControllerAnimated:NO completion:nil]; } - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { // Handle any errors detected during the save if (error) { NSLog(@"Unable to save the image to the Photo Album"); } else { NSLog(@"Image successfully saved to the Photo Album"); } }
- 
然后,通过从产品菜单选择产品 | 运行或按Command + R来构建和运行应用程序。 
当编译完成后,点击相机按钮打开相机并拍照。照片将被保存在 iOS 设备的相册中。
它是如何工作的...
在这个食谱中,我们首先扩展我们的类,包括以下每个类协议:UIImagePickerControllerDelegate,UINavigationControllerDelegate,以便我们可以访问它们各自的属性和方法。然后,我们检查是否能够使用UIImagePickerController类的isSourceTypeAvailable属性访问 iOS 设备的相机,然后创建我们UIImagePickerController类的新实例。接下来,我们将委托指向自身,然后将sourceType属性设置为使用相机,并将cameraDevice属性的值设置为使用后置摄像头。最后,我们显示相机界面,并关闭UIImagePickerController对象。接下来,我们声明一个用于我们的图像选择器控制器的委托方法imagePickerControllerDidCancel,该方法将负责处理和照顾关闭相机会话,不进行图像选择,或当点击取消按钮时拍照。
接下来,当用户使用相机拍照时,它不会自动保存在设备上。要保存它,我们调用UIImageWriteToSavedPhotosAlbum方法,这是UIImage类的方法。此方法接受一个类型为UIImageconextInfo的委托,如果出现问题将报告错误并立即调用didFinishSavingWithError:方法来处理错误类型。
参见
- 从相册选择图片和视频的食谱
使用 Apple TV 集成播放视频
AirPlay框架是一个更新的框架,允许您从任何 iOS 设备流式传输音频和视频内容到任何启用了 AirPlay 功能的设备,这些设备能够播放音频和视频,例如电视和音频系统。
从 iOS 5 开始,开发者现在可以灵活地将 Airplay 内容集成到他们的应用程序中,并将此信息输出到附近的 Apple TV 接收器。在本节中,我们将探讨如何创建一个简单的应用程序,在 iOS 设备上播放视频内容,然后查看将内容输出到 Apple TV 设备的步骤。
准备工作
在我们之前的菜谱之后,创建一个新的单视图应用程序,并将其命名为PlayVideoExample。
如何操作...
首先,按照以下顺序遵循以下简单步骤:
- 
将对 MediaPlayer.framework的引用添加到您的项目中。
- 
从项目导航器窗口选择 ViewController.xib文件。
- 
从对象库中拖动一个 UIButton对象,并将其添加到我们的视图中。
- 
从属性检查器部分,将标题属性更改为播放视频。 
- 
为此 UIButton创建一个出口,并命名为btnPlayVideo。
- 
为播放视频按钮创建一个动作方法,并命名为 btnPlayVideo。
- 
我们接下来的步骤是创建当播放视频按钮被点击时负责播放视频的代码功能。 
- 
从项目导航器窗口打开 ViewController.h接口文件。
- 
接下来,创建以下代码部分,如代码片段中突出显示的: // ViewController.h // PlayVideoExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <MediaPlayer/MediaPlayer.h> @interface ViewController : UIViewController // Declare the Getters and Setters for each of our objects. @property (strong, nonatomic) IBOutlet UIButton *btnPlayVideo; @property (strong, nonatomic) MPMoviePlayerController *moviePlayerController;
- 
从项目导航器窗口打开 ViewController.m实现文件,并输入以下突出显示的代码部分:@synthesize btnPlayVideo; @synthesize moviePlayerController;
- 
接下来,修改 btnPlayVideo:方法,如代码片段所示:#pragma mark Handle Playback of the video when button is pressed. - (IBAction)btnPlayVideo:(id)sender { // Initialize our moviePlayer Controller with the video path NSString *moviePath = [[NSBundle mainBundle]pathForResource:@"GenieCompanyVideo" ofType:@"mp4"]; NSURL *movieURL = [NSURL fileURLWithPath:moviePath isDirectory:NO]; self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; // Set up our notifications to determine when movie completes [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackComplete:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayerController]; // Add the movie player controller to the view and // determine if AirPlay is available [self.view addSubview:self.moviePlayerController.view]; if ([self.moviePlayerController respondsToSelector:@selector(setAllowsAirPlay:)]){ [self.moviePlayerController setAllowsAirPlay:YES]; } // Initialize the movie player properties and play the video. self.moviePlayerController.fullscreen = YES; self.moviePlayerController.scalingMode = MPMovieScalingModeAspectFit; [self.moviePlayerController play]; }
- 
接下来,创建名为 moviePlaybackComplete:的方法,如代码片段所示:#pragma mark Handle once the video has finished playback. - (void)moviePlaybackComplete:(NSNotification *)notification { self.moviePlayerController = [notification object]; [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayerController]; [self.moviePlayerController.view removeFromSuperview]; }
- 
然后,通过选择产品菜单中的产品 | 运行或按Command + R来构建和运行应用程序。 
编译完成后,以下截图显示了AirPlay启用时的图标:

它是如何工作的...
在本菜谱中,我们声明了一个变量(NSString)moviePath,它将包含电影文件的文件路径,然后创建一个(NSURL)movieURL,将文件路径转换为初始化MPMoviePlayerController方法时所需的对象。
接下来,我们设置一个通知方法,通过使用名为MPMoviePlayerPlaybackDidFinishNotification的通知方法到NSNotificationCenter属性,调用moviePlaybackComplete:方法,然后将MPMoviePlayerController视图添加到我们的自定义视图控制器中,以便它出现在屏幕上,然后使用MPMoviePlayerController对象的respondsToSelector:方法来处理不支持allowsAirPlay属性的旧 iOS 设备。
为了提供 AirPlay 功能,我们在 MPMoviePlayerController 对象上启用一个特殊属性,通过将 allowsAirPlay 属性设置为 YES,然后指定我们想要全屏显示我们的视频。
然后我们告诉 moviePlayerController 方法开始播放,并修改 MPMoviePlayerController 对象的 scalingMode 属性。通过设置此属性,它将确定电影图像如何适应填充您定义的播放大小。以下是目前存在的缩放模式,并在此处显示:
- 
MPMovieScalingModeNone
- 
MPMovieScalingModeAspectFit
- 
MPMovieScalingModeAspectFill
- 
MPMovieScalingModeFill
最后,我们创建 moviePlaybackComplete: 方法,并使用 [notificationobject] 语句检索对象,然后使用新的 moviePlayerController 指针引用它。然后我们向 NSNotificationCenter 方法发送消息,移除我们在 playVideo 方法中先前注册的观察者。我们最后清理我们的自定义视图控制器。
注意
关于 MPMoviePlayerController 类的更多信息,您可以参考以下位置的 Apple 开发者文档:developer.apple.com/library/ios/#documentation/mediaplayer/reference/MPMoviePlayerController_Class/Reference/Reference.html#//apple_ref/occ/cl/MPMoviePlayerController
参见
- 使用 MediaPlayer 框架播放音乐 食谱
使用 MediaPlayer 框架播放音乐
在本食谱中,我们将学习如何播放存储在 iOS 设备上的歌曲。
准备工作
在我们之前的食谱基础上,创建一个新的 单视图应用程序,并将其命名为 MusicPlayerExample。
如何做到...
要开始,请按照以下顺序遵循以下简单步骤:
- 
将 MediaPlayer.framework添加到您的项目中。
- 
从 项目导航器 窗口中选择 ViewController.xib文件。
- 
从 对象库 窗口中,选择并拖动一个 UIToolbar对象,并将其添加到我们的视图中,并选择工具栏中的 项目 按钮。
- 
从 属性检查器 部分更改 标题 属性,使其显示为 浏览。 
- 
为此 UIBarButtonItem创建出口,并将其命名为btnBrowse。
- 
为 浏览 按钮创建 action方法,并将其命名为btnBrowse。
- 
从 对象库 窗口中,选择并拖动一个 UIBarButtonItem对象,并将其添加到我们的工具栏中的 浏览 按钮旁边。
- 
从 属性检查器 部分更改 标题 属性,使其显示为 播放。 
- 
为此 UIBarButtonItem创建出口,并将其命名为btnPlay。
- 
创建 播放 按钮的 action方法,并将其命名为btnPlay。
- 
我们的下一步是创建代码功能,该功能将负责在从媒体库中选择并播放歌曲时播放音乐。 
- 
从项目导航器窗口中打开 ViewController.h接口文件。
- 
接下来,创建以下代码部分,如代码片段中突出显示: // ViewController.h // MusicPlayerExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <MediaPlayer/MediaPlayer.h> @interface ViewController : UIViewController<MPMediaPickerControllerDelegate> { } // Declare the Getters and Setters for each of our objects. @property (nonatomic, retain) MPMusicPlayerController *mPlayer; @property (nonatomic, retain) MPMediaPickerController *mPicker;
- 
从项目导航器窗口中打开 ViewController.m实现文件,并输入以下突出显示的代码部分:@synthesize btnBrowse; @synthesize btnPlay; @synthesize mPicker; @synthesize mPlayer;
- 
接下来,按照代码片段中的修改 viewDidLoad:方法:- (void)viewDidLoad { [super viewDidLoad]; // Create the Media Picker and Music Player controller's self.mPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeMusic]; self.mPlayer = [[MPMusicPlayerController alloc] init]; [mPlayer prepareToPlay]; self.view.backgroundColor = [UIColor blackColor]; }
- 
然后,创建以下代码部分,如代码片段中指定: #pragma mark called when the user presses the Browse button - (IBAction)btnBrowse:(id)sender { self.mPicker.delegate = self; self.mPicker.allowsPickingMultipleItems = YES; [self presentViewController:mPicker animated:YES completion:nil]; } -(IBAction)btnPlay:(id)sender { // Check to see if we are already playing our audio. if (!self.mPlayer.playbackState == MPMusicPlaybackStatePlaying) { [self.btnPlay setTitle :@"Stop"]; [self.mPlayer play]; } else { [self.btnPlay setTitle:@"Play"]; [self.mPlayer stop]; } } #pragma mark - Image Picker Delegate Methods -(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{ [self.mPlayer setQueueWithItemCollection:mediaItemCollection]; [self.mPicker dismissViewControllerAnimated:YES completion:nil]; } #pragma mark called when the user cancels the media picker -(void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker { [self.mPicker dismissViewControllerAnimated:YES completion:nil]; }
- 
然后,通过从产品菜单中选择产品 | 运行或按Command + R键,构建和运行应用程序。 
当编译完成后,点击浏览按钮,选择一首或多首歌曲,然后按播放来播放所选歌曲。
它是如何工作的...
在本配方中,我们首先扩展我们的类,包括以下每个类协议:MPMediaPickerControllerDelegate,以便我们可以访问它们各自的属性和方法。然后,我们初始化媒体选择器,并传递我们希望它查找的媒体类型,然后初始化并创建MPMusicPlayerController类的实例。接下来,我们声明我们的btnBrowse方法,该方法将显示 iOS 设备音乐库界面,并允许选择多个文件。在我们的btnPlay:方法中,我们检查是否有歌曲正在播放,这是通过检查playbackState属性来确定的。通过MPMusicPlayerController类对象的play和stop方法实现歌曲的播放和停止。在mediaPicker:didPickMediaItems:方法中,我们通过使用setQueueWithItemCollection:方法将用户选择的歌曲设置到音乐播放器中,并通过关闭模态媒体选择器控制器来关闭。
参见
- 
使用 Apple TV 集成播放视频配方 
- 
使用相机捕获媒体配方 
使用 iOS 设备的麦克风录音
在本配方中,我们将学习如何使用 iOS 设备的麦克风来录音。
准备工作
在我们之前的配方之后,创建一个新的单视图应用程序,并将其命名为RecorderExample。
如何做...
首先,按照以下顺序遵循以下简单步骤:
- 
将 AVFoundation.framework引用添加到您的项目中。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库窗口中,选择并拖动一个 UIToolbar对象,并将其添加到我们的视图中,然后选择工具栏内的项目按钮。
- 
在属性检查器部分,将标题属性更改为开始录音。 
- 
为此 UIBarButtonItem创建出口,并将其命名为btnStart。
- 
为按钮创建 action方法,并将其命名为startRecord。
- 
从对象库窗口中,选择并拖动一个 UIBarButtonItem对象,并将其添加到工具栏中开始录音按钮的右侧。
- 
从属性检查器部分,将标题属性更改为读取播放。 
- 
为此 UIBarButtonItem创建出口,并将其命名为btnPlay。
- 
为按钮创建 action方法,并将其命名为startPlayback。
- 
我们下一步要创建的代码功能将负责录音和播放我们的音频内容。 
- 
从项目导航器窗口打开 ViewController.h接口文件。
- 
接下来,创建以下代码部分,如代码片段中突出显示: // ViewController.h // RecorderExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import<UIKit/UIKit.h> #import<AVFoundation/AVFoundation.h> @interface ViewController : UIViewController // Declare the getters and setters for our Outlets @property (nonatomic, strong) IBOutlet UIBarButtonItem *btnPlay; @property (nonatomic, strong) AVAudioRecorder *aRecorder; @property (nonatomic, strong) AVAudioPlayer *aPlayer; // Declare our class instance methods - (IBAction)startRecord:(id)sender; - (IBAction)startPlayback:(id)sender;
- 
从项目导航器窗口打开 ViewController.m实现文件,并输入以下突出显示的代码部分:@synthesize btnStart, btnPlay; @synthesize aPlayer, aRecorder;
- 
接下来,修改 viewDidLoad:方法,如代码片段所示:- (void)viewDidLoad { [super viewDidLoad]; // Initialize our recording sample file properties. NSString *fileName = @"RecorderExample.caf"; NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docsDir = [dirPaths objectAtIndex:0]; NSString *soundFilePath = [docsDir stringByAppendingPathComponent:fileName]; NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath]; // Initialize the recorder with default settings. NSDictionary *recordSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:AVAudioQualityMin], AVEncoderAudioQualityKey, [NSNumber numberWithInt:16], AVEncoderBitRateKey, [NSNumber numberWithInt:2], AVNumberOfChannelsKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey, nil]; // Initialize our audio Recorder settings aRecorder = [[AVAudioRecorder alloc] initWithURL:soundFileURL settings:recordSettings error:nil]; [aRecorder prepareToRecord]; aRecorder.meteringEnabled = YES; btnPlay.enabled = NO; // Set the background color of our view to black self.view.backgroundColor = [UIColor blackColor]; }
- 
然后,创建以下代码部分,如代码片段中指定: #pragma mark Handles recording of the audio - (IBAction)startRecord:(id)sender { // Check to see if we are already recording. if (!aRecorder.recording) { [btnStart setTitle :@"Stop Recording" ]; btnPlay.enabled = NO; [aRecorder record]; } else { [btnStart setTitle:@"Start Recording" ]; btnPlay.enabled = YES; [aRecorder stop]; } } #pragma mark Handles playback of our recording. -(IBAction)startPlayback:(id)sender { // Check to see if we are already playing. if (!aPlayer.playing) { [btnPlay setTitle :@"Stop Playing"]; // Grab the recorded file from the url location. aPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:aRecorder.url error:nil]; // Play our audio file [aPlayer prepareToPlay]; [aPlayer play]; } else { // Stop the audio playback [btnPlay setTitle :@"Play"]; [aPlayer stop]; } }
- 
然后,通过选择产品菜单中的产品 | 运行或按Command + R来构建和运行应用程序。 
当编译完成后,点击开始录音按钮来录制你的声音样本,然后按下播放按钮来听你录音的回放。
它是如何工作的...
在这个菜谱中,我们首先在应用程序首次启动时创建AVAudioRecorder类的实例。然后,使用指向默认文件名的 URL 初始化此方法,并声明一个NSDictionary对象来初始化和设置音频的录音设置。接下来,我们使用NSSearchPathForDirectoriesInDomains类来识别应用程序的文档目录,然后构造一个指向该位置名为RecorderExample.caf的文件的 URL,并准备aRecorder实例在用户请求时开始录音。
接下来,在我们的startRecord方法中,我们使用aRecorder对象来确定我们是否正在录音。如果没有在录音,我们将startRecord方法的文本更改为显示停止录音,禁用btnPlay按钮,然后设置我们的aRecorder对象开始录音。如果我们正在录音,我们将startRecord方法的文本更改为显示开始录音按钮,并禁用btnPlay按钮,并停止aRecorder对象的录音。
在我们的下一步中,我们初始化aPlayer对象,使用aRecorder对象的 URL 方法播放文件录音,并在调用Play方法开始播放之前设置我们的aPlayer对象以使用prepareToPlay方法。如果我们已经决定我们现在正在播放我们的声音录音,我们就在我们的aPlayer对象上调用stop方法,这将停止播放。
注意
关于 AVFoundation 类的更多信息,请参阅位于以下位置的 Apple 开发者文档:developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVAudioRecorder_ClassReference/Reference/Reference.html#//apple_ref/occ/cl/AVAudioRecorder
相关内容
- 使用 MediaPlayer 框架播放音乐 的教程
视图动画
在本教程中,我们将学习如何使用 UIKit 动画在屏幕上移动 UILabel 控件。
准备工作
在我们之前的教程基础上,创建一个新的 单视图应用程序,并将其命名为 AnimateViewExample。
如何实现...
首先,按照以下顺序执行以下简单步骤:
- 
从 项目导航器 窗口选择 ViewController.xib文件。
- 
从 对象库 窗口,将一个 UILabel对象拖放到视图中。
- 
调整 UILabel控件的大小,使其内容填充视图的宽度。
- 
接下来,为这个 UILabel创建一个出口,并将其命名为lblAnimateMsg。
- 
接下来,从 对象库 窗口,将一个 UIButton对象拖放到视图中,并将其放置在我们之前添加的UILabel下方。
- 
在 属性检查器 部分中,修改 标题 属性,使其显示为 开始。 
- 
接下来,为这个 UIButton创建一个出口,并将其命名为btnAnimate。
- 
接下来,为 开始 按钮创建动作方法,并将其命名为 doAnimation。
- 
在添加按钮后,通过从菜单栏选择 文件 | 保存 来保存文档。 
- 
我们接下来的步骤是创建代码功能,该功能将在按下 开始 按钮时负责动画 UILabel控件。
- 
从 项目导航器 窗口打开 ViewController.m实现文件。
- 
创建以下变量声明,如代码片段中突出显示: // ViewController.m // AnimateViewExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "ViewController.h" @interface ViewController () @end @implementation ViewController @synthesize lblAnimateMsg; @synthesize btnAnimate; CGRect origPos;
- 
接下来,修改 viewDidLoad:方法,如下代码片段所示:- (void)viewDidLoad { [super viewDidLoad]; // Set the color of our Animation Label and disable button lblAnimateMsg.text = @"Animating Views Example"; lblAnimateMsg.textColor = [UIColor yellowColor]; [self.view setBackgroundColor:[UIColor blackColor]]; }
- 
接下来,修改 doAnimation:方法,如下代码片段所示:#pragma mark Perform the animation of our Label - (IBAction)doAnimation:(id)sender { // Save the original position of our Label origPos = lblAnimateMsg.frame; CGRect AnimateFrame = origPos; // Initialize and specify the height of our frame AnimateFrame.size.height = 700; // Set the color of our Animation Label and disable button lblAnimateMsg.text = @"Animating Views Example"; lblAnimateMsg.textColor = [UIColor yellowColor]; btnAnimate.enabled = NO; // Set up our Animations [UIView beginAnimations:@"AnimationBegin" context:nil]; [UIView setAnimationDuration:5]; [UIView setAnimationDelegate:self]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDidStopSelector: @selector(doAnimationStop)]; // Increase the frame of our Animation message self.lblAnimateMsg.frame = AnimateFrame; [UIView commitAnimations]; }
- 
接下来,创建 doAnimationStop:方法,如下代码片段所示:#pragma mark Method is called when animation stops. -(void)doAnimationStop { lblAnimateMsg.frame = origPos; btnAnimate.enabled = YES; lblAnimateMsg.text = @"Animation has Completed."; lblAnimateMsg.textColor = [UIColor greenColor]; }
- 
然后,通过从 产品 菜单选择 产品 | 运行 或按 Command + R 来 构建 和 运行 应用程序。 
当编译完成后,iOS 模拟器将出现。点击 开始 按钮以查看标签开始动画,并在动画完成后显示消息。
工作原理...
在本教程中,我们探讨了如何使用 UIKit 框架在视图中的对象上执行动画。我们首先保存标签的原始位置,并将其存储在一个名为 origPos 的变量中,然后创建一个新的变量 AnimateFrame,该变量将用于指定标签将动画到的具体高度。
在我们的下一步中,我们开始使用 beginAnimations 方法标记动画块的开始,然后指定动画的名称,在将图形上下文设置为 nil 之前。接下来,我们使用 setAnimationDuration 属性指定动画的长度(以秒为单位),然后设置动画代理对象。
接下来,我们通过使用 setAnimationCurve 方法定义动画开始和结束点将应用到的默认缓动函数。然后,我们设置一个选择器对象,当动画完成时将被调用。最后,我们使用 commitAnimations 方法停止动画块,以标记动画的结束。最后,在我们的 doAnimationStop: 方法中,我们将标签控件恢复到下一次循环的原始位置。
注意
关于 UIKit 框架的更多信息,您可以参考以下 Apple 开发者文档位置:developer.apple.com/library/ios/#documentation/UIKit/Reference/UIKit_Framework/_index.html
参见
- 
使用 Xcode 创建 iOS 项目中的 使用 Xcode 创建 iOS 项目 菜单,获取和安装 iOS SDK 开发工具 
- 
第一章中的 创建到 Interface Builder 对象的出口 菜单,获取和安装 iOS SDK 开发工具 
绘制自定义文本
在本菜谱中,我们将看看如何轻松地在视图中绘制带有轮廓的样式化文本。
准备工作
在我们之前的菜谱基础上,创建一个新的 单视图应用程序,并将其命名为 DrawTextExample。
如何做...
首先,按照以下顺序遵循以下简单步骤:
- 
选择 DrawTextExample文件夹,选择 文件 | 新建 | 文件…
- 
从可用的模板列表中选择 Objective-C 类 模板。 
- 
点击 下一步 按钮以进入向导的下一步。 
- 
输入 DrawTextView作为要创建的类名。
- 
确保您已从 子类类型 下拉列表中选择 UIView作为要创建的子类的类型。
- 
点击 下一步 按钮以进入向导的下一步。 
- 
然后,点击 创建 按钮以将文件保存到指定的文件夹位置。 
- 
我们已成功完成创建我们的 DrawTextView类。我们的下一步是实现该类使用的功能和方法。
- 
从 项目导航器 窗口中打开 DrawTextView.m实现文件。
- 
修改如下代码片段所示的 drawRect:方法。- (void)drawRect:(CGRect)rect { // Set up and initialize our Graphics Context CGContextRef context = UIGraphicsGetCurrentContext(); CGContextClearRect(context, rect); // Specify the text color and font fill style CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor); CGContextSetTextDrawingMode(context, kCGTextFillStroke); // Finally, draw the text using the specified font. NSString *theString = @"GENIESOFT STUDIOS..."; [theString drawAtPoint:CGPointMake(20.0f, 100.0f) withFont:[UIFont fontWithName:@"Verdana-Bold" size:25]]; }
我们的下一步是创建代码功能,该功能将负责将我们的自定义视图作为子视图添加到现有的视图控制器中。
- 
从Project Navigator窗口打开 ViewController.m实现文件。
- 
在代码片段中突出显示以下类引用: // ViewController.m // TextDrawExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "ViewController.h" #import "DrawTextView.h"
- 
接下来,按照以下代码片段修改 viewDidLoad:方法:- (void)viewDidLoad { [super viewDidLoad]; // Declare and add our custom view as a subView // to the current view. DrawTextView *drawTextView = [[DrawTextView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:drawTextView]; }
- 
然后,通过从Product菜单中选择Product | Run或按Command + R,构建和运行应用程序。 
编译完成后,iOS 模拟器出现并显示在这个菜谱中创建的文本。图像已在设备中旋转。

工作原理...
在这个菜谱中,我们创建了一个新的自定义DrawTextView类,它继承自UIView类。然后我们修改了drawRect:方法,该方法将用于将文本绘制到视图中。然后我们声明了变量context,它将使用UIGraphicsGetCurrentContext函数来获取当前的图形上下文。
接下来,我们使用CGContextClearRect函数清除视图,并设置文本的颜色,将FillStroke值传递给我们的CGContextSetTextDrawingMode方法,然后调用我们的drawAtPoint:方法来使用UIFont类指定的字体细节绘制文本。最后,我们初始化我们的DrawTextView自定义类,并将其作为子视图添加,尺寸与父视图控制器相同。
注意
关于CoreGraphics类的更多信息,您可以参考以下网址的 Apple 开发者文档位置:developer.apple.com/library/ios/#documentation/coregraphics/reference/coregraphics_framework/_index.html
参见
- 绘制线条和曲线菜谱
绘制线条和曲线
在这个菜谱中,我们将看看如何轻松地将线条和曲线绘制到视图中。
准备工作
在我们之前的菜谱之后,创建一个新的Single View Application,并将其命名为LinesCurvesExample。创建一个自定义视图,并将其命名为LinesCurvesView。
如何做...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从Project Navigator窗口打开 LinesCurvesView.m实现文件。
- 
按照以下代码片段修改 drawRect:方法:- (void)drawRect:(CGRect)rect { // Set up and initialize our Graphics Context CGContextRef context = UIGraphicsGetCurrentContext(); CGContextClearRect(context, rect); // Draw our Line to our View CGContextSetLineWidth(context, 5); CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor); CGContextMoveToPoint(context, 0, 0); CGContextAddLineToPoint(context, self.bounds.size.width, self.bounds.size.height); CGContextStrokePath(context); // Then, draw and Add the Curve CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); CGContextMoveToPoint(context, 0, self.bounds.size.height); CGContextAddCurveToPoint(context, 0, self.bounds.size.height, 5, self.bounds.size.height / 2, self.bounds.size.width, 0); CGContextStrokePath(context); }
- 
我们的下一步是创建代码功能,该功能将负责将我们的自定义视图作为子视图添加到现有的视图控制器中。 
- 
从Project Navigator窗口打开 ViewController.m实现文件。
- 
添加对之前所做的 LinesCurvesView.h接口文件的引用。
- 
接下来,按照以下代码片段修改 viewDidLoad:方法:- (void)viewDidLoad { [super viewDidLoad]; // Declare and add our custom view as a subView // to the current view. LinesCurvesView *linesCurvesView = [[LinesCurvesView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:linesCurvesView]; }
- 
然后,通过从Product菜单中选择Product | Run或按Command + R,构建和运行应用程序。 
当编译完成时,iOS 模拟器将出现。以下截图显示了我们在本菜谱中创建的线条和曲线。我已经在设备内旋转了这张图片。

它是如何工作的...
在这个菜谱中,我们创建了一个新的自定义linesCurvesView类,它继承自UIView类。然后我们修改了drawRect:方法,并继续声明一个变量context,该变量用于使用UIGraphicsGetCurrentContext函数获取当前的图形上下文,就像我们在先前的示例中所做的那样。
在我们的下一步中,我们继续设置线宽、颜色,并使用CGContextAddLineToPoint方法添加线条,该方法接受每条线的起始点和终点,并调用CGContextStrokePath方法将线条显示到视图中。
为了将曲线添加到我们的视图中,我们相应地重复这些步骤,然后调用CGContextMoveToPoint方法,该方法移动当前点,然后我们调用CGContextAddCurveToPoint方法,以便在由CGContextMoveToPoint方法确定的当前点处发生曲线。
接下来,我们调用CGContextStrokePath方法将曲线显示到视图中。最后,我们初始化我们的LinesCurvesView自定义类,然后使用与父视图控制器相同的尺寸将其添加为子视图。
相关内容
- 
绘制自定义文本菜谱 
- 
在第二章的添加和自定义视图菜谱中,用户界面 - 创建用户界面 
绘制和填充形状
在这个菜谱中,我们将看看如何在屏幕上绘制和填充一系列形状是多么容易。
准备工作
在我们之前的菜谱基础上,创建一个新的单视图应用程序,并将其命名为ShapesDrawExample。创建一个自定义视图,并将其命名为ShapesView。
如何做...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 ShapesView.m实现文件。
- 
修改以下代码片段中的 drawRect:方法。- (void)drawRect:(CGRect)rect { // Set up and initialize our Graphics Context CGContextRef context = UIGraphicsGetCurrentContext(); CGContextClearRect(context, rect); // Draw our Circle to our view CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor); CGContextAddEllipseInRect(context, CGRectMake(50,50,150,150)); CGContextFillPath(context); // Draw Square to the Screen CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor); CGContextAddRect(context, CGRectMake(50,200,200,200)); CGContextFillPath(context); // Draw a Triangle to the Screen CGContextMoveToPoint (context, 150, 100); // Top left CGContextAddLineToPoint(context, 300, 100); // Middle right CGContextAddLineToPoint(context, 150, 300); // Bottom left CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor); CGContextFillPath(context); }
- 
我们下一步是创建代码功能,该功能将负责将我们的自定义视图作为子视图添加到现有的视图控制器中。 
- 
从项目导航器窗口打开 ViewController.m实现文件。
- 
添加对之前所做的 ShapesView.h接口文件的引用。
- 
接下来,修改以下代码片段中的 viewDidLoad:方法:- (void)viewDidLoad { [super viewDidLoad]; // Declare and add our custom view as a subView // to the current view. ShapesView *shapesView = [[ShapesView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:shapesView]; }
- 
然后,通过从产品菜单中选择产品 | 运行或按Command + R来构建和运行应用程序。 
当编译完成时,iOS 模拟器将出现。以下截图显示了我们的每个着色形状都已填充。我已经在设备内旋转了这张图片。

它是如何工作的...
在这个菜谱中,我们创建了一个新的自定义 ShapesView 类,它继承自 UIView 类。然后我们修改了 drawRect: 方法,并声明了一个变量 context,用于使用 UIGraphicsGetCurrentContext 函数获取当前的图形上下文,就像我们在之前的例子中做的那样。在我们的下一步中,我们通过使用 CGContextSetFillColorWithColor 方法设置圆的填充颜色,然后调用 CGContextAddEllipseInRect 方法,并传递 CGRectMake 对象。CGRectMake 方法定义了圆的边界矩形。接下来,我们调用 CGContextFillPath 方法,该方法填充对象,并将其显示到屏幕上。
每当你需要定义一个矩形时,你需要使用 CGContextAddRect 方法,并使用 CGRectMake 方法指定尺寸,就像我们在定义圆时做的那样。接下来,我们通过使用 CGContextMoveToPoint 方法指定三角形的第一点来定义我们的三角形,然后对构成三角形的每个顶点调用 CGContextAddLineToPoint 方法,在指定三角形的颜色后,使用 CGContextFillPath 方法将三角形对象显示到屏幕上。最后,我们初始化我们的 LinesCurvesView 自定义类,然后使用与父视图控制器相同的尺寸将其添加为子视图。
相关内容
- 
绘制线条和曲线 菜谱 
- 
在 第二章 的 添加和自定义视图 菜谱中,用户界面 - 创建用户界面 
使用 Core Image 应用颜色效果
在这个菜谱中,我们将看看我们如何使用 Core Image 将颜色效果应用到图像上。
准备工作
在我们之前的菜谱之后,创建一个新的 单视图应用程序,并将其命名为 ColorEffectsExample。
如何做...
要开始,请按照以下顺序遵循以下简单步骤:
- 
将 CoreImage.framework添加到你的项目中。
- 
从 项目导航器 窗口中选择 ViewController.xib文件。
- 
从 对象库 窗口中,将一个 UIImageView对象拖放到视图中。
- 
根据需要调整控件的大小,使其占据整个屏幕区域,并在 属性检查器 选项卡中将图像视图 模式 设置为 缩放填充。 
- 
接下来,为这个 UIImageView创建出口,并将其命名为imageView。
- 
我们下一步要创建的代码功能将负责将各种颜色效果应用到图像上。 
- 
从 项目导航器 窗口中打开 ViewController.h接口文件。
- 
接下来,修改接口文件,如下面的代码片段中突出显示的代码所示: // ViewController.h // ColorEffectsExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #define IMAGENAME @"Frederick_Prince_of_Wales.jpg" @interface ViewController : UIViewController<UIActionSheetDelegate> // Declare the Getters and Setters for each of our objects. @property (strong, nonatomic) IBOutlet UIImageView *imageView;
- 
从 项目导航器 窗口中打开 ViewController.m实现文件,并输入以下突出显示的代码部分:@synthesize imageView;
- 
接下来,修改 viewDidLoad:方法,如下面的代码片段所示:- (void)viewDidLoad { [super viewDidLoad]; // Set up and initialize our ImageView Controller. self.imageView.image = [UIImage imageNamed:IMAGENAME]; self.imageView.contentMode = UIViewContentModeScaleAspectFill; }
- 
接下来,创建如代码片段中所示的 touchesBegan:方法:#pragma mark method called when the user taps the screen. -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // Initialize our Action Sheet with options UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Available Transitions" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Close" otherButtonTitles:@"Vibrance",@"Sepia Tone",@"Reset", nil]; // Display the actionsheet to the view. [actionSheet showInView:self.view]; }
- 
接下来,创建如代码片段中所示的 actionSheet:方法:#pragma mark Delegate which handles the option buttons selected - (void)actionSheet:(UIActionSheet *)actionSheetclickedButtonAtIndex:(NSInteger)buttonIndex { CIImage*inputImage = [CIImage imageWithCGImage:[self.imageView.image CGImage]]; CIContext *context = [CIContextcontextWithOptions:nil]; CIImage *outputImage; CIFilter *filter; // Determine the button selected and handle accordingly. switch (buttonIndex) { case 1: // Adjusts the saturation of the image filter = [CIFilter filterWithName:@"CIVibrance"]; [filter setDefaults]; [filter setValue: inputImage forKey: @"inputImage"]; [filter setValue: [NSNumber numberWithFloat:1.00] forKey: @"inputAmount"]; outputImage = [filter valueForKey: @"outputImage"]; break; case 2: // Set the Septia Tone of the image filter = [CIFilter filterWithName:@"CISepiaTone"]; [filtersetDefaults]; [filter setValue: inputImage forKey:@"inputImage"]; [filter setValue: [NSNumber numberWithFloat:0.8] forKey: @"inputIntensity"]; outputImage = [filter valueForKey:@"outputImage"]; break; case 3: // Reset our image to the original state self.imageView.image = [UIImage imageNamed:IMAGENAME]; break; default: NSLog(@"Unsupported option detected."); break; } // Don't process if the Close and Cancel buttons are pressed if (buttonIndex> 0 &&buttonIndex< 3) { self.imageView.image = [UIImage imageWithCGImage: [context createCGImage:outputImage fromRect:outputImage.extent]]; } }
- 
然后,通过从产品菜单中选择产品 | 运行或按Command + R来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现。轻触 iOS 设备屏幕以查看各种图像选项。以下截图显示了应用于图像的棕褐色调效果。

它是如何工作的...
在这个配方中,我们首先通过扩展我们的类以包括UIActionSheetDelegate类的类协议,以便我们可以访问协议的相关方法。然后,我们通过渲染由我们的#define指令定义的默认图像来初始化我们的imageView控件,并创建了touchesBegan:方法,初始化了一个设置可选列表的actionSheet变量,并将这些选项应用于图像。接下来,我们创建了actionSheet:clickedButtonAtIndex:方法,该方法将用于确定从动作表选项面板中选择了哪个按钮,该按钮是通过buttonIndex属性派生的。接下来,我们声明了一个CIContext变量context。这个变量将用于将cImage图像对象渲染到视图中。
我们随后声明了一个类型为CIImage的inputImage变量对象,它包含指向imageView中图像的指针,并声明了一个CIImageoutputImage变量,该变量将用于应用图像过滤器更改,然后输出修改后的imageView控件。我们随后声明了一个名为filter的CIFilter变量,它将包含要使用的过滤器效果类型。
注意
关于CoreImage 过滤器的更多信息,请参阅以下链接:developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CoreImageFilterReference/Reference/reference.html
相关内容
- 应用过渡效果配方
应用过渡效果
在这个配方中,我们将探讨如何应用过渡效果以创建图像的水波纹效果。
准备工作
在我们之前的配方基础上,创建一个新的单视图应用程序,并将其命名为TransitionsExample。
如何操作...
首先,按照以下顺序执行以下简单步骤:
- 
将 QuartzCore.framework引用添加到您的项目中。
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库窗口中,将一个 UIImageView对象拖放到视图中。
- 
根据需要调整控件的大小,使其占据整个屏幕区域,并在属性检查器选项卡中将图像视图模式设置为填充。 
- 
接下来,为这个 UIImageView创建一个出口,并将其命名为imageView。
- 
我们下一步要创建的代码功能将负责将过渡效果应用到图像上。 
- 
从项目导航器窗口打开 ViewController.m实现文件。
- 
包含代码片段中突出显示的 QuartzCore类引用:// ViewController.m // TransitionsExample // Created by Steven F Daniel on 20/11/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "ViewController.h" #import "QuartzCore/QuartzCore.h" @synthesize imageView;
- 
接下来,修改如以下代码片段所示的 viewDidLoad:方法:- (void)viewDidLoad { [super viewDidLoad]; // Set up and initialize our ImageView Controller. self.imageView.image = [UIImage imageNamed:@"Prince_Frederick.jpg"]; self.imageView.contentMode = UIViewContentModeScaleAspectFill; }
- 
接下来,创建如代码片段所示的 touchesBegan:方法:#pragma mark method called when the user taps the screen. -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // Applies the Ripple Effect transition to the view. CATransition *animation = nil; animation = [CATransition animation]; [animation setDelegate:self]; [animation setDuration:3.0f]; [animation setType:@"rippleEffect" ]; [self.view.layer addAnimation:animation forKey:NULL]; }
- 
然后,通过选择产品菜单中的产品 | 运行或按Command + R组合键来构建和运行应用程序。 
当编译完成后,iOS 模拟器将出现。轻触 iOS 设备屏幕,可以看到应用在图像上的水波纹效果,如图所示,图像已在设备内部旋转。

它是如何工作的...
在这个配方中,我们学习了如何轻松地将过渡效果应用到视图中的图像。我们首先声明一个名为 animation 的变量,该变量将负责处理UIView层的过渡动画。在下一步中,我们指定了涟漪效果的持续时间,这将用来定义动画的单次迭代显示需要多少秒。
接下来,我们创建touchesBegan:方法,当用户将手指放在屏幕上时会被调用。然后,我们指定我们想要使用的动画类型是rippleEffect过渡效果,并将动画效果应用到我们的视图中。
注意
关于QuartzCore框架的更多信息,请参考以下 URL:developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/QuartzCoreRefCollection/_index.html
参见
- 使用 Core Image 应用颜色效果配方
第八章. 使用 CoreData 和 GameKit 框架
在本章中,我们将涵盖:
- 
添加 GameKit 和 MessageUI 框架 
- 
构建 Core Data 数据模型 
- 
创建 Core Data 模型文件 
- 
添加和配置 Storyboard 
- 
创建 Books Library 用户界面 
- 
在表格视图中显示数据 
- 
在我们的 Core Data 数据模型中插入数据 
- 
使用 Core Data 从表格视图中删除项目 
- 
在表格视图中重新排序行 
- 
在表格视图中过滤和搜索数据 
- 
使用不同的键盘样式 
- 
使用蓝牙将数据传输到另一台设备 
- 
实现电子邮件消息 
简介
iPhone 和 iPad 设备都内置了蓝牙功能,允许它与其他蓝牙设备通信,例如其他 iOS 设备或蓝牙兼容的耳机。在本章中,我们将探讨如何创建一个简单的BooksLibrary应用程序,利用苹果强大的Core Data框架,允许您直接通过表单与SQLite数据库接口创建和存储客户信息。
然后,我们将看看如何将蓝牙功能集成到您的应用程序中,以便您可以通过与其他 iOS 设备通信来发送此信息,并且这些信息可以在另一端无线接收并存储在数据库中。
这一切一开始可能听起来有些令人困惑,但很快你就会发现,通过使用 iOS SDK,蓝牙编程实际上相当简单,并且这个功能被很好地封装在GameKit框架中。最后,我们将探讨如何使用MessageUI框架和MFMailComposeViewController类发送包含在表格视图中选择的书本详情的电子邮件消息,以及学习如何在UITableView控件中重新排序和删除记录。
添加 GameKit 和 MessageUI 框架
在本食谱中,我们将学习如何添加GameKit和MessageUI框架,以提供使用蓝牙发送信息以及发送电子邮件的能力。
准备工作
为了继续,我们需要创建一个新的应用程序并将框架添加到我们的项目中。
如何操作...
首先,按照以下顺序遵循以下简单步骤:
- 
从 /Xcode4/Applications文件夹启动Xcode。
- 
选择创建一个新的 Xcode 项目,或文件 | 新 | 项目…。 
- 
从可用的模板列表中选择Empty Application模板。 
- 
点击Next按钮继续向导的下一步。 
- 
将项目名称输入为BooksLibrary。 
- 
在Devices下拉菜单下选择iPhone。 
- 
确保已勾选Use Core Data复选框。 
- 
确保已勾选Use Automatic Reference Counting复选框。 
- 
确保未勾选Include Unit Tests复选框。 
- 
点击 下一步 按钮继续到向导的下一步,并指定您想要保存项目的位置。 
- 
然后,点击 创建 按钮继续,并显示 Xcode 工作区。 
- 
接下来,将 GameKit.framework和MessageUI.framework框架添加到您的项目中。
工作原理...
在本菜谱中,我们学习了如何将 GameKit 和 MessageUI 框架添加到我们的项目中,这将使我们能够执行通过蓝牙网络向其他 iOS 设备传输信息的能力,以及使用 MFMailComposeViewController 类在我们的应用程序中发送电子邮件消息的能力。
相关内容
- 
构建核心数据数据模型 菜单 
- 
在 第五章 的 添加 CoreLocation 和 MapKit 框架 菜单中,位置服务和地图 
构建核心数据数据模型
在本菜谱中,我们将学习如何使用核心数据模型编辑器为我们的 BooksLibrary 应用程序构建数据库模式。
准备工作
在上一个菜谱的基础上,确保我们的 BooksLibrary 项目文件已打开。
如何操作...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从 项目导航器 中选择 BooksLibrary.xcdatamodel文件。![如何操作...]() 
- 
接下来,点击 +添加实体 按钮,并将此实体命名为 Books。 
- 
然后点击 +添加属性 按钮,或者从 属性 面板中。 
- 
创建每个 属性 和 类型,如以下表格所示: 属性 类型 authorStringdisplayOrderInteger 16publisherStringtitleString
- 
使用 文件 | 保存 保存您的项目,因为我们已经定义了数据库表模式。 
工作原理...
核心数据数据库模型存储在 BooksLibrary.xcdatamodel 中,位于 项目导航器 窗口内的 BooksLibrary 组中。在本菜谱中,我们学习了如何定义我们的 SQLite 数据库的数据库模式,并创建实体(表)和属性(字段),这将使我们的应用程序能够将这些字段写入数据库,以便以后可以查询。
在我们的下一个菜谱中,我们将探讨如何创建允许我们访问表定义的核心数据模型文件。
相关内容
- 创建核心数据模型文件 菜单
创建核心数据模型文件
在本菜谱中,我们将学习如何为我们的 BooksLibrary 数据库模式创建相关的核心数据模型文件对象定义。
准备工作
在上一个菜谱的基础上,确保我们的 BooksLibrary 项目文件已打开。
如何操作...
在我们的应用程序开始使用我们的 BooksLibrary 数据库之前,我们需要创建实体类定义,这些定义将定义数据库存储中包含的变量,以便我们可以通过代码访问它们。
- 
从 项目导航器 窗口中选择 BooksLibrary.xcdatamodel文件。
- 
选择 文件 | 新建 | 文件… 或按 Command + N。 
- 
接下来,从 iOS 组中选择 Core Data。 
- 
然后,从可用模板列表中选择 NSManagedObject 子类。 ![如何操作...]() 
- 
点击 下一步 按钮继续向导中的下一步。 
- 
点击 创建 按钮将文件保存到指定的文件夹位置。 
- 
接下来,我们需要定义我们想要为它们创建 NSManagedObject类的实体。![如何操作...]() 
- 
从 选择您要管理的实体 列表中选择 Books 实体,然后点击 下一步 按钮继续向导中的下一步。 
- 
确保未勾选 对于原始数据类型使用标量属性 选项,然后点击 创建 按钮以生成 NSManagedObject类文件。
工作原理...
在这个菜谱中,我们了解了 NSManagedObject 类,以及我们如何使用这个类来定义在 Core Data 存储中创建的 Books 实体的类,以及定义表模式字段,以便当我们想要使用 Books 类时,我们可以在运行时访问属性。
Core Data 模型向导为我们生成了两个文件。Books.h 接口文件和 Books.m 实现文件。Books.h 接口文件包含我们每个实体属性字段,每个字段都是根据其对象类型声明的,而 Books.m 实现文件包含我们每个实体属性字段,每个字段都被声明为 dynamic。这定义了实体属性属性,以便在向 Core Data 模型写入或从 Core Data 模型检索数据时可以使用。
注意
关于动态数据类型的信息,您可以参考以下 Apple 开发者文档:developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreData/Articles/cdAccessorMethods.html%23//apple_ref/doc/uid/TP40002154-SW9
更多信息...
在使用 Core Data 时需要注意的一点是,如果你尝试向数据模型模式中添加新字段,你的应用程序将会崩溃。你需要重新生成 NSManagedObject 文件,然后重置模拟器或从 iOS 设备中删除应用程序。
相关内容
- 添加和配置 Storyboard 菜谱
添加和配置 Storyboard
准备工作
在这个菜谱中,我们将学习如何使用 Xcode 添加和配置应用程序的项目属性,以便正确设置以使用 Storyboard 文件。
如何操作...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从 项目导航器 窗口中选择 BooksLibrary 项目。 
- 
选择 文件 | 新建 | 文件… 或按 Command + N 
- 
从 iOS 部分下的 用户界面 选项中,从可用的模板列表中选择 Storyboard 模板。 
- 
点击 下一步 按钮继续向导的下一个步骤。 
- 
确保您已从 设备家族 下拉菜单中选择 iPhone。 
- 
点击 下一步 按钮继续向导的下一个步骤。 
- 
在 另存为 字段中指定 MainStoryboard.storyboard作为 Storyboard 文件的名称,作为要创建的文件名称。
- 
点击 创建 按钮将文件保存到指定的文件夹中。 
- 
我们接下来的步骤是手动配置我们的项目,使其能够识别并使用此文件来构建我们的应用程序。这可以通过以下步骤实现: 
- 
从 项目导航器 窗口中选择 BooksLibrary 项目。 
- 
接下来,从 TARGETS 组中选择您的项目目标,并选择 摘要 选项卡。 ![如何操作...]() 
- 
从 主 Storyboard 下拉菜单中选择 MainStoryboard。 
它是如何工作的…
在本食谱中,我们学习了如何手动将新的 Storyboard 模板添加到项目中。然后我们查看如何配置项目属性,以便应用程序能够使用 Storyboard 用户界面文件。
更多内容…
最后,当使用 Storyboards 时,我们不需要创建一个新的 UIWindow,因为这将会创建另一个窗口实例,并将其放置在 Storyboard 之上。我们需要修改应用程序的代理类 AppDelegate.m,如下面的代码片段所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
   // Override point for customization after 
   // application launch.
   return YES;
}
}
注意
有关在应用程序中使用 Storyboards 的更多信息,您可以参考位于:developer.apple.com/library/ios/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/InterfaceBuilder/InterfaceBuilder
相关信息
- 创建书籍库用户界面 食谱
创建书籍库用户界面
在本食谱中,我们将学习如何构建我们的书籍库项目的用户界面,以及创建一个自定义的表格视图控制器作为数据源。
准备中…
在本节中,我们将首先构建将构成我们应用程序用户界面的组件。
如何操作…
首先,按照以下顺序执行以下简单步骤:
- 
在 项目导航器 窗口中,选择名为 MainStoryboard.storyboard的文件。
- 
从 对象库 中选择并拖动一个新的 ( UITableViewController) 表格视图控制器 控件,并将其添加到视图中。
- 
接下来,选择我们刚刚添加的 UITableViewController控件,然后选择 编辑 | 嵌入 | 导航控制器。
- 
在Navigation Controller窗口中,确保在Navigation Controller窗口的Bar Visibility部分下,Shows Navigation Bar和Shows Toolbar都已被勾选。 
- 
接下来,选择 UITableViewController控件,然后点击Table View Cell标签,然后从Prototype Cells部分选择Prototype单元格。
- 
在Attributes Inspector部分,将Style更改为Subtitle。这将改变单元格的外观,使其包含两个标签。 
- 
选择Identifier项目,并输入BookCell作为其唯一标识符。 
- 
接下来,从Object Library窗口中选择并拖动一个( UIBarButtonItem),并将其添加到我们的Table View Controller导航栏的左侧。
- 
在Attributes Inspector部分,将Title的值更改为Add。 
- 
在我们的Table View Controller的导航栏右侧添加另一个 UIBarButtonItem,并将Title的值更改为Sort Order。
- 
在我们的Table View Controller的导航栏底部左端添加另一个 UIBarButtonItem,并将Title的值更改为Connect。
- 
在Connect按钮旁边添加一个Flexible Space Bar Button项目。 
- 
在我们的Table View Controller的导航栏底部右端添加另一个 UIBarButtonItem,并将Title的值更改为Transfer。
- 
接下来,我们需要创建并设置我们自己的自定义 UITableViewController子类,该子类将作为我们表格的数据源。
- 
选择 BooksLibary文件夹,选择File | New | File…或按Command + N。
- 
从Cocoa Touch组中选择Objective-C class模板。 
- 
点击向导中的Next按钮以进入下一步。 
- 
输入BooksViewController作为要创建的文件名。 
- 
确保您已从Subclass下拉菜单中选择UITableViewController作为要创建的子类的类型。 
- 
接下来,点击向导中的Next按钮以进入下一步,然后点击Create按钮以保存文件。 
- 
接下来,从Project Navigator窗口中选择 MainStoryboard.storyboard文件。
- 
选择 UITableViewController控件,并点击Identity Inspector部分,将Custom Class属性的值更改为BooksViewController。
- 
接下来,我们将为每个按钮创建和设置 outlets和action方法,名称如下:btnAdd、btnSortOrder、btnConnect和btnTransfer。
接下来,我们需要为NSManagedObjectContext和NSFetchedResultsController对象设置引用。
- 
从Project Navigator窗口中打开 BooksViewController.h接口文件。
- 
接下来,根据突出显示的代码部分修改接口文件。 // BooksViewController.h // BooksLibrary // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import "Books.h" @interface BooksViewController : UITableViewController { NSManagedObjectContext *managedObjectContext; NSFetchedResultsController *fetchedResultsController; NSArray *fetchedObjects; IBOutlet UIBarButtonItem *btnAdd; IBOutlet UIBarButtonItem *btnSortOrder; IBOutlet UIBarButtonItem *btnConnect; IBOutlet UIBarButtonItem *btnTransfer; // Fields for our btnAdd UIAlertView dialog UITextField *bookTitle; UITextField *bookAuthor; UITextField *bookPublisher; } // Core Data session objects @property (strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; // Property getters and setters for our UI @property (strong, nonatomic) IBOutlet UIBarButtonItem *btnAdd; @property (strong, nonatomic) IBOutlet UIBarButtonItem *btnSortOrder; @property (strong, nonatomic) IBOutlet UIBarButtonItem *btnConnect; @property (strong, nonatomic) IBOutlet UIBarButtonItem *btnTransfer; // Create the class instance methods -(void)populateBookDetails; - (IBAction)btnAdd:(id)sender; - (IBAction)btnSortOrder:(id)sender; - (IBAction)btnConnect:(id)sender; - (IBAction)btnTransfer:(id)sender; @end
- 
接下来,打开位于BooksLibrary文件夹中的 AppDelegate.m实现文件,并输入以下突出显示的代码。// AppDelegate.m // BooksLibrary // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "AppDelegate.h" #import "BooksViewController.h" @implementationAppDelegate @synthesize managedObjectContext = _managedObjectContext; @synthesize managedObjectModel = _managedObjectModel; @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- 
接下来,修改位于 AppDelegate.m实现文件中的didFinishLaunchingWithOptions方法。- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Point our Books View Controller to our data-model UINavigationController *navigationController = (UINavigationController *) self.window.rootViewController; BooksViewController *booksViewController = [[navigationController viewControllers]objectAtIndex:0]; booksViewController.managedObjectContext = self.managedObjectContext; return YES; }
- 
然后,通过从产品菜单选择产品 | 运行或按Command + R来构建和运行应用程序。 
它是如何工作的...
在这个菜谱中,我们首先构建了我们的BooksLibrary应用程序的用户界面,以及正确配置了我们的表格视图控制器。然后,我们创建了自己的自定义UITableViewController子类,它将作为表格的数据源,以便在从数据库检索信息时知道要显示多少行。
接下来,我们将我们的UITableViewController类的类型更新为使用我们新创建的类,而不是默认的UITableViewController类,并包含了NSManagedObjectContext和NSFetchedResultsController对象的引用,这些对象为我们提供了所有与 Core Data 数据检索相关的函数,这些函数在填充我们的表格视图时需要执行。这些函数封装了与表格和 Core Data 模型相关联的常用函数。我们创建了一个NSArray数组属性,用于存储检索到的数据。
最后,我们使用managedObjectContext方法初始化了booksViewController的数据源。这确保了我们的控制器可以访问所有必要的属性和方法,以便从我们的数据模型中添加和检索信息。在此之前,我们需要首先遍历 Storyboard 中的每个场景,以便获取对BooksViewController的引用。这样我们就可以初始化其数据源,使其指向我们的数据库。
参见
- 在表格视图中显示数据的菜谱
在表格视图中显示数据
在这个菜谱中,我们将学习如何用一些数据填充表格视图控制。
准备工作
在我们之前的菜谱基础上,我们将学习如何从我们的 Core Data 数据库模型中填充我们的UITableViewControl。
如何做到这一点...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 BooksViewController.m实现文件。
- 
接下来,根据高亮显示的代码部分修改实现文件。 // BooksViewController.m // BooksLibrary // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "BooksViewController.h" @implementation BooksViewController @synthesize fetchedResultsController; @synthesize managedObjectContext; @synthesize btnAdd; @synthesize btnSortOrder; @synthesize btnConnect; @synthesize btnTransfer;
- 
根据高亮显示的代码部分修改 viewDidLoad:和viewDidAppear:方法。#pragma mark - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; // Initialize and reload Book Details. self.title = @"Book Details"; [self populateBookDetails]; } #pragma mark reloads our book details when our view reappears - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self populateBookDetails]; }
- 
接下来,根据代码片段中的说明创建以下代码部分。 #pragma mark Populate our table view with our database records -(void)populateBookDetails { // Define our table/entity name to use NSEntityDescription *entity = [NSEntityDescription entityForName:@"Books" inManagedObjectContext:managedObjectContext]; // Set up the fetch request NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setEntity:entity]; // Define how we are to sort the records NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"displayOrder" ascending:YES]; NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; [fetchRequest setSortDescriptors:sortDescriptors]; // Define the FetchResults controller fetchedResultsController = [[NSFetchedResultsControlleralloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"]; // Fetch the records and handle any errors NSError *error = nil; if (![[self fetchedResultsController] performFetch:&error]) { NSLog(@"There was an error retrieving the book details."); } // Number of rows to populate our Table View controller with. fetchedObjects = fetchedResultsController.fetchedObjects; [self.tableView reloadData]; } #pragma mark - Table view data source -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ // Return the number of sections. return 1; } #pragma mark Return the number of rows in the section. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [fetchedObjects count]; } #pragma mark populate our tableview for each book added - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"BookCell"; Books *bookDetails; // Populate our tableView with all items from our resultset bookDetails = [fetchedResultsController objectAtIndexPath:indexPath]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; } // Configure the cell... cell.textLabel.text = bookDetails.title; cell.detailTextLabel.text = bookDetails.author; cell.showsReorderControl = YES; [tableView setAllowsSelection:YES]; return cell; }
它是如何工作的...
在这个菜谱中,我们首先为我们的导航栏设置标题,然后调用populateBookDetails方法,该方法将用于将数据库对象项填充到我们的表格视图中。然后我们定义了我们要用作主要数据源的表格实体,并创建了一个fetchRequest对象的实例,该实例将用于存储返回的项。接下来,我们指定我们希望结果按displayOrder升序排序,并继续执行记录集,使用performFetch方法检查是否发生任何错误,然后将结果集保存到我们的fetchedObjects属性中,并在我们的表格视图控件上调用reloadData方法以重新显示记录。接下来,我们修改了viewDidAppear方法以处理视图出现时表格视图控件的刷新,然后设置表格视图将包含的节数和行数。最后,我们提供了之前设置的UITableViewController单元格的重用标识符,然后从我们的BookDetails数组中分配每个属性,并将其写入每个单元格标签。
注意
每次您将reuse标识符作为以下方法dequeueReusableCellWithIdentifier的参数时,这会自动创建一个原型的新副本,并将对象返回给您。
参见
- 在我们的 Core Data 数据模型中插入数据的菜谱
在我们的 Core Data 数据模型中插入数据
在这个菜谱中,我们将学习如何写入我们核心数据模型中包含的数据字段。
准备工作
在我们之前的菜谱之后,我们将学习如何使用一些数据更新我们的 Core Data 数据库字段内容。
如何做...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 BooksViewController.m实现文件。
- 
修改如下代码片段所示的 btnAdd:方法。#pragma mark method called when the user presses the Add button -(IBAction)btnAdd:(id)sender{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Add Book Details" message:@"\n\n\n\n" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; bookTitle = [[UITextField alloc] initWithFrame:CGRectMake(12.0, 45.0, 260.0, 25.0)]; [bookTitle setPlaceholder:@"Book Title:"]; [bookTitle setBackgroundColor:[UIColor whiteColor]]; [alert addSubview:bookTitle]; bookAuthor = [[UITextField alloc] initWithFrame:CGRectMake(12.0, 80.0, 260.0, 25.0)]; [bookAuthor setPlaceholder:@"Author:"]; [bookAuthor setBackgroundColor:[UIColor whiteColor]]; [alert addSubview:bookAuthor]; bookPublisher = [[UITextField alloc] initWithFrame:CGRectMake(12.0, 80.0+35.0, 260.0, 25.0)]; [bookPublisher setPlaceholder:@"Publisher:"]; [bookPublisher setBackgroundColor:[UIColor whiteColor]]; [alert addSubview:bookPublisher]; // Show our Alert Dialog [alert show]; }
- 
接下来,根据代码片段中的指定创建以下代码部分。 #pragma mark method called and uses the data entered by the btnAdd method -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSString *title = [alertView buttonTitleAtIndex:buttonIndex]; if ([title isEqualToString:@"OK"]) { // Set a pointer to our Books database table schema Books *book = (Books *)[NSEntityDescription insertNewObjectForEntityForName:@"Books" inManagedObjectContext:managedObjectContext]; NSMutableArray *array = [[fetchedResultsController fetchedObjects] mutableCopy]; // Assign our text fields to each of their attributes [book setTitle:bookTitle.text]; [book setAuthor:bookAuthor.text]; [book setPublisher:bookPublisher.text]; [book setValue:[NSNumber numberWithInt:[array count] == 0 ? 0 : ([array count] + 1)] forKey:@"displayOrder"]; NSError *error; if (![managedObjectContext save:&error]) { // Record could not be saved error message UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Book Details" message:@"There was a problem saving the book details." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } [self populateBookDetails]; } }
它是如何工作的...
在这个菜谱中,我们首先动态创建每个将接受用户输入的字段,并将这些字段定位在UIAlertView对话框中,然后将其添加到初始视图控制器作为子视图。然后,在创建用于创建新管理对象的托管对象上下文之前,我们检查是否已按下确定按钮。使用Books实体描述。
我们使用每个托管对象模式字段的getters和setters方法,根据用户之前输入的内容设置托管对象的每个属性值。最后,上下文被指示调用上下文的save方法将更改保存到持久存储中。如果在 Core Data 数据模型的保存操作中检测到任何错误,这些错误将在UIAlertView对话框中显示。最后,我们刷新表格视图以显示新记录已被添加。
相关内容
- 使用 Core Data 从表格视图中删除一个项目的配方
使用 Core Data 从表格视图中删除一个项目
在这个配方中,我们将学习如何从表格视图中删除一个项目,以及我们的 Core Data 模型。
准备工作
在我们之前的配方基础上,我们将学习如何从我们的表格视图中删除一行,并永久从我们的 Core Data 数据库中删除它。
如何操作...
要开始,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 BooksViewController.m实现文件。
- 
修改如下代码片段所示的 tableView:commitEditingStyle:方法。- (void)tableView:(UITableView *)tableViewcommitEditingStyle:(UITa bleViewCellEditingStyle)editingStyleforRowAtIndexPath:(NSIndexPa th *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Turn on table view animations [self.tableView beginUpdates]; // Get the item to delete from our row Books *itemToDelete = [fetchedResultsController objectAtIndexPath:indexPath]; // Delete the item in Core Data [self.managedObjectContext deleteObject:itemToDelete]; // Commit the deletion NSError *error; if (![self.managedObjectContext save:&error]) { NSLog(@"There was a problem deleting the Book %@",[error domain]); } // Delete the (now empty) row on the table [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [self populateBookDetails]; // Turn off table animations [self.tableView endUpdates]; } // Finally, reload data in view [self.tableView reloadData]; }
- 
然后,通过从产品菜单中选择产品 | 运行或按Command + R来构建和运行应用程序。 
当编译完成时,从选定的行向左滑动以显示删除按钮。

工作原理...
在这个配方中,我们首先确定当前在表格视图中正在执行的操作类型,这由UITableViewCellEditingStyle类确定。然后,我们将它与UITableViewCellEditingStyleDelete常量变量进行比较,如果条件满足,我们将从Books数据库中删除所选行的所选书籍详情,然后刷新表格视图数据源。如果在删除过程中检测到任何错误,这些错误将被记录到调试窗口中。
在表格视图中重新排序行
在这个配方中,我们将学习如何从表格视图中删除一个项目,以及我们的核心数据模型。
准备工作
在我们之前的配方基础上,我们将学习如何从我们的表格视图中删除一行,并永久从我们的 Core Data 数据库中删除它。
如何操作...
要开始,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 BooksViewController.m实现文件。
- 
修改如下代码片段所示的 btnSortOrder:方法。#pragma mark method called when the user presses the Display Order button - (IBAction)btnSortOrder:(id)sender { if ([btnSortOrder.title isEqualToString:@"Sort Order"]){ self.tableView.editing = YES; [btnSortOrder setTitle:@"Done"]; } else if ([btnSortOrder.title isEqualToString:@"Done"]) { [btnSortOrder setTitle:@"Sort Order"]; self.tableView.editing = NO; } }
- 
接下来,创建如下代码片段中指定的以下代码部分。 #pragma mark method to allow for rows to be moved around. - (BOOL)tableView:(UITableView *)sender canMoveRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } #pragma mark Change the order of the data. - (void)tableView:(UITableView *)sender moveRowAtIndexPath:(NSIndexPath *)sourcePath toIndexPath:(NSIndexPath *)destinationPath { NSMutableArray *array = [[fetchedResultsController fetchedObjects] mutableCopy]; id objectToMove = [array objectAtIndex:sourcePath.row]; [array removeObjectAtIndex:sourcePath.row]; [array insertObject:objectToMove atIndex:destinationPath.row]; for (int i=0; i<[array count]; i++) { [(NSManagedObject *)[array objectAtIndex:i] setValue:[NSNumber numberWithInt:i] forKey:@"displayOrder"]; } NSError *error; BOOL success = [[self managedObjectContext] save:&error]; if (!success) { // Handle error NSLog(@"There was a problem updating the display order %@",[error domain]); } }
- 
然后,通过从产品菜单中选择产品 | 运行或按Command + R来构建和运行应用程序。 
当编译完成时,轻触排序顺序按钮以启用我们的表格视图支持编辑,并允许行移动。

工作原理...
在这个菜谱中,我们首先在按下排序顺序按钮时,通过将self.tableView.editing属性设置为YES来开启表格编辑。接下来,我们实现canMoveRowAtIndexPath:方法,该方法将用于确定行是否可以在表格视图中移动。在moveRowAtIndexPath:方法中,我们创建一个NSMutableArray,它将用于存储由NSFetchedResultsController类返回的数据模型结果。然后,我们遍历项目并更新每个项目的displayOrder字段,在保存带有重新排序结果的托管对象上下文之前。
更多内容...
我们使用NSMutableArray数组的原因是因为我们可以在不触发数据存储任何变化的情况下,从数组中插入和移除管理对象指针。你对对象所做的任何更改都将立即反映出来,这通常是您想要做的,但在这种情况下,我们不想立即应用这些更改。
参见
- 在表格视图中过滤和搜索数据菜谱
在表格视图中过滤和搜索数据
在这个菜谱中,我们将学习如何通过搜索表格来过滤列表项。
准备工作
在我们之前的菜谱之后,我们将学习如何使用UISearchBar控件来允许我们过滤和缩小书籍详情的结果。
如何做到...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口选择 MainStoryboard.storyboard文件。
- 
从对象库中选择并拖动一个( UISearchBar)搜索****栏和搜索显示控制器控件到我们在上一步中添加的(UITableViewController)控件导航栏的顶部。
- 
接下来,为搜索栏创建 outlet方法,并将其命名为filteredResults。
- 
从项目导航器窗口打开 BooksViewController.h接口文件。
- 
接下来,修改接口文件并添加以下突出显示的代码部分。 // BooksViewController.h // BooksLibrary // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import "Books.h" @interface BooksViewController : UITableViewController <UISearchBarDelegate>{ ... ... IBOutlet UISearchBar *filteredResults; } ... ... // Property getters and setters for our UI @property (strong, nonatomic) IBOutlet UISearchBar *filteredResults;
- 
现在我们已经创建了出口并扩展了我们的类以包括 UISearchBarDelegate对象,以便我们可以访问属性和方法。我们的下一步是开始向我们的BooksViewController类添加额外的内容,这将使我们能够过滤列表的结果。
- 
从项目导航器窗口打开 BooksViewController.m实现文件,并创建以下代码部分,如代码片段中指定。// // BooksViewController.m // BooksLibrary // // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. // #import "BooksViewController.h" @implementation BooksViewController @synthesize fetchedResultsController; @synthesize managedObjectContext; @synthesize btnAdd; @synthesize btnSortOrder; @synthesize btnConnect; @synthesize btnTransfer; @synthesize filteredResults; #pragma mark method called by the UISearchBar Delegates -(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { // Only show the Search Bar's cancel button while in edit mode filteredResults.showsCancelButton= YES; filteredResults.autocorrectionType = UITextAutocorrectionTypeNo; } #pragma mark Hides our Search Bar's cancel button when not in edit mode -(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { filteredResults.showsCancelButton = NO; } #pragma mark reload our contact details -(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [self populateBookDetails]; } #pragma mark use an NSPredicate combined with the fetchedResultsController to perform the search -(void)searchBarSearchButtonClicked:(UISearchBar *)theSearchBar { if (![destinationSearchBar.text isEqualToString:@""]) { NSPredicate *predicate =[NSPredicate predicateWithFormat:@"publisher contains[cd] %@", self.filteredResults.text]; [fetchedResultsController.fetchRequest setPredicate:predicate]; } else { // We have hit the cancel button, reload our TableView [filteredResults resignFirstResponder]; [self.tableView reloadData]; return; } NSError *error = nil; if (![[self fetchedResultsController] performFetch:&error]) { // Handle the error that was caught by the exception NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); } // Return the number of rows to populate our Table View // controller with. fetchedObjects = fetchedResultsController.fetchedObjects; // reload the TableView Controller and hide the keyboard. [filteredResults resignFirstResponder]; [self.tableView reloadData]; // Display the total number of matching records found. NSString *searchResults = [[NSString alloc] initWithFormat:@"%d matching record(s) found.",[fetchedObjects count]]; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Search Results" message:searchResults delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; }
它是如何工作的...
在这个菜谱中,我们首先扩展我们的BooksViewController类以使用UISearchBarDelegate类协议,以便我们可以访问其属性和方法,然后当用户点击时更改搜索栏的外观。接下来,我们指定在用户处于编辑模式时显示取消按钮,然后关闭自动更正功能,并在用户完成编辑后隐藏搜索栏的取消按钮。在我们的下一步中,我们调用我们的populateBookDetails方法来从数据库中获取更新后的记录,并在继续遍历数据源并选择那些包含搜索字符串的对象之前,将这些记录填充到我们的表格视图控制中。
最后,我们继续用返回的搜索数据重新加载我们的表格视图,然后在UIAlertView对话框中显示与我们的搜索字符串匹配的记录总数。如果搜索标准为空,我们使用isEqualToString方法执行比较,并检查字符串是否为空。然后我们放弃键盘并从我们的数据模型重新加载所有书籍详情。
参见
- 处理不同的键盘样式菜谱
处理不同的键盘样式
在这个菜谱中,我们将学习如何从表格视图中删除一个项目,以及我们的核心数据模型。
准备工作
在我们之前的菜谱基础上,我们将学习如何将自定义键盘样式应用到我们UIAlertView中的字段,正如我们在之前的菜谱中定义的btnAdd方法中所述。
如何做到这一点...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 BooksViewController.m实现文件。
- 
修改 btnAdd方法以应用我们的bookTitle字段的自定义键盘类型,如下面的代码片段中突出显示的代码部分所示。UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Add Book Details" message:@"\n\n\n\n" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; bookTitle = [[UITextField alloc] initWithFrame:CGRectMake(12.0, 45.0, 260.0, 25.0)]; [bookTitle setPlaceholder:@"Book Title:"]; [bookTitle setBackgroundColor:[UIColor whiteColor]]; bookTitle.keyboardType = UIKeyboardTypeAlphabet; [alertaddSubview:bookTitle];
它是如何工作的...
在这个菜谱中,我们首先更新bookTitleUITextField控制的keyboardType属性,然后指定UIKeyboardTypeAlphabet变量作为要使用的键盘类型。
更多内容...
keyboardType属性接受一个名为UIKeyboardType的枚举类型。
下表解释了这些类型中的一些:
| 键盘类型 | 描述 | 
|---|---|
| UIKeyboardTypeDefault | 当前输入方法的默认键盘。 | 
| UIKeyboardTypeASCIICapable | 显示标准 ASCII 字符。 | 
| UIKeyboardTypeNumbersAndPunctuation | 显示数字和标点符号键盘。 | 
| UIKeyboardTypeURL | 显示一个针对 URL 输入优化的键盘。 | 
| UIKeyboardTypeNumberPad | 显示一个为 PIN 输入设计的数字键盘。 | 
| UIKeyboardTypePhonePad | 显示一个为输入电话号码设计的键盘。 | 
| UIKeyboardTypeNamePhonePad | 显示一个为输入人名或电话号码设计的键盘。 | 
| UIKeyboardTypeEmailAddress | 显示一个针对指定电子邮件地址优化的键盘。 | 
| UIKeyboardTypeDecimalPad | 显示一个带有数字和小数点的键盘。 | 
| UIKeyboardTypeTwitter | 显示一个针对 twitter 文本输入优化的键盘,易于访问@和#字符。 | 
| UIKeyboardTypeAlphabet | 已弃用,但使用显示标准 ASCII 字符的键盘。 | 
注意
有关UIKeyboardType类以及各种键盘类型的更多信息,请参阅位于developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITextInputTraits_Protocol/Reference/UITextInputTraits.html的 Apple 开发者文档。
参见
- 在 Core Data 数据模型中插入数据的食谱
使用蓝牙将数据传输到另一台设备
在这个食谱中,我们将学习如何使用允许通过蓝牙网络进行通信的GameKit框架 API。
准备工作
在我们之前的食谱基础上,我们将探讨如何将这些功能实现到我们的应用程序中,以处理将书籍详情从一个 iOS 设备传输到另一个设备。
如何操作...
要开始,请按照以下顺序遵循以下简单步骤:
- 
从项目导航器窗口打开 BooksViewController.h接口文件。
- 
接下来,修改接口文件并添加以下高亮显示的代码部分。 // BooksViewController.h // BooksLibrary // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <GameKit/GameKit.h> #import "Books.h" @interface BooksViewController : UITableViewController <UISearchBarDelegate, GKSessionDelegate, GKPeerPickerControllerDelegate>{ ... ... GKSession *currentSession; GKPeerPickerController *peerPicker; NSString *itemSelected; } ... ... // Bluetooth session objects @property (strong, nonatomic) GKSession *currentSession;现在我们已经创建了我们的属性,并将我们的类扩展到包括 GKSessionDelegate和GKPeerPickerControllerDelegate对象,以便我们可以访问它们的属性和方法。我们的下一步是开始向BooksViewController类添加额外的内容,这将使我们能够通过蓝牙发送详情。
- 
从项目导航器窗口打开 BooksViewController.m实现文件,并输入以下高亮显示的代码部分。// // BooksViewController.m // BooksLibrary // // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. // #import "BooksViewController.h" @implementation BooksViewController @synthesize fetchedResultsController; @synthesize managedObjectContext; @synthesize btnAdd; @synthesize btnSortOrder; @synthesize btnConnect; @synthesize btnTransfer; @synthesize currentSession; @synthesize filteredResults;
- 
接下来,修改 btnConnect方法,如下面的代码片段所示。#pragma mark called when the user presses the Connect button - (IBAction)btnConnect:(id)sender { if ([btnConnect.title isEqualToString:@"Connect"]) { [btnConnect setTitle:@"Disconnect"]; peerPicker = [[GKPeerPickerController alloc]init]; peerPicker.delegate = self; peerPicker.connectionTypesMask = GKPeerPickerConnectionTypeNearby; [peerPicker show]; btnTransfer.enabled = YES; } else if ([btnConnect.title isEqualToString:@"Disconnect"]) { [btnConnectsetTitle:@"Connect"]; [self.currentSession disconnectFromAllPeers]; currentSession = nil; btnTransfer.enabled = NO; } }
- 
接下来,修改 btnTransfer方法,如下面的代码片段所示。#pragma mark called when the user presses the Transfer button - (IBAction)btnTransfer:(id)sender { // Converts an NSString object to NSData NSData *data; data = [itemSelected dataUsingEncoding:NSASCIIStringEncoding]; [self.currentSession sendDataToAllPeers:data withDataMode:(GKSendDataReliable) error:nil]; }
- 
接下来,根据代码片段中的说明创建以下代码部分。 #pragma mark Handle Bluetooth capabilities using the GameKit framework. -(void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerIDtoSession:(GKSession *)session{ self.currentSession = session; session.delegate = self; [sessionsetDataReceiveHandler:self withContext:nil]; picker.delegate = nil; [picker dismiss]; } #pragma mark Called when a connection has been made to Bluetooth, #pragma mark details are added to the database on the receiving iOS device. -(void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context { // Convert our NSData type to NSString NSString *strData; strData = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; // Split out our data and place the contents into an array. NSArray *stringComponents = [strData componentsSeparatedByString:@"~"]; NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:1000]; [myArray addObjectsFromArray:stringComponents]; // Insert the passed record details into our database. Books *bookDetails = (Books *)[NSEntityDescription insertNewObjectForEntityForName:@"Books" inManagedObjectContext:managedObjectContext]; [bookDetails setTitle:[myArray objectAtIndex:0]]; [bookDetails setPublisher:[myArray objectAtIndex:1]]; [bookDetails setAuthor:[myArray objectAtIndex:2]]; [bookDetails setDisplayOrder:[myArray objectAtIndex:3]]; NSError *error; if (![managedObjectContext save:&error]) { // Display error message stating that record not saved. UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Book Library Details" message:@"There was a problem saving the book details." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } // Reload our book details from our database [self populateBookDetails]; } #pragma mark called when user cancels the Bluetooth connection -(void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker { [btnTransfer setTitle:@"Transfer Book"]; peerPicker = nil; peerPicker.delegate = nil; [peerPicker dismiss]; } #pragma mark called when a change in state has been detected. -(void)session:(GKSession *)session peer:(NSString *)peerIDdidChangeState:(GKPeerConnectionState)state { NSString *GKPeerStateInfo; switch (state) { case GKPeerStateAvailable: GKPeerStateInfo = @"Wi-Fi is Available"; break; case GKPeerStateUnavailable: GKPeerStateInfo = @"Wi-Fi is not Available"; break; case GKPeerStateConnecting: GKPeerStateInfo = @"Establishing Connection"; break; case GKPeerStateConnected: GKPeerStateInfo = @"Connection Successful"; break; case GKPeerStateDisconnected: GKPeerStateInfo = @"Disconnected from Session"; currentSession = nil; break; } // Display the current connection state. NSLog(@"Connection State: %@", GKPeerStateInfo); } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableViewdidSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Get the currently selected item from our resultset Books *bookDetails = [fetchedResultsController objectAtIndexPath:indexPath]; // Get the currently selected item within our tableView itemSelected =[NSString stringWithFormat:@"%@~%@~%@~%@", bookDetails.title, bookDetails.publisher, bookDetails.author, bookDetails.displayOrder]; }
它是如何工作的...
在这个食谱中,我们首先扩展我们的BooksViewController类以使用GKSessionDelegate和GKPeerPickerControllerDelegate类协议,这样我们就可以访问其属性和方法,这些属性和方法负责处理蓝牙功能,以及一个NSString对象变量itemSelected,它将用于存储从我们的表格视图中选择的书本项。
接下来,我们声明一个 GKSessionDelegate 对象,用于表示两个连接的蓝牙设备之间的活动会话,并允许在两个设备之间发送和接收数据。GKPeerPickerControllerDelegate 提供一个标准用户界面,让您的应用程序发现并连接到另一个蓝牙设备,这是在设备之间连接的最简单方法。
btnConnect: 方法通过使用 isEqualToString 方法检查我们按钮的当前值,如果值显示为 Connect,我们将调用 GKPeerPickerController 类,该类显示一个标准用户界面,您可以通过它连接到另一个蓝牙设备。或者,如果您点击 Disconnect,我们将从 GKSession 对象调用 disconnectFromAllPeers 方法来关闭两个设备之间的连接。connectionTypesMask 属性指示用户可以选择的连接类型。
注意
有两种连接类型:GKPeerPickerConnectionTypeNearby 和 GKPeerPickerConnectionTypeOnline。当使用蓝牙通信时,使用 GKPeerPickerConnectionTypeNearby 常量。或者,如果您想使用基于互联网的连接,请使用 GKPeerPickerConnectionTypeOnline 常量。
当检测到两个设备之间的蓝牙连接,并且用户已从可用设备列表中选择了一个要连接的项目时,将调用 peerPickerController:didConnectPeer:toSession: 方法,并启用我们的 Transfer 按钮。一旦用户连接到对等蓝牙设备,您将 GKSession 对象保存到 currentSession 属性中,这样您就可以使用 GKSession 对象与远程设备进行通信。
当用户按下 Transfer 按钮时,它调用 btnTransfer: 方法,该方法调用 GKSession 对象的 sendDataToAllPeers: 方法,通过 NSData 对象将数据发送到另一台设备。我们使用一个名为 itemSelected 的变量,其中包含要发送的联系人地址信息。
注意
当使用 GKSendDataReliable 常量时,GKSession 对象将继续发送数据,直到成功传输数据或连接超时。或者,使用 GKSendDataUnreliable 表示 GKSession 对象应仅发送一次数据,不进行重试。
当两个设备之间建立连接后,GKSession 对象的 receiveData:fromPeer:inSession:context: 方法会被调用一次,以处理接收作为 NSData 分隔字符串发送的数据。在可以使用 componentsSeparatedByString 字符串类将每个字段单独分割出来并将这些放入我们的 NSMutableArray 对象变量 myArray 之前,需要将这些数据转换为 NSString 对象。
接下来,我们创建一个新的managedObjectContext实例,该实例指向我们的Books实体,并使用我们的NSManagedObject的getter和setter方法将myArray对象中的每个数组元素分配给实体的每个字段属性,然后在将详细信息写入数据库之前。在将数据模型 Core Data 的保存操作中检测到的任何错误将在UIAlertView对话框中显示。当用户从蓝牙选择器取消时,将调用peerPickerControllerDidCancel:方法。一旦发生这种情况,我们更改连接按钮的标题并禁用传输按钮,然后在关闭peerPicker对话框之前释放分配的内存。
当设备连接或从会话断开时,didChangeState:方法会被调用,并且足够智能,能够知道连接是否已经建立或结束,通过检查GKPeerConnectionState类的state属性和常量来确定连接类型。在确定state类型后,我们使用NSLog语句将连接state写入控制台窗口。
在我们的最后一步,我们使用didSelectRowAtIndexPath:方法,为所选行构造书籍详细信息。我们使用indexPath的行属性在我们的Books数组中查找,并提取记录信息。然后我们将记录作为分隔字符串构造,并将其分配给我们的itemSelected变量,该变量将被传输按钮使用。
注意
关于NSMutableArray对象的更多信息,您可以参考以下 URL 的 Apple 开发者文档:developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
参见
- 实现电子邮件消息配方
实现电子邮件消息
在本配方中,我们将了解MFMailComposeViewController类,以及我们如何使用它将电子邮件消息集成到我们的应用程序中。
准备工作
在我们之前的配方基础上,我们将探讨如何实现sendEmail方法,以便在发生传输时用所选书籍详情编写电子邮件。
如何做...
首先,按照以下顺序遵循以下简单步骤:
- 
从项目 导航器窗口打开 BooksViewController.h接口文件。
- 
接下来,修改接口文件并添加以下高亮代码部分。 // BooksViewController.h // BooksLibrary // Created by Steven F Daniel on 03/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <GameKit/GameKit.h> #import <MessageUI/MessageUI.h> #import "Books.h" @interface BooksViewController : UITableViewController<UISearchBarDelegate, GKSessionDelegate, GKPeerPickerControllerDelegate, MFMailComposeViewControllerDelegate> // Create the class instance methods -(void)sendEmail:(NSArray *)bookDetails;
- 
从项目导航器窗口打开 BooksViewController.m实现文件。
- 
接下来,修改 sendEmail:方法,如下所示代码片段。#pragma mark sends an email for the chosen book details - (void)sendEmail:(NSArray *)bookDetails { MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; mailComposer.mailComposeDelegate = self; // Check to make sure that we are set up to send mail if ([MFMailComposeViewController canSendMail]) { [mailComposer setToRecipients:[NSArray arrayWithObjects:@"youremail@yourdomain.com",nil]]; [mailComposer setSubject:@"Book Details"]; // Book Details for the body of our email message NSString *bookItem = [NSString stringWithFormat:@"Title:%@\nPublisher: %@\nAuthor:%@\nSort Order:%@", [bookDetails objectAtIndex:0], [bookDetails objectAtIndex:1], [bookDetails objectAtIndex:2], [bookDetails objectAtIndex:3]]; [mailComposer setMessageBody:bookItem isHTML:NO]; [mailComposer.navigationBar setTintColor:[UIColor blueColor]]; mailComposer.modalPresentationStyle = UIModalPresentationFormSheet; [self presentViewController:mailComposer animated:YES completion:nil]; } else { // Error sending the email message, so notify the user. UIAlertView *alertMessage = [[UIAlertView alloc] initWithTitle:@"Failure" message:@"Your device hasn't been set up for email" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil]; [alertMessage show]; } } #pragma mark Dismiss our Mail Controller when the user finishes - (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { NSString *emailMsg = nil; // Notifies users about errors associated with the interface switch (result) { case MFMailComposeResultCancelled: emailMsg = @"Email cancelled"; break; case MFMailComposeResultSent: emailMsg = @"Email sent Successfully"; break; case MFMailComposeResultFailed: emailMsg = @"Email sending failure"; break; default: emailMsg = @"Problem sending Email"; break; } // Display the alert dialog based on the message UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Book Details Email" message: emailMsg delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; // make the MFMailComposeViewController disappear [self dismissViewControllerAnimated:YES completion:nil]; }
- 
接下来,我们需要修改 receiveData:方法以启用电子邮件发送,如下所示代码高亮部分:-(void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context { // Convert our NSData type to NSString ... ... NSError *error; if (![managedObjectContext save:&error]) { ... } else { // Send an email for the received book details [self sendEmail:myArray]; } ... }
它是如何工作的...
在这个配方中,我们创建了一个新的MFMailComposeViewController类代理对象实例,该对象控制邮件对话框视图,从而允许用户在不离开应用程序的情况下编写和发送电子邮件。我们继续使用控制器的navigationBar:setTintColor:方法将邮件编写表单的颜色更改为红色,并设置我们的电子邮件消息的主题标题和正文。在下一步中,我们将控制器的mailComposeDelegate设置为自身,这确保了当用户完成电子邮件对话框时,我们的控制器会从MFMailComposeViewControllerDelegate协议接收到mailComposeController:didFinishWithResult:error:消息。最后,我们调用控制器的dismissViewControllerAnimated:方法来显示电子邮件对话框。
更多内容…
将应用程序部署到两台不同的 iOS 设备上,然后通过从产品菜单选择产品 | 运行或按Command + R来构建和运行应用程序。
当编译完成后,使用蓝牙连接每个设备,从列表中选择一本书,然后点击连接按钮。几秒钟后,会弹出一个窗口,其中包含用户可以选择的附近蓝牙设备列表。一旦用户选择了设备,需要从该设备获取确认,以便 iOS 模拟器能够与其建立连接。一旦建立连接,点击传输按钮将书籍详情传输到另一台设备。以下截图显示了在 iOS 模拟器中运行的应用程序以及另一台具有互联网连接和蓝牙连接的 iOS 设备,以及显示在列表中选定的项目的生成电子邮件。

参见
- 使用蓝牙将数据传输到另一台设备的配方
第九章:使用 Facebook iOS SDK 创建社交网络应用
在本章中,我们将涵盖:
- 
下载 Facebook iOS SDK 
- 
在 Facebook 上注册您的 iOS 应用 
- 
构建一个简单的社交网络应用 
- 
将 Facebook iOS SDK 添加到您的项目中 
- 
实现单点登录(SSO)功能 
- 
请求额外的 Facebook 权限 
- 
实现和使用 Graph API 读取 JSON 数据 
- 
与 Facebook 社交渠道集成 
- 
在您的应用中处理 Facebook 错误 
简介
在 2007 年 5 月 24 日,马克·扎克伯格宣布了 Facebook 平台,这是一个为程序员创建 Facebook 内社交应用的开发平台。
当 Facebook 推出开发平台时,已经建立了许多应用,并且已经有数百万用户在玩这些应用。社交网络应用利用 Facebook 的 API 集合,使您能够连接到 Facebook 并发送应用请求通知,以便您可以将他们添加到您的朋友列表中。
在本章中,我们将探讨如何下载 Facebook iOS SDK 并注册您的 iOS 应用,以便它可以与 Facebook 一起使用。然后,我们将从创建一个简单应用开始,看看我们如何将 Facebook iOS SDK 添加到我们的项目中,以便用户可以登录他们的 Facebook 账户,以便发送通知请求以及直接将新闻源提交到他们的主页墙。
最后,我们将探讨如何实现 Facebook iOS SDK 的单点登录(SSO)功能,该功能允许用户使用他们的 Facebook 身份登录您的应用。
下载 Facebook iOS SDK
在本食谱中,我们将学习下载 Facebook iOS SDK 所需的步骤。
准备工作
为了继续操作,我们首先需要启动我们的浏览器,并从 Facebook 开发者网站下载 Facebook iOS SDK 包。
如何操作...
首先,按照以下步骤操作:
- 
启动您的浏览器并输入 developers.facebook.com/ios/。![如何操作...]() 
- 
接下来,点击下载 SDK按钮开始下载 Facebook SDK。下载完成后,安装该包。默认安装位置是 ~/Documents/FacebookSDK。
它是如何工作的...
在本食谱中,我们探讨了从 Facebook 开发者门户下载 Facebook iOS SDK 所需的必要步骤。与 Facebook SDK 的早期版本相比,现在的 SDK 被包装成一个框架,并提供您所需的所有功能,以便您的应用与 Facebook 平台交互。当使用 Facebook SDK 的 3.1 版及以上版本开发应用时,它需要 Xcode 4.5 及以上版本。
更多内容...
Facebook SDK 框架包含所有必需的方法对象和 API,使您能够与 Facebook 交互,发送通知请求,或简单地使用 Facebook iOS SDK 的单点登录功能向当前人的墙页发布消息。
这允许您的用户使用他们的 Facebook 身份登录您的应用程序。在 SDK 的初始版本中,authorize 方法总是打开一个包含 UIWebView 的内联对话框,其中显示了授权用户界面,并要求用户为每个他们授权的应用程序分别输入他们的凭据。
在 SDK 的更新版本中,这一变化已经发生,不再需要用户为设备上他们想要授权的每个应用程序重新输入凭据。使用 Facebook iOS SDK 允许您执行以下操作:
| Facebook iOS SDK 类型 | 描述 | 
|---|---|
| 身份验证和授权 | 提示用户登录 Facebook 并授予您的应用程序权限。 | 
| 调用 API | 这允许您通过 JSON API 调用获取用户配置文件数据,以及与用户的朋友相关的任何信息。 | 
| 显示对话框 | 这允许您通过 UIWebView视图与用户交互。这对于在不要求预先权限的情况下启用与 Facebook 的交互非常有用。 | 
相关内容
- 使用 Facebook 注册您的 iOS 应用程序 的配方
使用 Facebook 注册您的 iOS 应用程序
在本配方中,我们将学习如何注册 iOS 应用程序,以便它可以与 Facebook 平台一起使用。
准备工作
在我们之前的配方之后,在我们开始将我们的应用程序与 Facebook 平台集成之前,我们需要将应用程序注册到 Facebook 的移动网站上,并提供一些基本的应用程序信息。
如何做...
要开始,请按照给定顺序概述的简单步骤进行操作:
- 
打开您的浏览器并输入 developers.facebook.com/apps。![如何做...]() 
- 
接下来,如果您不是注册用户,请注册 Facebook,或者输入您的 Facebook 账户凭据并点击 登录 按钮。 
- 
接下来,从 应用 页面点击 +创建新应用 按钮。 ![如何做...]() 
- 
接下来,在 应用名称 字段中输入 Social Networking App。
- 
点击 继续 按钮以进入向导的下一步。这将用于并显示在您向朋友发布或发送通知消息时。 ![如何做...]() 
- 
接下来,在您继续下一步之前,系统将提示您输入 安全验证 词语。 ![如何做...]() 
- 
输入屏幕上显示的词语并点击 提交 按钮以继续。显示的词语每次都会不同。 注意如果您输入的单词不正确,您的账户可能会被锁定。如果是这种情况,您需要直接联系 Facebook 以解锁此账户。 ![如何做...]() 
上述屏幕截图显示了允许您在提交更改之前进行任何最终更改的最终屏幕。一旦您对所有的更改都感到满意,请点击保存更改按钮。
注意
App ID是我们将在 iOS 应用程序中使用的重要字段,用于与 Facebook 通信,并在之前的屏幕截图中被箭头突出显示。
它是如何工作的...
在本菜谱中,我们探讨了将我们的应用程序注册到 Facebook 移动开发平台所需的步骤,以便我们的应用程序可以与该平台通信。
我们探讨了如何创建和定义一个新的应用名称,该名称将在发送或发布到您的墙或朋友的通知消息时显示。在下一个菜谱中,我们将看看如何构建我们的社交网络应用的用户界面。
参见
- 构建简单的社交网络应用菜谱
构建简单的社交网络应用
在这个菜谱中,我们将学习如何构建我们的社交网络应用项目的用户界面。
准备工作
在本节中,我们将开始构建将构成我们应用程序用户界面的组件。
如何做...
首先,按照给定的顺序遵循简单的步骤:
- 
从 /Xcode4/Applications文件夹启动Xcode。
- 
选择创建新的 Xcode 项目,或文件 | 新建项目。 
- 
从可用模板列表中选择单视图应用程序。 
- 
点击下一步按钮,进入向导的下一步。 
- 
接下来,输入 SocialNetworkApp作为您项目的名称。
- 
从设备下拉列表中选择iPhone。 
- 
确保没有选中使用故事板的复选框。 
- 
确保没有选中使用自动引用计数和包含单元测试的复选框。 
- 
接下来,点击下一步按钮,进入向导的下一步,并指定您想要保存项目的位置。 
- 
然后,点击创建按钮继续,并显示 Xcode 工作区。 
现在我们已经创建了SocialNetworkApp项目,我们可以开始构建我们的用户界面,该界面将使我们能够与 Facebook 通信:
- 
从项目导航器窗口中选择 ViewController.xib文件。
- 
从对象库中选择并拖动一个UIToolbar对象,并将其添加到我们的视图中,然后选择工具栏内的项目按钮。 
- 
在属性检查器部分,将标识符属性更改为自定义,并将样式的值更改为带边框,将标题的值更改为登录。 
- 
为此 UIBarButtonItem创建一个出口,并将其命名为loginButton。
- 
为登录按钮创建一个 action方法,并将其命名为loginButton。
- 
在登录按钮旁边添加一个灵活空间按钮项。 
- 
在灵活空间按钮项的右侧添加另一个 UIBarButtonItem,并在属性检查器中,将标识符属性更改为Action。
- 
从对象库中拖动一个UIImageView对象到视图中。 
- 
调整 UIImageView控件的大小,使其能够容纳护照大小的照片。
- 
接下来,为这个 UIImageView创建一个出口,并将其命名为imgPhoto。
- 
从对象库中拖动一个UITextView对象到视图中。 
- 
调整UITextView控件的大小,使其占据视图控制器宽度。 
- 
接下来,为这个 UITextView创建一个出口,并将其命名为userInfoDetails。
- 
通过选择菜单栏中的文件 | 保存来保存SocialNetworkApp。 ![如何操作…]() 
如果您正确地遵循了步骤,完成的视图控制器屏幕应该与之前的截图类似。请随意调整您的屏幕。
工作原理...
在本菜谱中,我们首先构建了我们SocialNetworkApp应用程序的用户界面,这将使我们能够登录和注销 Facebook 并在我们的墙上发布消息。在我们的下一个菜谱中,我们将探讨如何将 Facebook iOS SDK 添加到我们的项目中,这将允许我们与 Facebook 移动开发平台进行通信,并包括必要的属性和方法,使我们能够向当前用户的墙发布消息。
相关内容
- 
将 Facebook SDK 和依赖项添加到您的项目中菜谱 
- 
在第一章的使用 Xcode 创建 iOS 项目菜谱中,获取和安装 iOS SDK 开发工具 
- 
在第一章的为 Interface Builder 对象创建出口菜谱中,获取和安装 iOS SDK 开发工具 
将 Facebook SDK 和依赖项添加到您的项目中
在本菜谱中,我们将学习如何将 Facebook SDK 和框架依赖项作为我们SocialNetworkingApp项目的一部分来包含,以便我们能够与 Facebook 移动平台进行通信。
准备工作
在我们之前的菜谱基础上,确保我们的SocialNetworkApp项目文件已打开。
如何操作...
要添加 Facebook SDK 和所需的框架,请按照以下简单步骤操作:
- 
从项目导航器窗口中,选择 SocialNetworkApp文件夹。
- 
选择将文件添加到 "SocialNetworkApp" …或 Mac 用户可以按Option + Command + A。 
- 
接下来,将 FacebookSDK.framework文件夹从Facebook SDK 安装文件夹拖动到您的项目导航器的框架部分。
- 
然后,选择为任何添加的文件夹创建组,并取消选择将项目复制到目标组文件夹中(如果需要)以保持对 SDK 安装文件夹的引用,而不是创建一个副本。 
- 
接下来,通过将 FacebookSDKResources.bundle文件从FacebookSDK.framework/Resources文件夹拖动到您的项目导航器的框架部分,添加 Facebook SDK for iOS 资源包。
- 
接下来,SDK 依赖于其他三个框架(AdSupport、Accounts 和 Social)来使用 iOS6 中内置的 Facebook 功能。要添加这些框架,请转到目标的摘要面板中的链接框架和库部分,然后点击+按钮来添加它们。 
- 
接下来,我们需要通过将 DeprecatedHeaders文件夹从FacebookSDK.framework/Versions/A/DeprecatedHeaders文件夹拖动到您的项目导航器的框架部分来导入 Facebook 对话框调用头文件:![如何操作...]() 
- 
然后,我们需要在构建设置面板中将–lsqlite3.0 SQL 库添加到构建依赖项列表中: ![如何操作...]() 
- 
一旦做出更改,您的解决方案应包含以下文件: ![如何操作...]() 
- 
通过选择文件 | 保存来保存您的项目,因为我们已经完成了添加 Facebook SDK。 
工作原理...
在这个菜谱中,我们学习了如何使用 Xcode 手动将 Facebook SDK 组件添加到我们的项目中,以便我们能够与 Facebook 平台通信,向当前用户的墙发布消息,或者使用 SSO 功能和 Facebook Graph API 方法检索与用户关联的信息。
当在 iOS6 平台上构建项目时,我们学习了关于依赖于 Facebook SDK 的新框架依赖项,以及将 SQLite SQL 库添加到项目构建设置中的构建依赖项列表。
最后,我们学习了如何导入 Facebook Feed 和 Requests 对话框方法,以确保 Facebook SDK 头文件与 Facebook SDK 2.0 版本中使用的相同方法保持向后兼容性。
在我们的下一个菜谱中,我们将学习如何在我们的应用程序中实现单点登录(SSO)功能,以便我们能够登录和注销 Facebook,以及向当前用户的 Facebook 墙发布消息。
注意
有关 Facebook iOS SDK 的更多信息,请参阅 Facebook 开发者文档,可在developers.facebook.com/docs/guides/mobile找到。
相关内容
- 实现单点登录(SSO)功能菜谱
实现单点登录(SSO)功能
在这个菜谱中,我们将学习如何实现 Facebook iOS SDK 的单点登录功能,这将使我们的应用程序能够与 Facebook 通信。
使用 SSO 的过程是通过将用户重定向到他们设备上的 Facebook iOS 应用程序,并向他们展示一个认证对话框,只显示您的应用程序已配置为使用的那些权限。
准备工作
在我们之前的菜谱基础上,确保我们的SocialNetworkApp项目文件是打开的。
如何做...
要开始,按照给定的顺序遵循以下简单步骤:
- 
从项目导航器中打开 AppDelegate.hinterface文件。
- 
接下来,根据高亮显示的代码部分修改接口文件: // AppDelegate.h // SocialNetworkApp // Created by Steven F. Daniel on 11/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import "FBConnect.h" @class ViewController; @interface AppDelegate : NSObject <UIApplicationDelegate, FBSessionDelegate, FBDialogDelegate> { Facebook *facebook; } // Create the required class Setters and Getters @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) ViewController *viewController; @property (nonatomic, retain) Facebook *facebook; -(void)fbDidLogin; -(void)fbDidLogout; -(void)fbSessionInvalidated; @end
- 
接下来,从项目导航器中打开 AppDelegate.m实现文件。
- 
修改实现文件,如高亮显示的代码部分所示: // AppDelegate.m // SocialNetworkApp // Created by Steven F. Daniel on 11/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import "AppDelegate.h" #import "ViewController.h" @implementation AppDelegate @synthesize window = _window; @synthesize viewController = _viewController; @synthesize facebook;
- 
接下来,修改 didFinishLaunchingWithOptions:方法,如以下代码片段所示:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application // launch. self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; self.window.rootViewController = self.viewController; // Do any additional setup after loading the view, // typically from a nib. self.facebook = [[Facebook alloc] initWithAppId:@"YOUR_APPID_HERE" andDelegate:self]; // Check and retrieve authorization information NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if ([defaults objectForKey:@"FBAccessTokenKey"] && [defaults objectForKey:@"FBExpirationDateKey"]) { self.facebook.accessToken = [defaults objectForKey:@"FBAccessTokenKey"]; self.facebook.expirationDate = [defaults objectForKey:@"FBExpirationDateKey"]; } // Check to ensure that we have a valid session object if (![self.facebook isSessionValid]) { [self.facebook authorize:nil]; } [self.window makeKeyAndVisible]; return YES; }注意为了确保您的应用程序与您自己的 AppID 一起工作,您需要将 initWithAppId:后面的YOUR_APPID_HERE字符串替换为您自己创建的 Facebook AppID。
- 
接下来,创建以下代码部分,如代码片段中指定: -(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { return [self.facebook handleOpenURL:url]; } -(BOOL)application:(UIApplication *)application openURL:(NSURL *)urlsourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ return [self.facebook handleOpenURL:url]; } -(void)fbDidLogin { // Check and retrieve authorization information NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:[self.facebook accessToken] forKey:@"FBAccessTokenKey"]; [defaults setObject:[self.facebook expirationDate] forKey:@"FBExpirationDateKey"]; [defaults synchronize]; } - (void)fbDidLogout { // Remove saved authorization information if it exists NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if ([defaults objectForKey:@"FBAccessTokenKey"]) { [defaults removeObjectForKey:@"FBAccessTokenKey"]; [defaults removeObjectForKey:@"FBExpirationDateKey"]; [defaults synchronize]; } UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"FaceBookSampleApp" message:@"Your session has logged out." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [alertView show]; [alertView release]; } [alertView show]; [alertView release]; } #pragma mark Called when the session has expired. - (void)fbSessionInvalidated { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"FaceBookSampleApp" message:@"Your session has expired." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [alertView show]; [alertView release]; [self fbDidLogout]; }
它是如何工作的...
在这个菜谱中,我们首先通过扩展我们的类来包含FBSessionDelegate和FBDIalogDelegate类协议及其方法。然后我们声明了一个名为facebook的实例变量,这将使我们能够访问Facebook类的方法。接下来,我们添加了一个Facebook类的属性实例来创建类获取器和设置器,然后继续在AppDelegate.h接口文件中合成我们定义的facebook变量。这样做是为了让我们的实现文件知道facebook变量,以便我们可以访问对象属性和方法。在我们的didFinishLaunchingWithOptions:方法中,我们初始化我们的Facebook对象以调用 SSO,通过传递我们在注册 iOS 移动应用时创建的应用程序 AppID 以及我们应用内的 Graph API 和平台对话框。一旦对象被实例化,我们需要检查任何之前保存的访问令牌信息,然后使用这些保存的信息来设置一个有效的会话,通过将保存的信息分配给 Facebook 访问令牌和过期日期属性,以确保您的应用不会重定向到 Facebook 应用程序。如果应用程序已经有一个有效的access_token,则调用授权对话框。
我们继续检查有效的会话,如果会话无效,我们调用authorize方法,这将登录用户并提示用户授权应用程序,然后声明两个 iOS 在 SSO 过程中将应用程序重定向到应用时将被调用的方法。这些方法为应用程序提供了用户的凭据。
你会注意到我们声明了两种不同的方法来处理 iOS 应用的不同版本。The handleOpenURL: 方法用于版本低于 4.2 的版本,而 openURL 方法用于 4.2 版本及以上。在我们的下一步中,我们实现了 FBSessionDelegate 的 fbDidLogin: 方法。在 SSO 过程成功登录并 Facebook 应用将用户重定向回调用应用后,我们使用 FBAccessTokenKey 和 FBExpirationDateKey 键保存用户的凭据,然后将这些信息保存到用户偏好设置 NSUserDefaults 中。
然后,我们继续实现 FBSessionDelegate 的 fbDidLogout: 方法。在 SSO 过程成功从 iOS 应用中注销后,callback 方法被调用。在移除存储的用户凭据之前,我们需要检查是否有成功的访问令牌键,使用 FBAccessTokenKey 和 FBExpirationDateKey 键。
然后,我们使用 NSUserDefaults 对象从用户偏好设置中删除这些详细信息。最后,我们创建一个 UIAlertView 对话框的实例来通知用户已成功注销。在我们之前的章节中,我们实现了 FBSessionDelegate 的 fbSessionInvalidated 方法。当请求向当前用户的墙发布新消息或向您的朋友发送通知时,会调用 fbSessionInvalidated 方法以确保存在有效的会话。这使用了 SSO 过程在您的应用登录时创建的 session 对象。如果会话状态已过期,我们声明一个 UIAlertView 类的实例来向用户显示一条消息,然后最终调用 fbDidLogout 方法以确保所有必需的访问令牌都被干净地移除。
还有更多...
如果用户已经在他们的设备上登录了 Facebook iOS 应用,则无需再次提供。使用 SSO 的过程是通过将用户重定向到他们设备上的 Facebook iOS 应用,并向他们展示一个身份验证对话框来实现的,该对话框只显示您的应用已配置为使用的权限。一旦用户允许了 iOS 应用请求的权限,他们将被重定向回您的应用,并带有适当的访问令牌。
当使用 Facebook SSO 过程时,某些行为可能会根据用户 iOS 设备上安装的 Facebook iOS 应用的版本有所不同。
以下表格解释了在特定条件下运行 Facebook SSO 过程时会发生什么:
| Facebook SSO 条件 | 描述 | 
|---|---|
| iOS 应用运行支持多任务的 iOS 版本,并且运行 3.2.3 或更高版本的 Facebook iOS 应用。 | Facebook SDK 将尝试在 Facebook 应用内打开授权对话框。在用户授权或拒绝授权后,用户将被重定向回调用应用,并带有一个授权令牌、过期时间以及 Facebook oAuth认证服务器可能返回的任何其他参数。 | 
| iOS 设备运行支持多任务的 iOS 版本,并且不是运行 3.2.3 或更高版本的 Facebook iOS 应用。 | Facebook SDK 将在 Safari 中打开授权对话框。在用户授权或拒绝授权后,Safari 将用户重定向回调用应用。此过程类似于 Facebook 应用的授权,允许多个应用通过 Safari cookie 共享相同的 Facebook 用户 access_token。 | 
| 如果 iOS 应用运行不支持多任务的 iOS 版本。 | SDK 将使用弹出内联 UIWebView网页视图控件的老机制,提示用户登录并授权。 | 
注意
Facebook 建议您始终确保您的设备运行的是最新版本的 Facebook iOS 应用。
参见
- 实现视图控制器类菜谱
实现视图控制器类
在这个菜谱中,我们将开始为我们的ViewController类实现额外的功能,以及导入一些非常重要的头文件并扩展我们的类。
准备工作
在我们之前的菜谱基础上,确保我们的SocialNetworkApp项目文件已打开。
如何做...
要开始,请按照给定顺序遵循以下简单步骤:
- 
从项目导航器打开 ViewController.h接口文件。
- 
接下来,根据高亮代码部分修改接口文件: // ViewController.h // SocialNetworkApp // Created by Steven F. Daniel on 11/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import "AppDelegate.h" @interface ViewController : UIViewController<UIActionSheetDelegate, FBRequestDelegate> { AppDelegate *mainDelegate; IBOutlet UIBarButtonItem *loginButton; IBOutlet UIBarButtonItem *postMessage; IBOutlet UIImageView *imgPhoto; IBOutlet UITextView *userInfoDetails; Facebook *facebook; } // Create the required class Setters and Getters @property (nonatomic, strong) UIBarButtonItem *loginButton; @property (nonatomic, strong) UIBarButtonItem *postMessage; @property (nonatomic, strong) UIImageView *imgPhoto; @property (nonatomic, strong) UITextView *userInfoDetails; @property(nonatomic, retain) Facebook *facebook; @property(nonatomic, retain) AppDelegate *mainDelegate; // Declare our Instance methods -(void)SendNotificationRequest; -(void)PostMessagetoWall; -(void)getGraphAPIData; -(void)dialogDidComplete:(FBDialog *)dialog; -(void)request:(FBRequest *)request didLoad:(id)result; -(void)request:(FBRequest *)request didFailWithError:(NSError *)error; @end
- 
接下来,从项目导航器打开 ViewController.m实现文件,并根据高亮代码部分修改实现文件:// ViewController.m // SocialNetworkApp // Created by Steven F. Daniel on 11/12/12. // Copyright (c) 2012 GenieSoft Studios. All rights // reserved. #import "ViewController.h" #import "FBConnect.h" @interface ViewController () @end @implementation ViewController @synthesize mainDelegate, loginButton, postMessage; @synthesize imgPhoto,userInfoDetails, facebook; - (void)viewDidLoad{ [super viewDidLoad]; // Set up our delegate object self.mainDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate]; self.facebook = mainDelegate.facebook; // Initialize our form fields userInfoDetails.text = @""; } #pragma mark Handle when the login button is pressed. - (IBAction)loginButton:(id)sender{ if ([loginButton.title isEqualToString:@"Sign In"]) { if (![self.facebook isSessionValid]) { NSLog(@"facebook session"); NSArray *permissions = [[NSArray alloc] initWithObjects:@"email",@"publish_actions", nil]; [self.facebook authorize:permissions]; [permissions release]; } else {NSLog(@"session still valid");} [loginButton setTitle:@"Sign Out"]; } else if([loginButton.title isEqualToString:@"Sign Out"]) { [loginButton setTitle:@"Sign In"]; [self.facebook logout:self.mainDelegate]; } }
它是如何工作的...
在这个菜谱中,我们首先扩展了我们的类以包含UIActionSheetDelegate和FBRequestDelegate类协议及其方法。然后我们声明了一个名为facebook的实例变量,这将使我们能够访问Facebook类的方法。接下来,我们添加了一个Facebook类的属性实例以创建类获取器和设置器,然后继续合成我们在AppDelegate.h接口文件中定义的facebook变量。
接下来,我们导入我们的FBConnect.h接口文件的接口文件头信息,以便我们可以访问请求对话框类方法。接下来,我们声明一个指向我们的delegate类中属性和类方法的mainDelegate应用程序委托对象。接下来,我们在ViewController中初始化我们的facebook对象,使用与我们在delegate类中实例化的相同对象实例。最后,我们使用UITextView控件初始化我们的用户信息详情。
接下来,我们创建我们的loginButton:方法,并检查按钮的状态。如果按钮读取为登录,我们使用facebook对象的isSessionValid方法来确定我们是否仍然与facebook实例保持有效连接。
如果它证明我们的会话已过期,我们初始化并将请求访问用户电子邮件的权限传递给我们的facebook类的authorize方法,并允许 iOS 发布到Open Graph API操作。最后,我们释放由我们的permissions对象分配的内存,然后更新按钮的标题为注销。如果我们的按钮通过loginButton:方法读取为注销,我们更新按钮的标题为登录,然后调用Facebook类的logout方法,并传入我们的mainDelegate对象。当这个方法被调用时,它将在我们的AppDelegate类中调用FBSessionDelegate的fbDidLogout方法来处理任何注销后的操作和对象的释放,以及通知用户已成功注销。
注意
当调用logout方法时,你的应用程序权限不会被撤销;它只会清除你应用程序的access_token值。如果之前已经从你的应用程序注销的用户决定再次运行它,他们只会看到一个通知,表明他们正在重新登录你的应用程序,而不是请求权限的通知。有关 Facebook 协议方法的更多信息,请参阅以下 URL:developers.facebook.com/docs/reference/iossdk/#protocols
参见
- 请求额外的 Facebook 权限食谱
请求额外的 Facebook 权限
在这个食谱中,我们将学习如何指定我们的应用程序要使用的额外权限。
准备工作
在我们之前的食谱基础上,我们将学习如何实现额外的权限。
如何做...
要开始,按照以下顺序遵循给定的简单步骤:
- 
从项目导航器打开 AppDelegate.m实现文件。
- 
接下来,修改 didFinishLaunchingWithOptions:方法,如下面的代码片段所示:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after // application launch. self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; self.window.rootViewController = self.viewController; // Do any additional setup after loading the view, // typically from a nib. self.facebook = [[Facebook alloc] initWithAppId:@"YOUR_APPID_HERE" andDelegate:self]; // Check and retrieve authorization information NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if ([defaults objectForKey:@"FBAccessTokenKey"] && [defaults objectForKey:@"FBExpirationDateKey"]) { self.facebook.accessToken = [defaults objectForKey:@"FBAccessTokenKey"]; self.facebook.expirationDate = [defaults objectForKey:@"FBExpirationDateKey"]; } // Set up the permissions to use for this App NSArray *permissions = [[NSArray alloc] initWithObjects: @"user_likes", @"user_birthday", @"user_interests", @"read_stream",nil]; // Check to ensure that we have a valid session object if (![self.facebook isSessionValid]) { [self.facebook authorize:permissions]; } [permissions release]; [self.window makeKeyAndVisible]; return YES; }
它是如何工作的...
在这个示例中,我们首先声明一个用于存储我们想要请求的每个权限的 NSArray 对象变量 permission。然后,我们将此变量传递给 facebook 对象的 authorize 方法,最后释放对象分配的内存。
注意
有关您可用的完整权限列表的更多信息,请参阅以下 URL 的 Facebook 权限参考:developers.facebook.com/docs/authentication/permissions/
还有更多...
当在您的应用程序中使用 Facebook 集成时,您可以指定应用程序要使用的额外权限。当您未指定额外权限启动应用程序时,这将使用默认权限,并且只能读取用户的基本信息,这包括 User 对象的某些属性,如 id、姓名、图片、性别和他们的地区。如果您想读取额外的数据或将数据发布回 Facebook,您将需要请求这些额外权限。
这些额外权限分为以下几部分,如给定的表格所示:
| 请求的权限 | 描述 | 
|---|---|
| 基本信息(无权限) | 当用户授权您的应用程序且您未指定额外权限时,您的应用程序将只能访问用户的基本信息。这包括某些属性,如他们的 id、姓名、性别、地区和他们的个人资料图片。 | 
| 用户和好友权限 | 作为授权过程的一部分,您还可以请求对用户个人资料的额外访问权限。您可以访问诸如他们的生日、活动、签到和教育历史等信息。然而,用户必须在启动时授权,以便继续并授权您的应用程序。 | 
| 扩展权限 | 如果您正在使用增强型授权对话框,则将向用户展示扩展权限。这类权限允许您读取您用户的朋友列表、读取用户的邮件收件箱、访问您用户的朋友请求,并代表用户创建和修改事件。 | 
| 开放图权限 | 这类权限允许您的应用程序向 Open Graph API 发布操作,并使其能够检索任何其他应用程序已发布的任何操作。 | 
| 页面权限 | 这类权限允许您检索用户管理的页面和应用程序的 access_tokens,并且仅与 Graph API 兼容。 | 
参见
- 
实现单点登录(SSO)功能的配方 
- 
使用 Graph API 读取 JSON 数据 的配方 
使用 Graph API 读取 JSON 数据
在这个菜谱中,我们将学习关于图形 API 和 Facebook 查询语言(FQL)的内容。图形 API 是 Facebook 的核心,它通过表示图中每个对象(例如,人、照片、活动和页面)以及它们之间的连接(例如,朋友关系、共享内容和照片标签)来表示一个简单的社会图。
准备工作
在我们之前的菜谱之后,我们将学习如何通过将图形路径传递给request方法来访问图形 API,这是非常简单的。这可以通过遵循以下步骤来实现。
如何做...
在facebook DEVELOPERS网站上,您可以访问图形 API 探索器来了解更多关于返回的信息类型,并查看Facebook 查询语言(FQL)产生的数据模式的视觉表示。
- 
登录到以下地址的facebook DEVELOPERS网站: developers.facebook.com/tools。
- 
接下来,从工具部分,点击图形 API 探索器链接。 ![如何做...]() 这将显示图形 API 探索器窗口,并在中心视图中显示返回的每个数据字段的说明。 
- 
接下来,从应用程序部分,从下拉菜单中选择社交网络应用程序。 ![如何做...]() 
- 
然后,点击提交按钮以检索当前用户 ID 的所有基本信息。 
您已经看到使用图形 API 探索器检索各种类型信息的数据视觉表示是多么容易。让我们看看我们如何访问当前登录用户的信息。
- 
打开位于 SocialNetworkApp文件夹中的ViewController.m实现文件。
- 
接下来,创建以下代码部分,如代码片段中指定: #pragma mark - Facebook GraphAPI Method -(void)getGraphAPIData { // Make a call using the Facebook Query Language to // get the current user details. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"SELECT quotes, uid, name, pic FROM user WHERE uid = me()", @"query",nil]; [self.facebook requestWithMethodName:@"FQL.query" andParams:params andHttpMethod:@"POST" andDelegate:self]; } #pragma markCalled when the Graph API call has completed. -(void)request:(FBRequest *)request didLoad:(id)result { if ([result isKindOfClass:[NSArray class]]) { result = [result objectAtIndex:0]; } // This callback can be a result of getting the users // basic information or getting the user's permissions. if ([result objectForKey:@"name"]) { // Retrieve back the basic user information. NSString *concatString = [[NSString alloc] initWithFormat:@"ID: %@\nName: %@\nQuotes: %@\n", [result objectForKey:@"uid"], [result objectForKey:@"name"], [result objectForKey:@"quotes"]]; // Get the profile image UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[result objectForKey:@"pic"]]]]; self.imgPhoto.image = image; self.userInfoDetails.text = concatString; } } #pragma markCalled when an error has occurred while #pragma mark retrieving GraphAPI details -(void)request:(FBRequest *)request didFailWithError:(NSError *)error { NSLog(@"An error occurred obtaining details: %@",error); }
它是如何工作的...
在这个菜谱中,我们首先声明一个NSMutableDictionary对象变量param,它将被用来将 SQL 查询传递给 FQL 对象。这使得我们能够使用 SQL 风格的接口通过图形 API 查询数据。
然后,我们调用facebook对象的requestWithMethodName方法,并将type设置为FQL.query,这告诉该方法我们正在传递一个查询字符串,并返回一个字典数组对象的内容。当我们的requestWithMethodName方法完成后,它调用request方法的didLoad方法。此方法使用 JSON 调用解析结果。
接下来,我们检查返回的类型。如果返回多个结果,则返回一个NSArray对象,否则对于单个结果值返回一个NSDictionary对象。然后,我们将我们的结果设置为指向数组中的第一个位置,然后检索uid、name和quotes的每个字段。然后我们声明一个UIImage变量image,然后将个人资料图片类型转换为UIImage,将其分配给我们的表单上的imgPhoto控件,并显示相关的个人资料详情。最后,如果我们的requestWithMethodName方法完成时确定存在 JSON 解析错误,则调用didFailWithError方法。任何错误信息都包含在NSError变量error对象中。
注意
有关 Graph API 和 FQL 查询语言的更多信息,请参阅以下位置的 Facebook API 和 FQL 参考材料:developers.facebook.com/docs/reference/api/ 和 developers.facebook.com/docs/reference/fql/
参见
- 与 Facebook 社交渠道集成的示例
与 Facebook 社交渠道集成
在这个示例中,我们将学习如何从表格视图中删除一个项目,以及我们的 Core Data 模型。
准备工作
在我们的前一个示例之后,我们将学习如何从我们的表格视图中删除一行,并将其永久地从我们的 Core Data 数据库中删除。
如何操作...
首先,按照给定的顺序遵循简单的步骤:
- 
打开位于 SocialNetworkApp文件夹中的ViewController.m实现文件。
- 
接下来,创建以下代码部分,如代码片段中指定: #pragma markSend a notification request to a group of friends - (void)sendNotificationRequest{ NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"invites you to check out some great stuff.", @"message", @"Check this out", @"notification_text",nil]; // Display the Facebook Request Notifications DialogBox [self.facebook dialog:@"apprequests" andParams:params andDelegate:self.mainDelegate]; } #pragma markFBDialogDelegate - (void)dialogDidComplete:(FBDialog *)dialog { NSLog(@"dialog completed successfully"); } #pragma markPost a message to the current user's Wall. - (void)postMessagetoWall{ NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"Testing FacebookSampleApp Feed Dialog", @"name", @"Using Feed Dialogs within iOS are great.", @"caption", @"Click to check out my BlockHeadz game on the AppStore", @"description", @"http://itunes.apple.com/app/blockheadz/ id386884355?mt=8#", @"link", @"http://geniesoftstudios.com/blog/wp- content/uploads/2011/03/blockhead.png",@"picture",nil]; // Display the Facebook feed dialog with our array. [self.facebook dialog:@"feed" andParams:params andDelegate:self.mainDelegate]; } #pragma mark called when user presses the Post Message Button - (IBAction)postMessage:(id)sender { // Define an instance of our action sheet UIActionSheet *actionSheet; // Initialize our action sheet with the // differen't mapping types. actionSheet = [[UIActionSheet alloc] initWithTitle:@"Choose from the list below" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Close" otherButtonTitles:@"Send Notification",@"Submit new post", @"Obtain User Details",nil]; // Set our Action Sheet style and then // display it to the user. actionSheet.actionSheetStyle = UIBarStyleBlackTranslucent; [actionSheet showInView:self.view]; } // Delegate that handles the chosen action sheet options -(void)actionSheet:(UIActionSheet *)actionSheetclickedButtonAtIndex:(NSInteger)buttonIndex{ // Determine the chosen item switch (buttonIndex) { case 1: [self sendNotificationRequest]; break; case 2: [self postMessagetoWall]; break; case 3: [self getGraphAPIData]; break; default: break; // Catch the Close button and exit. } }
它是如何工作的...
在这个示例中,我们首先声明一个NSMutableDictionary对象变量params,该变量将用于传递消息和通知文本,使用@message和@notification_text参数。然后,我们使用facebook对象的dialog方法,并告诉对话框我们想要使用apprequests对话框。接下来,我们声明一个名为dialogDidComplete的方法,该方法在requests对话框成功显示给用户时被调用。在postMessagetoWall:方法中,我们声明一个NSMutableDictionary对象变量params,该变量将用于传递消息和通知文本,使用@name、@caption、@description、@link和@picture属性。这些属性定义了在向用户的墙发布消息时显示的信息。接下来,我们使用facebook对象的dialog方法,并告诉对话框我们想要使用feed对话框,因为我们正在向墙发布详细信息。
接下来,在我们的postMessage:方法中,我们声明并实例化一个基于UIActionSheet类的actionsheet对象,然后初始化我们的操作表,以显示我们想要执行的不同类型的操作,作为可供选择的选项列表。接下来,我们使用UIActionSheet类的actionSheetStyle属性设置我们的操作表样式,然后使用showInView:self.view方法将操作表显示在当前视图中。
在我们的下一部分,我们定义一个委托方法来确定从操作表按下的按钮,并使用actionSheet属性的clickedButtonAtIndex方法。然后我们检查buttonIndex变量的值以确定按下的按钮索引。
还有更多...
Facebook iOS SDK 为您提供了一个简单的方法,使您的应用程序能够与 Facebook 社交渠道集成。使用这些社交渠道允许您的用户向他们的墙提交帖子,或向他们的朋友发送通知请求。
iOS SDK 为您提供了一个通过 Facebook 平台对话框使用社交渠道进行集成的功能。以下表格列出了 Facebook 目前支持的对话框:
| 社交渠道对话框 | 描述 | 
|---|---|
| 喂食对话框 | 此对话框用于向用户的新闻源发布帖子。 | 
| 请求对话框 | 此类型对话框允许您向一个或多个朋友发送请求。 | 
当使用 Facebook 请求时,这些社交渠道对话框为您提供了允许用户邀请他们的朋友到您的 iOS 应用程序或甚至接受他们朋友礼物的绝佳方式。
请求是通过Request对话框发送的,如果用户的 iOS 设备支持push通知,那么每当发送通知请求时,他们将通过 Facebook iOS 应用程序收到推送通知。
相关内容
- 如何在您的应用程序中处理 Facebook 错误配方
在您的应用程序中处理 Facebook 错误
在这个配方中,我们将学习如何在我们的应用程序中处理 Facebook 错误。
准备工作
在我们之前的配方中,当使用 Facebook SDK 时,在您的应用程序中处理错误非常简单。如果在您的 iOS 应用程序中发生任何错误,FBRequestDelegate和FBDialogDelegateprotocols将立即处理它们。
如何操作...
首先,按照给定的顺序遵循简单的步骤:
- 
从项目导航器中打开 ViewController.m实现文件。
- 
接下来,创建以下代码部分,如以下代码片段中指定: # pragma mark method is called when an error has occurred # while pragma mark retrieving GraphAPI details. -(void)request:(FBRequest *)request didFailWithError:(NSError *)error{ NSLog(@"An error occurred obtaining details: %@",error); } - (void)dialog:(FBDialog*)dialog didFailWithError:(NSError *)error{ NSLog(@"An error occurred obtaining details: %@",error); }
工作原理...
在这个配方中,我们首先创建request::(FBRequest *)request:didFailWithError:,当使用 Graph API 请求信息时发生错误时会被调用。任何发生的错误都将被捕获并返回在error对象中。
如果在对话框过程中出现错误,将调用dialog:didFailWithError方法。发生的任何错误都将被捕获并返回到error对象中。
注意
关于如何使用 Facebook iOS SDK 处理错误的更多信息,您可以参考以下 URL 的 Facebook iOS 参考文档:developers.facebook.com/docs/mobile/ios/build/#errors
还有更多…
在我们拥有一个完整的运行应用程序之前,我们还需要实现一些功能。我们需要对我们的应用程序属性列表进行一些更改,以便在应用程序运行时启用单点登录(SSO)支持。
这可以通过遵循以下简单步骤实现:
- 
从项目导航器中选择 SocialNetworkApp-info.plist文件。
- 
接下来,在面板中心右键点击,然后从弹出列表中选择添加行。 
- 
添加一个名为 URL Types的新条目,并将其类型设置为数组。
- 
右键点击,然后在URL类型中选择添加行。 
- 
然后,创建一个名为 Item 0的新条目,并将其类型设置为字典。
- 
接下来,创建一个名为 URL Schemes的新条目,并将其类型设置为数组。
- 
然后,创建一个名为 Item 0的新条目,并将其类型设置为字符串。
- 
最后,使用我们在注册移动应用程序时注册的 Facebook AppID 值,将其输入到值字段中。您必须在此前加上 fb,然后加上您的 App ID,这样才能正确工作。![还有更多…]() 如前一个屏幕截图所示,我们已经修改了我们的 .plist文件,使其能够支持 SSO。我们特别创建并注册了两个 URL 变量:URL Types和URL Schemes,以便 iOS 能够唯一地识别您的应用程序。
- 
接下来,通过选择产品 | 从产品菜单运行,或 Mac 用户可以按Command+ R来构建和运行应用程序。 
以下屏幕截图显示了在 iOS 设备上运行的应用程序:

从前面的屏幕截图可以看出,当我们首次加载我们的社交网络应用程序时,我们会收到带有我们请求的权限的授权对话框。
一旦用户点击了登录按钮,对话框将消失,我们的 iOS 应用程序将显示出来(如屏幕截图的第二部分所示)。
然后,我们按下操作按钮,并选择提交新帖子按钮来显示发布到我们墙上的新消息,这显示在最后的屏幕截图中。
参见
- 
集成 Facebook 社交渠道配方 
- 
实现单点登录(SSO)功能配方 
第十章。打包和部署您的应用程序
在本章中,我们将涵盖:
- 
设置您的 iOS 开发团队 
- 
创建 iOS 开发证书 
- 
从苹果公司获取开发证书 
- 
注册您的 iOS 设备以进行测试 
- 
创建您的应用程序 App ID 
- 
创建开发配置文件 
- 
使用配置文件在 iOS 设备上安装应用程序 
- 
使用 iTunes Connect 准备应用程序提交到 App Store 
- 
使用 Xcode 4 归档和提交应用程序 
简介
恭喜您做到了这里。您已成功构建了应用程序,现在您准备将其发布到全世界。您现在需要做的就是决定如何部署和推广它。
我们将查看提交您的应用程序到苹果 App Store 所需的内容,并与社区分享您的创作。在本章中,我们将学习如何设置您的 iOS 开发团队以及开发和分发所需的证书。
我们还将学习如何创建开发和分发所需的配置文件,以及创建与 iOS 应用程序关联的必要应用程序 App ID。最后,我们将学习如何注册用于测试的 iOS 设备,以及提交您的应用程序到苹果 App Store 所需的步骤。
设置您的 iOS 开发团队
在本食谱中,我们将学习创建和设置 iOS 开发团队的步骤。
准备工作
在您可以将应用程序提交给苹果 App Store 进行审核之前,您需要设置您的 iOS 开发团队。
如何操作...
首先,按照以下简单步骤操作:
- 
登录到 iOS 开发者门户网站 developer.apple.com/。
- 
点击屏幕顶部的会员中心链接。 
- 
使用您的 Apple ID 和密码登录您的账户。这将显示开发者程序资源页面,如下面的截图所示: ![如何操作...]() 
- 
接下来,点击前面截图中突出显示的iTunes Connect按钮。这是您可以检查各种事项的地方,例如销售和趋势和管理您的应用程序。 ![如何操作...]() 
- 
接下来,点击管理用户以添加自己或组织内的其他人,他们可以登录到 iOS 开发者程序门户,在 iOS 设备上测试应用程序,并能够将额外的 iOS 设备添加到账户中。 ![如何操作...]() 
- 
选择前面截图中突出显示的iTunes Connect 用户按钮。这将弹出添加新用户选项面板,您可以从其中添加新用户,如下面的截图所示: ![如何操作...]() 
- 
前一个屏幕截图中的列表显示了一个现有用户的列表,这些用户已经预先设置,包括他们的详细信息以及他们被分配的角色,他们被设置以访问这些角色。 
- 
接下来,点击如图所示的前一个屏幕截图中的 添加新用户。 
- 
然后,填写您将要添加到您的开发团队的人的 个人信息 部分。完成之后,点击以下屏幕截图所示的 继续 按钮: ![如何操作...]() 
- 
接下来,我们需要分配用户将承担哪些角色,如图所示: ![如何操作...]() 
- 
接下来,只需从四个选项中选择一个,然后点击 继续 按钮以进入向导的最终步骤。 
- 
接下来,选择如图所示的 通知 选项卡。此部分是我们将分配相关通知类型和地区的地方: ![如何操作...]() 
- 
一旦您为每个地区指定了不同类型的通知方法,请点击 保存更改。然后,将创建一个新的用户帐户,并发送确认电子邮件到用户的帐户,以便他们激活他们的帐户。 
- 
以下表格解释了前一个屏幕截图中显示的每种不同类型的通知: 通知 描述 应用状态 提供带有应用状态更新的电子邮件警报。 合同 提供带有合同状态更新的电子邮件警报(例如,合同到期警告)或如果 iTunes 需要更多合同信息。 财务报告 当财务报告在 iTunes Connect 上可供下载时,提供电子邮件警报。 支付 当您的银行机构返回付款时,提供电子邮件警报。 
工作原理...
在本菜谱中,我们探讨了创建和分配用户角色所需的必要步骤,以及哪些用户角色允许登录 iOS 开发者门户来管理用户、查看销售或趋势、以及支付和财务报告,以及那些有能力添加新设备以在各个不同的 iOS 设备上测试应用程序的用户。
在下一个菜谱中,我们将查看生成 iOS 开发证书所需的步骤。此证书是加密的,作为您的数字身份,您必须在运行和测试您在 iOS 设备上开发的任何应用程序之前使用此证书签名您的应用程序。
相关内容
- 创建 iOS 开发证书 菜单
创建 iOS 开发证书
在本菜谱中,我们将学习如何创建 iOS 开发证书,以便我们能够在 iOS 设备上运行和测试我们的应用。
准备工作
我们将首先生成 iOS 开发证书。这个证书是加密的,并作为您的数字身份使用。在您可以在 iOS 设备上运行和测试您开发的任何应用程序之前,您必须使用此证书签名您的应用程序。
如何操作…
首先,按照以下步骤操作:
- 
启动 Keychain Access 应用程序,该应用程序可以在 /Applications/Utilities文件夹中找到。
- 
接下来,从Keychain Access | Certificate Assistant中选择从证书颁发机构请求证书…选项,如图所示: ![如何操作…]() 
- 
接下来,在生成证书之前,我们需要提供一些信息。按照以下截图所示输入所需信息,确保您已选择保存到磁盘和让我指定密钥对信息选项: ![如何操作…]() 
- 
一旦填写完所有信息,请点击继续按钮。您将被要求为证书指定一个名称;接受默认建议的名称,然后点击保存按钮。 
- 
在此阶段,证书正在指定位置创建。您将被要求指定密钥大小和算法。 
- 
接受默认的2048 位和RSA 算法位。点击继续按钮,当出现最终屏幕时,点击完成。 
它是如何工作的...
在本食谱中,我们学习了如何使用预安装的 Mac OS X Keychain Access 应用程序生成 iOS 开发证书签名请求(CSR)。此证书是必需的,并将用于对 iOS 应用程序进行代码签名,使我们能够将应用程序部署到 iOS 设备进行开发和测试。
接下来,我们学习了如何使用 Keychain Access 应用程序中的证书助手请求证书,该应用程序是预安装的。然后我们学习了如何填写证书信息以及证书的密钥对信息,这些信息将被开发和分发配置文件使用。通用 名称字段是在您开始对应用程序进行代码签名时使用的名称。
相关内容
- 从苹果获取开发证书食谱
从苹果获取开发证书
在本食谱中,我们将学习如何从苹果请求开发证书。
准备工作
基于之前的食谱,现在我们将学习如何从苹果获取开发证书。
如何操作…
首先,按照以下步骤操作:
- 
打开您的浏览器,并在地址栏字段中登录以下链接 developer.apple.com/devcenter/ios/index.action的 iOS 开发者门户。
- 
确保您已使用 Apple 凭据登录。 
- 
接下来,点击页面右侧的iOS 配置文件门户。 ![如何操作…]() 
- 
点击面板左侧的证书选项卡,然后点击以下截图所示的开发选项卡: ![如何操作…]() 
- 
点击请求****证书按钮,然后点击选择文件按钮。 
- 
接下来,选择您在之前菜谱中创建的证书请求文件,完成后点击提交按钮。 
- 
在这一点上,您应该看到证书将显示待发行状态。这如图所示: ![如何操作…]() 
- 
几秒钟后,页面将刷新,证书将准备好,您将能够下载它。 
- 
下载后,双击文件,在 Keychain Access 应用程序中安装它,如图所示: ![如何操作…]() 
它是如何工作的…
在本菜谱中,我们首先从 Apple 请求开发证书,这将用于提供给我们代码签名我们的应用程序的能力,以便将我们的应用程序部署到 iOS 设备上。接下来,我们查看如何使用之前菜谱中创建的生成的证书请求文件来生成开发证书,此时证书将处于待发行状态,几秒钟后您的证书将可供下载。
最后,我们查看如何使用下载的证书,并使用 Keychain Access 应用程序安装它。在我们的下一个菜谱中,我们将看看如何注册 iOS 设备以用于开发和测试。
相关内容
- 注册您的 iOS 设备以进行测试菜谱
注册您的 iOS 设备以进行测试
在本菜谱中,我们将学习如何注册 iOS 设备,以便它可以通过使用唯一设备标识符(UDID)支持移动配置文件。
准备工作
在我们之前的菜谱基础上,我们将学习如何使用 UDID(或通用唯一标识符(UUID))注册 iOS 设备,以便它可以用于开发和测试。
如何操作…
要开始,请按照以下简单步骤操作:
- 
从 /Xcode4/Applications文件夹中启动 Xcode。
- 
通过选择窗口 | 组织者打开组织者窗口。 
- 
确保您已将您的 iOS 设备连接到 Mac。 
- 
接下来,复制以下截图所示的标识符字段,并使用您喜欢的文本编辑器保存它。 ![如何操作…]() 
- 
点击用于开发选项。 
- 
用于开发按钮选项可能并不总是可见,因为这取决于您之前是否指定了使用 iOS 设备。 
- 
接下来,重新登录到Apple iOS 开发者中心页面,并点击页面右侧的iOS 配置文件门户链接。 
- 
然后,从iOS 配置文件门户页面,点击设备标签页。 
- 
之后,点击管理标签页,然后点击以下截图所示的添加设备按钮: ![如何操作...]() 
- 
接下来,为将用于测试的每个设备提供一个有意义的名称。 
- 
最后,为设备名称和设备 ID的 UDID 提供值,然后点击提交按钮以保存更改。 
工作原理...
在本教程中,我们探讨了注册用于开发和测试的 iOS 设备所需的步骤,这些设备将使用移动配置文件。我们了解到,为了实现这一点,您需要使用配置文件中每个设备 UDID。如果未能提供此信息,则在尝试部署应用程序时将出现错误。
在接下来的步骤中,我们继续登录到iOS 配置文件门户,为每个设备添加配置文件,并粘贴每个设备的 UDID 信息。点击+按钮可以一次性添加多个设备。点击提交按钮后,您将成功注册您提供的每个设备。如果您打算部署到更多设备,则需要执行相同的过程。
相关内容
- 创建您的应用程序 App ID教程
创建您的应用程序 App ID
在本教程中,我们将学习如何创建应用程序 App ID,以便我们可以使用这些 ID 将我们的应用程序部署到 iOS 设备上进行测试。
准备工作
基于我们之前的教程,我们将学习创建应用程序 App ID 所需的步骤。
如何操作...
首先,按照以下简单步骤操作:
- 
登录到Apple iOS 开发者中心页面。 
- 
接下来,点击页面右侧的iOS 配置文件门户链接。 
- 
然后,点击页面左侧的App IDs菜单。 
- 
接下来,点击以下截图所示的新建 App ID按钮: ![如何操作...]() 
- 
接下来,提供一个描述,该描述将用于识别 CoreLocation,因为这是您稍后将要使用的 App ID。
- 
点击使用团队 ID为捆绑种子 ID(App ID 前缀),确保您为您的捆绑标识符(App ID 后缀)提供一个合适的名称。 
- 
使用团队 ID选项可能并不总是可见的,因为这取决于您是作为个人还是公司设置的。捆绑标识符需要与您的应用程序的捆绑标识符相同。这可以指定为 12345678.com.yourcompany.yourappname,或者可以是提交日期,即20130512.com.yourcompany.yourappname。
- 
最后,点击提交按钮。 ![如何操作...]() 
- 
在以下截图中,您现在应该看到您在上一步骤中创建的新 App ID,以及您可能之前创建的 App ID: ![如何操作...]() 
工作原理...
在这道菜谱中,我们探讨了创建应用程序 App ID 所需的必要步骤,这是每个 iOS 应用程序都必须包含的,并且必须包含一个唯一的应用程序 ID 来标识自己。App ID 是配置文件的一部分,用于标识一个应用程序或一系列相关应用程序。当您的应用程序与 iOS 硬件配件、Apple 推送通知服务进行通信,以及当您的应用程序之间共享数据时,都会使用 App ID。
相关内容
- 创建开发配置文件菜谱
创建开发配置文件
在这道菜谱中,我们将学习如何创建开发配置文件,以便应用程序可以安装到 iOS 设备上。
准备工作
在上一道菜谱的基础上,我们将学习创建我们的开发配置文件所需的步骤,以便在部署到 Apple App Store 之前,可以在 iOS 设备上进行测试安装。
如何操作...
首先,按照以下简单步骤操作:
- 
登录到Apple iOS 开发者中心页面。 
- 
接下来,点击页面右侧的iOS 配置文件门户链接。 
- 
点击配置文件选项卡,然后点击以下截图所示的新建配置文件按钮: ![如何操作...]() 
- 
接下来,将 Development_Profile作为配置文件名称的值输入,确保您选择了所有希望与该配置文件关联的证书。
- 
将App ID的值选择为CoreLocation,然后检查所有您希望配置的设备,完成后点击提交按钮。 ![如何操作...]() 小贴士您可以选择通过点击iOS 配置文件门户中的设备选项卡来注册额外的设备,然后按照本章中注册测试设备菜谱中的步骤操作。 
- 
在这一点上,配置文件将显示为待批准状态。几秒钟后,您应该看到状态从待批准变为活动。如果这种情况没有发生,您可能需要刷新浏览器。此时,您将能够下载您的移动配置文件。 ![如何操作...]() 
- 
点击下载按钮以下载您的配置文件。您会注意到,当您下载移动配置文件时,文件将被命名为 Development_Profile.mobileprovision。在下一道菜谱中,我们将探讨如何使用此证书将应用程序部署到 iOS 设备上。
工作原理...
在这道菜谱中,我们学习了如何创建配置文件,以便您的应用程序可以安装到真实的 iOS 设备上。这使您能够指派有权在各自的设备上安装和测试应用程序的团队成员。
每当您将应用程序部署到 iOS 设备上时,它将包含每个团队成员的 iOS 开发证书,以及 UDID 和 App ID。
参见
- 使用配置文件在 iOS 设备上安装应用程序配方
使用配置文件在 iOS 设备上安装应用程序
在本配方中,我们将学习如何使用开发配置文件,以便应用程序可以安装到 iOS 设备上。
准备工作
在我们之前的配方基础上,我们将学习如何修改我们在前一章中创建的一个现有示例,以便它可以在 iOS 设备上进行测试。
如何操作...
首先,按照以下简单步骤操作:
- 
从 /Xcode4/Applications文件夹中启动 Xcode。
- 
导航到文件 | 打开或按Command + O。 
- 
选择 CoreLocation.xcodeproj文件。
- 
接下来,点击打开按钮继续,并将应用程序打开到 Xcode 工作区环境中。 
- 
接下来,通过导航到窗口 | 组织者或按Shift + Command + 2打开组织者窗口。![如何操作...] 
- 
此过程将使我们能够将我们在前几节中创建的移动配置文件添加到我们的项目中。 
- 
然后点击导入按钮,选择 Development_Profile.mobileprovision文件,并点击打开按钮。
- 
接下来,从项目导航器中选择 CoreLocation项目。
- 
然后从构建设置选项卡,转到代码签名部分。 
- 
选择任何 iOS SDK,并从下拉列表中选择您的证书 ![如何操作...]() 
- 
您的证书将以粗体显示,您的配置文件以灰色显示。如果您没有导入有效的配置文件证书,您将无法部署或上传 iOS 应用程序到苹果 App Store。 
- 
接下来,从项目导航器窗口中,点击您的项目,点击目标部分,然后点击摘要页面。 
- 
滚动直到到达权限部分,并勾选启用权限复选框。这将向您的项目添加一个名为 CoreLocation.entitlements的文件。
- 
当您勾选启用权限复选框时,Xcode 会为您创建一个 entitlements文件(如果不存在),并添加一个密钥链访问组键值对,您需要记住它们直接绑定到您的应用程序配置文件。
- 
构建并运行您的应用程序以查看是否一切正常。您将被要求授权证书。点击始终允许。 
它是如何工作的...
在这个菜谱中,我们学习了如何导入我们的开发证书的移动配置文件,然后修改我们在前一章中创建的CoreLocation示例应用程序,以便可以使用此配置文件将其部署到 iOS 设备。然后我们查看如何修改项目的代码签名权限部分,以确保代码签名标识字段反映了移动配置文件的详细信息。
在我们的最后几步中,我们学习了如何将entitlements文件添加到项目中。这是一个重要的文件,每当应用程序需要部署到 iOS 设备进行测试或部署到 Apple App Store 时都需要此文件。
参见
- 
使用 Xcode 4 存档和提交应用程序的菜谱 
- 
第六章中的请求 iCloud 存储权限菜谱 
使用 iTunes Connect 准备应用程序提交到 App Store
在这个菜谱中,我们将学习如何准备应用程序提交到 Apple App Store。
准备工作
在我们之前的菜谱之后,当您测试了您的应用程序以确保一切正常且无错误,并且您已设置好所有账户后,您将想要开始上传您的应用程序到 Apple App Store。
如何操作...
要开始,请按照以下简单步骤操作:
- 
登录iTunes Connect,然后点击管理您的应用程序选项。 ![如何操作...]() 
- 
接下来,点击添加新应用链接以开始将您的应用程序添加到 App Store。 
- 
接下来,继续输入我们上传的应用程序的应用程序详细信息。 
- 
点击继续按钮以进行下一步。SKU 编号是您为您的应用程序创建的唯一标识符,如下截图所示: ![如何操作...]() 
- 
在下一步中,我们将指定应用程序的可用日期和价格层级,以便在以下屏幕截图中显示时可供下载: ![如何操作...]() 
- 
有超过 87 个价格层级可供选择,包括免费销售应用程序的选项。点击继续按钮后,您将被引导到元数据屏幕,您需要填写有关您应用程序的信息。该屏幕上的字段及其描述如下表所示: 屏幕字段 描述 版本号 这可以是您喜欢的任何内容。最好从 1.0开始。描述 这是应用程序描述,最多可包含 4,000 个字符。 主要类别 这些包含多达 20 个不同的类别供您选择,包括游戏、娱乐、商业、书籍等。 Secondary Category (optional) 您可以从二级类别中进行选择。 Keywords 这些关键词有助于在客户在 iTunes 中搜索应用程序时更快地返回结果。 Copyright 拥有应用程序独家权利的个人或实体的名称,前面是获得权利的年份(例如, 2013 GENIESOFT STUDIOS)。Contact Email Address 用户在应用程序出现问题时可以联系您的电子邮件地址。 Support URL 一个提供您要添加的应用程序支持的 URL。此信息将在 App Store 中对客户可见。 App URL (optional) 一个包含您要添加的应用程序信息的 URL。如果提供,此信息将在 App Store 中对客户可见。 Review Notes (optional) 关于您的应用程序和/或您的应用内购买的附加信息。Review Notes不能超过 4000 字节。 
当苹果发布 iOS 3.0 时,他们包括了一个评级方案,允许父母控制他们的孩子可以下载哪些应用程序。这是应用程序分发流程的强制区域,必须在您提交应用程序之前完成。评级方案可以在以下屏幕截图中看到:

年龄限制将根据您如何对应用程序进行评级而变化。请确保您正确评级,因为苹果在内部流程中使用此信息,并审查您对应用程序的评分。
注意
如需了解更多关于提交您的应用程序的信息,请查看以下链接:developer.apple.com/library/ios/#documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/11_EditingandUpdatingAppInformation/EditingandUpdatingAppInformation.html#//apple_ref/doc/uid/TP40011225-CH14-SW40。
工作原理...
在这个菜谱中,我们学习了准备将应用程序提交到 Apple App Store 使用 iTunes Connect 所需的步骤。我们了解到,在将您的应用程序提交给 Apple App Store 进行审批之前,您必须确保它运行正常且无问题。接下来,我们探讨了如何为在 Apple App Store 上上传的应用程序创建一个新的应用程序 ID,提供有关应用程序的详细信息,以及它何时可用。
在下一步中,我们探讨了如何指定正在上传的应用程序的定价层级,您可以从超过 87 个定价层级中进行选择,如果您需要免费销售应用程序,也提供了这一功能。
您应该为您的应用程序收取多少费用是开发者和在 App Store 上销售应用程序的公司之间一个棘手的问题。我发现许多开发者似乎采用的一种策略是,开始以 6.99 美元的价格销售他们的应用程序,然后在发布后不久,他们暂时将价格降低几美元,或者有时在有限的时间内提供 50 至 80%的折扣。
通过降低价格,这将创造一个必胜的销售,鼓励人们在限时结束前抢购,这也可以增加您在 iTunes App Store 中的应用程序的销售数量,从而使其进入前十名,甚至达到第一名。
更多内容...
在将您的应用程序提交给苹果 App Store 进行审核之前,最好确保您的应用程序运行正确且无错误,iOS 模拟器是一个良好的起点。尽管在模拟器中不能测试所有内容,但它是一个良好的起点。虽然您的应用程序可能在模拟器中运行得很好,但当它部署到 iOS 设备上时,问题可能仍然存在。因此,始终最好将其部署到运行最新 iOS 版本的实时 iOS 设备上。
您还可以使用 Instruments 应用程序来确保您的应用程序中没有内存泄漏,并避免您的应用程序在用户的 iOS 设备上崩溃。如果您的应用程序崩溃,它也可能阻止您的应用程序成功通过苹果的审核并在 App Store 中显示。
无论何时您想要提交您的应用程序到苹果 App Store,请记住,只有可上传的发布版本。
参见
- 使用 Xcode 4 存档和提交应用程序的菜谱
使用 Xcode 4 存档和提交应用程序
在本菜谱中,我们将学习如何使用 Xcode 4 集成开发环境存档应用程序,以便提交到苹果 App Store。
准备工作
在我们之前的菜谱中,确保我们的CoreLocation项目文件已打开。
如何操作...
首先,按照以下简单步骤操作:
- 
从项目导航器窗口中,点击您的项目,然后点击TARGETS部分,然后点击Build Settings部分。 
- 
接下来,点击Build Settings标签,然后导航到Build Options | Validate Built Product | Release并将其设置为Yes。 ![如何操作...]() 
- 
导航到Product | Edit Scheme…或按Command + <键组合。 
- 
接下来,确保Destination已设置为使用iOS Device,并且Build Configuration已设置为使用Release方案。 ![如何操作...]() 
- 
从Xcode菜单导航到Product | Archive,它将开始创建应用程序存档,如下面的截图所示: ![如何操作...]() 
- 
点击Validate…按钮让 Xcode 验证你的应用程序。为了继续操作,你需要使用你的 Apple ID 登录。 
- 
一旦你满足了必要的验证要求,点击Distribute…按钮将你的应用程序提交给 Apple。 
- 
关于其他分发应用程序方式的更多信息,请查看通过 Apple Developer Connection 网站提供的链接,链接地址为 developer.apple.com/library/ios/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/090-Distribute_Your_App/distribute_app.html。
它是如何工作的...
在这个菜谱中,在存档你的应用程序之前,你需要确保目标应用程序文件是自包含的。这意味着,如果目标应用程序文件依赖于任何静态库,它将确保这些库是应用程序二进制文件的一部分,通过在构建和存档应用程序的目标的构建设置部分将Validate Built Product | Release设置为Yes。
在我们的下一步中,我们需要确保在提交应用程序到 App Store 或与他人分享之前,该方案中的存档操作已为要存档的应用程序类型设置了适当的目标;你创建一个应用程序存档,这将使你能够与其他开发者和测试者分享你的 App(IPA)或将它分发给用户。为了使你的应用程序有资格被包含在 Apple App Store 中,你必须将存档提交到 iTunes Connect。这是为了确保你的应用程序存档通过必要的 iTunes Connect 验证测试。Xcode 可以在你提交之前为你验证这一点。
参见
- 使用 iTunes Connect 将应用程序提交到 App Store 的菜谱
附录 A.探索多点触控界面
在本附录中,我们将涵盖:
- 
感知运动和设备方向 
- 
使用触摸界面进行摇动手势 
- 
使用触摸界面检测设备触摸 
- 
使用加速度计输入感知运动 
- 
理解核心运动框架 
- 
与 iOS 设备陀螺仪协同工作 
简介
与您通信的 Apple iOS 设备的主要界面是其大型的多点触控显示屏。由于没有物理键盘连接,所有操作都通过屏幕完成,以便您以更自然的方式与应用程序交互。任何对象都可以在屏幕上移动,放大和缩小,以及上下滚动,只需使用简单的手势即可。
在本附录中,我们将看到如何轻松地将单点触控和多点触控支持集成到我们的应用程序中,以检测 iOS 设备方向是否已更改,以及如何处理和响应摇动动作,最后我们将了解加速度计和陀螺仪。
感知运动和设备方向
在本食谱中,我们将学习如何检测 iOS 设备的当前方向。
准备工作
创建一个新的单视图应用程序项目,并将其命名为OrientationExample。
如何操作…
首先,按照以下简单步骤进行操作:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,修改如以下代码片段所示的 viewDidLoad方法:- (void)viewDidLoad{ [super viewDidLoad]; [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(hasOrientationChanged:) name:@"UIDeviceOrientationDidChangeNotification" object:nil]; }
- 
接下来,创建如以下代码片段所示的 hasOrientationChanged方法:-(void)hasOrientationChanged:(NSNotification *)notification { UIDeviceOrientationcurrentOrientation; currentOrientation = [[UIDevice currentDevice] orientation]; switch (currentOrientation) { case UIDeviceOrientationFaceUp: self.view.backgroundColor = [UIColor brownColor]; break; case UIDeviceOrientationFaceDown: self.view.backgroundColor = [UIColor magentaColor]; break; case UIDeviceOrientationPortrait: self.view.backgroundColor = [UIColor blueColor]; break; case UIDeviceOrientationPortraitUpsideDown: self.view.backgroundColor = [UIColor blueColor]; break; case UIDeviceOrientationLandscapeLeft: self.view.backgroundColor = [UIColor greenColor]; break; case UIDeviceOrientationLandscapeRight: self.view.backgroundColor = [UIColor redColor]; break; default: self.view.backgroundColor = [UIColor blackColor]; break; } }
- 
通过从产品菜单选择运行或按Command + R键来构建和运行应用程序。 
如果您在 iOS 模拟器中运行此程序,请尝试通过按Command + ;左箭头和Command + 右箭头来更改不同方向的视图,如截图所示:

它是如何工作的…
在本食谱中,我们涵盖了创建示例应用程序的步骤。我们首先告诉 iOS 设备开始为设备将采取的每个方向变化生成通知。然后,我们设置了一个观察者,用于UIDeviceOrientationDidChangeNotification类,每当设备改变方向时都会触发。接下来,我们通过UIDeviceOrientation类推导出我们的 iOS 设备当前的方向。最后,我们通过使用switch…case语句确定当前方向,然后更改视图的背景颜色。
如果你想确定 iOS 设备朝向哪个方向,你可以通过使用UIDevice类并使用其方向属性来获取这个信息。当注册UIDeviceOrientationDidChangeNotification通知方法时,你不仅会被告知 iOS 设备在纵向和横向视图之间是否已旋转,还会知道 iOS 设备是向上还是向下。
注意
如果你想要了解更多关于UIDevice类的信息,可以参考位于链接developer.apple.com/library/ios/#documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html的 Apple 开发者文档。
参见
- 
使用触摸界面的摇晃手势食谱 
- 
在第一章的使用 Xcode 创建 iOS 项目食谱中,获取和安装 iOS SDK 开发工具 
使用触摸界面的摇晃手势
在这个食谱中,我们将学习如何检测和处理 iOS 设备运动事件。
准备工作
创建一个新的单视图应用程序,并将其命名为ShakeExample。
如何做到这一点...
首先,按照以下简单步骤操作:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,按照以下代码片段修改 viewDidLoad方法:- (void)viewDidLoad{ [super viewDidLoad]; [self becomeFirstResponder]; self.view.backgroundColor =[UIColor greenColor]; }
- 
接下来,根据代码片段创建以下代码部分: - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) { self.view.backgroundColor =[UIColor yellowColor]; NSLog(@"Device has been shaken"); } } - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) { // Declare an instance of our Alert View dialog UIAlertView *dialog; // Initialize our Alert View Window with options dialog =[[UIAlertView alloc] initWithTitle:@"Device has been shaken" message:@"I'm all shook up" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK",nil]; // display our alert dialog [dialog show]; } } - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event { self.view.backgroundColor = [UIColor blackColor]; NSLog(@"Device shake has been cancelled"); } - (BOOL)canBecomeFirstResponder { return YES; } // Responds to the options within our Alert View Dialog -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSString *buttonTitle =[alertView buttonTitleAtIndex:buttonIndex]; if ([buttonTitle isEqualToString:@"OK"]) { self.view.backgroundColor =[UIColor greenColor]; NSLog(@"Device has stopped shaking"); } }
- 
将部署目标更改为你的 iOS 设备。 
- 
通过从产品菜单选择运行或按Command + R键来构建和运行应用程序。 
当编译完成后,应用程序将显示在你的 iOS 设备上。尝试摇晃设备以查看以下截图所示的警告消息:

它是如何工作的…
在这个食谱中,我们涵盖了创建示例应用程序的步骤。我们在viewDidLoad方法中将视图背景色初始化为绿色,以表示没有发生摇晃,并通过设置becomeFirstResponder方法将视图设置为第一个响应者,以便支持运动事件。如果不包含此方法,则不会触发任何运动事件,应用程序将不会按预期运行。
接下来,我们需要通过重写motionEnded:motion:withEvent方法,使我们的视图控制器成为UIResponder响应链中的第一个响应者,当摇晃手势结束时将显示一个警告消息。最后,我们实现了motionBegan方法,它确定摇晃何时发生,然后将我们视图的背景色设置为黄色。当设备确定运动停止时,将调用motionEnded方法,在那里我们可以检测发生了什么类型的事件。在这种情况下,我们声明并实例化一个UIAlertView类的实例,并向用户显示一个消息,告知他们摇晃已结束。如果系统认为该运动不是摇晃,则调用motionCancelled方法。设备摇晃被确定为大约一秒钟或更长的长度,然后调用motionEnded方法将我们视图控制器的背景色设置为黑色。
注意
如果您想了解更多关于UIResponder类的信息,可以参考位于链接developer.apple.com/library/ios/#documentation/uikit/reference/UIResponder_Class/Reference/Reference.html的 Apple 开发者文档。
更多信息…
当 iOS 设备被摇晃时,系统会使用加速度计,然后解释加速度计数据以查看是否是摇晃指令。
如果确定这是一个摇晃手势,系统将创建一个UIEvent对象来表示这个手势,然后将对象发送到当前活动应用程序进行处理。在 iPhone 上使用摇晃手势比触摸事件简单得多。当动作开始或停止时,仍然会生成事件,您甚至可以跟踪单个动作,就像您处理触摸事件一样。
为了使您的应用程序能够集成和处理 iOS 的摇晃手势,可以通过实现以下三个方法轻松实现,如下表所示:
| 方法 | 描述 | 
|---|---|
| motionBegan:motion:withEvent: | 当动作事件开始时调用此方法。 | 
| motionEnded:motion:withEvent: | 当动作事件结束时调用此方法。 | 
| motionCancelled:motion:withEvent: | 如果系统认为该运动不是摇晃,则调用此方法。摇晃被确定为大约一秒钟或更长的长度。 | 
注意
动作事件首次在 iOS 3.0 SDK 中引入,摇晃动作目前被解释为手势,然后继续成为动作事件。
参见
- 
检测运动和设备方向食谱 
- 
在第一章的使用 Xcode 创建 iOS 项目食谱中,获取和安装 iOS SDK 开发工具 
使用触摸界面检测设备触摸
在本食谱中,我们将学习如何检测和处理 iOS 设备的各种触摸事件。
准备工作
创建一个新的单视图应用程序,并将其命名为 TapExample。
如何操作…
首先,按照以下简单步骤进行:
- 
从项目导航器打开 ViewController.m实现文件。
- 
接下来,创建名为 touchedBegan的方法,如下面的代码片段所示:- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { UITouch *touch = [[event allTouches] anyObject]; switch ([touch tapCount]){ case 1: self.view.backgroundColor =[UIColor redColor];break; case 2: self.view.backgroundColor =[UIColorgreenColor];break; case 3: self.view.backgroundColor =[UIColor blueColor];break; case 4: self.view.backgroundColor =[UIColoryellowColor];break; case 5: self.view.backgroundColor =[UIColororangeColor];break; default: self.view.backgroundColor =[UIColor redColor];break; } }
- 
确保您已将部署目标更改为您的 iOS 设备。 
- 
通过从“产品”菜单中选择产品 | 运行,或通过按Command + R来构建并运行应用程序。 
编译完成后,应用程序将显示在您的 iOS 设备上。尝试点击设备屏幕,以查看视图背景如以下截图所示改变:

它是如何工作的…
在本食谱中,我们涵盖了创建示例应用程序的步骤。然后我们实现了 touchesBegan:touches 方法,该方法确定在 iOS 设备上何时进行了手势,然后根据总点击次数更新我们的视图背景颜色。
还有更多…
UIView 和 UIViewController 类都是 UIResponder 类的一部分,因为它们可以响应和处理视图内部的事件。通过实现以下任何一种方法,您就可以覆盖您视图或视图控制器使用的方法:
- 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- 
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- 
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- 
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event![还有更多…]() 
每个接收到的 UITouch 触摸都有一个阶段、位置、发生触摸的视图、时间戳和点击次数。在下表中,我们解释了在触发每个触摸阶段时会发生什么:
| 触摸事件 | 描述 | 
|---|---|
| UITouchPhaseBegan | 当用户触摸 iPhone 屏幕上的某个区域时,在触摸生命周期开始时发生。 | 
| UITouchPhaseMoved | 当用户在 iPhone 屏幕上移动手指或手指时发生。 | 
| UITouchPhaseStationary | 当用户在屏幕上的某个区域暂停时发生。 | 
| UITouchPhaseEnd | 当用户从 iPhone 屏幕上移除手指时发生。 | 
| UITouchPhaseCancelled | 当 iOS 设备确定发生了某些事情并且需要中止手势时发生。这种情况的一个例子可能是由于您在接听来电时系统中断,或者当应用程序或窗口视图不再活动时。 | 
注意
如果你想了解更多关于 UIResponder 类的信息,你可以参考位于链接 developer.apple.com/library/ios/#documentation/uikit/reference/UIResponder_Class/Reference/Reference.html 的 Apple 开发者文档。
相关内容
- 
使用触摸界面进行摇动手势 菜单 
- 
感应运动和设备方向 菜单 
- 
在 第一章 的 使用 Xcode 创建 iOS 项目 菜单中,获取和安装 iOS SDK 开发工具 
使用加速度计输入感应运动
在这个菜谱中,我们将学习如何使用 iOS 设备加速度计事件的功能。
准备工作
创建一个新的单视图应用程序,并将其命名为 AccelGyroExample。
如何操作…
要开始,请按照以下简单步骤进行:
- 
从项目导航器中选择 AccelGyroExample项目。
- 
然后从 TARGETS 组下选择你的项目。 
- 
选择 Build Phases 选项卡。 
- 
展开链接二进制与库的展开三角形。 
- 
点击 + 按钮,并从列表中选择 CoreMotion.framework。 
- 
然后点击 Add 按钮将框架添加到你的项目中。 ![如何操作...]() 
- 
我们接下来的步骤是创建负责访问加速度计设备功能的代码功能。 
- 
打开 ViewController.h接口文件,并创建以下突出显示的条目,如以下代码片段所示:// ViewController.h // AccelGyroExample // Created by Steven F Daniel on 19/09/12. // Copyright (c) 2012 GenieSoft Studios. All rights reserved. #import <UIKit/UIKit.h> #import <CoreMotion/CoreMotion.h> @interface ViewController : UIViewController <UIAccelerometerDelegate> { CMMotionManager *motionManager; } @property (nonatomic, retain) CMMotionManager *motionManager; @end
- 
打开 ViewController.m实现文件,并添加以下突出显示的代码片段:#import "ViewController.h" @interface ViewController () @end @implementation ViewController @synthesize motionManager;
- 
然后,修改 viewDidLoad方法,如以下突出显示的代码部分所示:- (void)viewDidLoad { [super viewDidLoad]; // Set up the accelerometer UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; accelerometer.updateInterval = 0.5; accelerometer.delegate = self; }
- 
接下来,创建如以下代码片段所示的 handleAcceleration方法:// Handle processing of the Accelerometer -(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { UIAccelerationValue xAxes; UIAccelerationValue yAxes; UIAccelerationValue zAxes; xAxes = acceleration.x; yAxes = acceleration.y; zAxes = acceleration.z; if (xAxes > 0.5) { // Moving Right self.view.backgroundColor = [UIColor purpleColor]; } else if (xAxes < -0.5) { // Moving Left self.view.backgroundColor = [UIColor redColor]; } else if (yAxes > 0.5) { // Moving Upside Down self.view.backgroundColor = [UIColor yellowColor]; } else if (yAxes < -0.5) { // Standing Up self.view.backgroundColor = [UIColor blueColor]; } else if (zAxes > 0.5) { // Facing Up self.view.backgroundColor = [UIColor magentaColor]; } else if (zAxes < -0.5) { // Facing Down self.view.backgroundColor = [UIColor greenColor]; } double value = fabs(xAxes); if (value > 1.0) { value = 1.0;} self.view.alpha = value; }
- 
将部署目标更改为你的 iOS 设备。 
- 
通过选择 Product | Run 从 Product 菜单或按 Command + R 来构建和运行应用程序。 
当编译完成后,应用程序将显示在你的 iOS 设备上。尝试将你的设备向所有方向移动,并观察背景颜色变化。
它是如何工作的…
在这个菜单中,我们所做的是实现了 UIAccelerometerDelegate 协议,这样我们就可以在我们的 ViewController.m 实现文件中使用它。
然后,我们声明了一个 CMMotionManager 实例 motionManager,这将使我们能够使用加速度计和陀螺仪功能。接下来,我们需要在我们的 ViewController.h 接口文件中合成我们声明的属性。如果我们不声明这个属性,我们将收到警告错误信息,这可能导致应用程序出现意外的错误。
接下来,我们声明将处理加速度计和陀螺仪功能的方法。在第一部分,我们声明了UIAccelerometer类的代理,然后推导出 x、y 和 z 的值,这些值将用于确定当前设备方向并相应地设置背景颜色。
作为最后一步,我们根据值是否在 0.0 到 1.0 的范围内,将背景 alpha 属性从透明设置为不透明,其中 0.0 表示完全透明,1.0 表示不透明。
注意
当你设置视图的 alpha 属性时,它只影响当前视图,不会影响其任何嵌套子视图。fabs函数是一个 C/C++库函数,它返回 X 的绝对值。
如果你想要了解更多关于CoreMotion类的信息,你可以参考以下链接提供的 Apple 开发者文档:developer.apple.com/library/ios/#documentation/CoreMotion/Reference/CoreMotion_Reference/_index.html。
更多内容...
到目前为止,你一直专注于如何检测用户何时进行点击以及如何检测设备震动。我们现在转向真正令人兴奋的内容,并不是说你之前没有覆盖的内容不令人兴奋,而是 iPhone 的加速度计比你想象的要强大得多,并且当 iOS 设备倾斜时,它能够提供关于(x, y, 和 z)轴三个维度的实时数据。
iOS 设备的加速度计数据通过UIAccelerometer类和accelerometer:didAccelerate代理方法提供,这些方法为你提供每个轴的数据,每个轴都是UIAcceleration类。返回的每个值都有一个介于-1 和+1 之间的范围,其中 0 是中间中心点。当设备移动或倾斜时,这些值会增加或减少。
注意
iPhone 4 及以上版本增加了另一个传感器,即三轴陀螺仪。当将陀螺仪与加速度计结合使用时,这使 iPhone 4 具有六个操作轴,并旨在使 iPhone 4 在游戏方面更加敏感、响应和强大。
理解核心运动框架
Core Motion 框架是一个系统框架,它从 iOS 设备上的传感器获取运动数据。然后,应用程序可以使用这些值。传感器数据的处理在 Core Motion 自己的线程中进行,并且它检测加速度计和陀螺仪(目前仅在 iPhone 4 上可用)的运动事件。这可以在以下图中说明:

下表描述了构成 Core Motion 框架的各个组件:
| 核心运动类 | 描述 | 
|---|---|
| CMMotionManager | 此类定义了一个封装运动数据测量的管理类。 | 
| CMAccelerometerData | 此类记录设备加速度的测量,并为每个轴收集加速度计的数据。 | 
| CMDeviceMotion | 这捕获来自加速度计和陀螺仪的设备运动数据。 | 
| CMAttitude | 这包含在 CMDeviceMotion类中,包含不同的姿态测量属性,包括以下内容:俯仰、偏航和横滚。 | 
| CMGyroData | 此类记录设备沿其三个空间轴的旋转速率,来自陀螺仪。 | 
注意
iOS 模拟器不支持加速度计和陀螺仪功能,因此如果您想运行本附录中显示的示例,您需要将它们部署到您的 iPhone 设备上。
参见
- 
使用触摸界面与摇动手势的技巧 
- 
使用 iOS 设备陀螺仪的技巧 
- 
在第一章 使用 Xcode 创建 iOS 项目技巧中,获取和安装 iOS SDK 开发工具 
使用 iOS 设备陀螺仪
在这个技巧中,我们将学习如何使用 iOS 设备陀螺仪事件的功能。
准备工作
我们已经了解了如何使用 iOS 设备的加速度计并根据轴的朝向修改视图的背景颜色。在这个技巧中,我们将探讨如何结合陀螺仪功能。
如何做...
首先,按照以下简单步骤操作:
- 
打开 AccelGyroExample.xcodeproj项目文件。
- 
打开 ViewController.m实现文件,并按以下突出显示的代码片段修改viewDidLoad方法:- (void)viewDidLoad { [super viewDidLoad]; // Set up the accelerometer UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; accelerometer.updateInterval = 0.5; accelerometer.delegate = self; // Perform a check to see if the device // supports the Gyroscope feature if ([self isGyroscopeAvailable] == YES) { motionManager = [[CMMotionManager alloc] init]; [motionManager startGyroUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMGyroData *gyroData, NSError *error) { [self doGyroRotation:gyroData.rotationRate]; }]; } else { // Device does not support the gyroscope feature NSLog(@"No Gyroscope detected."); } }接下来,创建以下代码部分,如以下代码片段中指定的那样: // Handles rotation of the Gyroscope - (void)doGyroRotation:(CMRotationRate)rotation { double value = (fabs(rotation.x)+fabs(rotation.y) + fabs(rotation.z)) / 8.0; if (value > 1.0) { value = 1.0;} self.view.alpha = value; } // Checks to see if Gyroscope is available on the device - (BOOL) isGyroscopeAvailable { #ifdef __IPHONE_4_0 CMMotionManager *gyroManager = [[CMMotionManager alloc] init]; gyroManager.gyroUpdateInterval = 1.0 / 60.0; BOOL gyroAvailable = gyroManager.gyroAvailable; return gyroAvailable; #else return NO; #endif }
- 
将部署目标更改为您的 iOS 设备。 
- 
通过选择产品 | 运行从产品菜单或通过按Command + R来构建和运行应用程序。 
当编译完成后,应用程序将显示在您的 iOS 设备上。尝试将您的设备向所有方向移动,以查看背景颜色开始循环显示各种颜色。
它是如何工作的...
在这个技巧中,我们添加了代码,使用#Ifdef __IPHONE_4_0指令来确定当前使用的设备是否为 iPhone 4。如果是这种情况,它将检查设备是否支持陀螺仪功能,并返回布尔状态YES;否则返回NO。
接下来,我们设置我们的UIAccelerometer代理并更新间隔为每秒两次,以便请求更新。然后我们调用我们的isGyroscopeAvailable函数来检查陀螺仪功能是否受支持。
最后,我们调用startGyroUpdatesToQueue函数,并添加一个处理程序来调用我们的doGryroRotation函数,然后更新我们视图的 alpha 混合颜色。如果设备不支持陀螺仪功能,这将在调试窗口中记录下来。
注意
要开始接收和处理陀螺仪功能的旋转速率数据,您需要创建CMMotionManager类的一个实例,并调用以下方法之一。
以下图像显示了当 iPhone 倾斜时,iPhone 对其三个轴的变化响应。在正常重力下,这些值将在-1 和+1 之间,0 值是中间中心点。快速移动手机会增加这些值:

以下表格解释了与CMMotionManager类相关的每个方法调用:
| CMMotionManager 方法 | 描述 | 
|---|---|
| startGyroUpdates | 当调用此方法时,Core Motion 开始工作,并持续更新 CMMotionManager类的gyroData属性,以获取最新的活动测量值。 | 
| startGyroUpdatesToQueue:withHandler | 在调用此方法之前,您需要确保已设置 gyroUpdateInterval属性的更新间隔。当调用此方法时,它创建一个NSOperationQueue事件,将陀螺仪事件排队,然后在达到更新间隔时触发,然后调用函数并传递最新的陀螺仪数据。 | 
| stopGyroUpdates | 此方法关闭 Core Motion 传感器并停止所有运动数据的更新。始终停止陀螺仪更新是一个好主意,因为这可以节省电池电量。 | 
注意
如果您想了解更多关于CoreMotion类的信息,可以参考位于链接developer.apple.com/library/ios/#documentation/CoreMotion/Reference/CoreMotion_Reference/_index.html的 Apple 开发者文档。
参见
- 
使用触摸界面进行摇动手势配方 
- 
使用加速度计输入检测运动配方 
- 
使用 Xcode 创建 iOS 项目中的使用 Xcode 创建 iOS 项目配方,获取和安装 iOS SDK 开发工具 

 
                    
                     
                    
                 
                    
                









































































































 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号