IOS8-企业级学习指南-全-
IOS8 企业级学习指南(全)
原文:
zh.annas-archive.org/md5/bee97c9541e4b26283f222e705b62035译者:飞龙
前言
本书将帮助你开始并理解 iOS 企业应用程序的开发。它作为一步步的指南,帮助你理解 iOS 应用程序开发的核心概念。它提供了对开发和应用部署方式的深入了解。它将内容无缝组织,帮助你逐步学习如何提高你的概念和编程技能。
本书还将帮助你建立基本的逻辑技能,并教你如何处理大型企业应用程序架构相关的问题。本书还包括 API 集成、社交媒体集成和第三方框架的集成。这将使你能够理解如何使用其他第三方框架轻松定制你的应用程序。这样的内容是本书的核心,使其内容丰富。
主要关注的是让你阅读并实现这本书的细节。在这本书的整个过程中,你会发现一些活动和小的项目,这些项目和活动会教你核心概念和逻辑。这种教学方法使得这本书对你来说特别,因为它总能让你有所玩味。在书的结尾,你将拥有一些带有隐含逻辑的美丽工作应用。
本书涵盖的内容
第一章,iOS 入门,教你 iOS 的基础知识,如方法、数组、属性、代理等。通过阅读这一章,你将能够使用 Xcode 并创建使用 UI 组件的简单应用程序。
第二章,探索更多 UI 组件,教你关于组件以及如何通过编程方式创建它们。它将帮助你理解表格视图的概念,并教你如何使用它们。此外,我们还将使用导航控制器制作一个简单的应用程序。
第三章,探索各种框架,提供了关于 UI 的一些更多细节,并教你如何在项目中集成框架。我们还将通过探索每个部分来创建一个简单的学生注册应用程序。到本章结束时,你将创建一个包含 UI 和功能的完整应用程序。你还将了解社交媒体集成数据库。
第四章,iOS 7 中引入的 APIs,包含了许多有趣的内容,包括游戏、在文本之间插入图片,以及通过 AirDrop 分享。所有这些 API 都是在 iOS 7 中引入的。在这一章之后,你应该尝试扩展其中提到的所有活动;这将使这些概念对你来说更加清晰。
第五章,iOS 8 引入的框架,讨论了新的 iOS 8 API 和一小段 Swift 代码。在本章中,你将了解 PhotoKit 框架和 Handoff,还有一些代码片段。
第六章,使用 iCloud 和安全服务,教你如何将数据推送到 iCloud 并保存你的私人数据,如密码、账户号码和 ATM PIN,到钥匙串。我们还将关注在 iOS 8 中引入的 Touch ID API。
第七章,应用分发程序,教你如何设置并理解你的开发者账户。你还将学习如何设置配置文件以在商店发布你的应用,并讨论使用 Apple 企业账户的企业级分发的结构。
你需要这本书的以下内容
为了开始为 iOS 设备编写应用程序,你需要以下东西:
-
运行 Mavericks(OS X 10.9.5 或更高版本)的基于 Intel 的 Macintosh
-
Xcode
-
你必须注册为 iPhone 开发者,才能在你的设备上测试示例项目
-
运行 iOS 7.0 及以上版本的 iOS 设备
这本书面向的对象
如果你是一名 iPhone 应用程序开发者,甚至如果你是初学者,这本书将帮助你探索你的技术技能。短语和代码以这种方式编写,即使是初学者也能理解它们。对于那些想了解 iOS 7 和 iOS 8 的新框架及其活动和用途的人来说,这本书将非常有帮助。这本书将对学生和经验丰富的 iOS 开发者都有所帮助。
习惯用法
在这本书中,你将找到许多文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。
文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称的显示方式如下:“在AppDelegate.h文件中,找到application:didFinishLaunchingWithOptions:方法。”
代码块按以下方式设置:
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
// Register for push notifications
[application registerForRemoteNotificationTypes:
UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound];
...
}
任何命令行输入或输出都按以下方式编写:
curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash
新术语和重要词汇以粗体显示。你在屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“启动你的 Xcode。导航到文件 | 新建 | 项目。”
注意
警告或重要注意事项以如下方框显示。
小贴士
小技巧和窍门看起来像这样。
读者反馈
我们的读者反馈总是受欢迎。告诉我们你对这本书的看法——你喜欢或不喜欢什么。读者反馈对我们来说很重要,因为它帮助我们开发出你真正能从中获得最大收益的标题。
要发送给我们一般反馈,只需发送电子邮件至<feedback@packtpub.com>,并在邮件主题中提及本书的标题。
如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在,您是 Packt 图书的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大收益。
下载示例代码
您可以从www.packtpub.com下载您购买的所有 Packt 出版物的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
下载本书的彩色图像
我们还为您提供了一个包含本书中使用的截图/图表彩色图像的 PDF 文件。彩色图像将帮助您更好地理解输出的变化。您可以从以下链接下载此文件:www.packtpub.com/sites/default/files/downloads/1829OT_GraphicsBundle.pdf。
错误清单
尽管我们已经尽一切努力确保我们内容的准确性,但错误仍然会发生。如果您在我们的某本书中找到错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何错误清单,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击错误提交表单链接,并输入您的错误详细信息来报告它们。一旦您的错误清单得到验证,您的提交将被接受,错误清单将被上传到我们的网站或添加到该标题的错误清单部分。
查看先前提交的错误清单,请访问www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将出现在错误清单部分。
盗版
互联网上版权材料的盗版是一个持续存在的问题,涉及所有媒体。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现任何形式的我们作品的非法副本,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过<copyright@packtpub.com>与我们联系,并提供疑似盗版材料的链接。
我们感谢您在保护我们的作者和我们为您提供有价值内容的能力方面的帮助。
问题
如果您在这本书的任何方面遇到问题,您可以通过<questions@packtpub.com>联系我们,我们将尽力解决问题。
第一章. 开始 iOS 开发
在其他 IT 领域,移动行业是增长最快的领域。iOS 在移动行业中扮演着关键角色;如今,甚至主要的 IT 玩家也在企业方式中融入了移动性。这个标题将帮助你从企业的角度理解 iOS 开发及其实现的基础。
要开始 iOS 开发,我们需要以下这些:
-
一个 Mac 系统
-
Xcode
-
iOS SDK
基本上,iOS 开发基于 Objective-C 语言。Objective-C 是 C 编程语言的扩展;这包括OOP(面向对象编程)的概念,并为 C 添加了 Smalltalk 风格的通信。Xcode 是为了帮助你为 iPad、iPhone 和 Mac 构建出色的应用而构建的。Xcode 是 iOS 的IDE(集成开发环境)。
你可以从苹果商店下载 Xcode,如下截图所示:

Xcode 选项
它是免费的;下载 Xcode 后,它将自动出现在启动板上。Xcode 提供了不同的功能,如编码、设计、用户界面和测试。
在本章中,我们将涵盖以下主题:
-
接口和实现类
-
iOS 中的方法类型
-
数据类型
-
数组
-
属性和合成
-
代理
-
构建一个简单的应用
接口和实现
在 Objective-C 中,表示单个类的文件有两种类型:一个是interface文件,另一个是implementation文件。在interface文件中,方法的声明和变量的声明是完成的;在implementation文件中,我们定义这个方法并使用声明的变量。
接口和实现的代码片段
以下是为interface类提供的代码片段:
@interface MyClass:NSObject{
// declare class variables here
}
// class methods and instance methods are declared here
@end
MyClass.h文件是一个interface类。NSObject是所有类的根类,导入NSObject类是必须的。@end关键字表示我们的接口块已完成。
以下是我们名为MyClass.m的implementation类:
@implementation MyClass
// class methods are defined here
@end
iOS 中的方法类型
我们在interface文件中声明的方法在implementation文件中定义。方法的声明方式如下:
-(returnType)methodName:(typeName) variable1 :(typeName)variable2;
iOS 中有两种方法:一种是类方法,另一种是实例方法。在类方法中,我们不需要创建对象;我们可以直接使用它们的类名来访问这些方法。类方法由+符号表示。我们可以在 Java 和 C++等语言中找到静态方法,我们将在 Objective-C 和 Ruby 等语言中找到类方法。静态方法和类方法之间的区别在于它们有不同的语言概念。
主要区别如下:
-
静态方法在所有实例之间共享(在 Objective-C 中不存在这种情况)。
-
类方法是在类上的一个方法。在 Objective-C 和 Ruby 等语言中,类本身是另一个类的实例(元类)。在方法声明前使用加号(+)表示该方法将在类上定义。技术上,它只是不同对象上的一个实例方法。类方法的语法如下:
+(void)classMethod;在实例方法中,我们必须创建一个对象。如果没有创建对象,我们无法访问实例方法。为对象分配内存。
alloc关键字用于分配内存。实例方法的语法如下:-(void)InstanceMethod;
创建对象
在 implementation 文件中创建对象如下:
MyClass *object= [[MyClass alloc]init] ;
[object InstanceMethod];
在这里,我们使用了 alloc 和 init 关键字。alloc 关键字用于为对象分配内存,而 init 关键字用于初始化该对象。
重要数据类型
与任何其他编程语言一样,iOS 也有不同的数据类型,如 int、float、double、char 和 id。数据类型用于指定变量中存储的数据类型。iOS 中有四种重要的数据类型:
-
NSString: 这用于表示字符串 -
NSInteger: 这用于声明整数 -
CGFloat: 这用于声明浮点值 -
BOOL: 这用于声明布尔值(是或否)
数组
数组是具有连续内存分配的同质数据类型的集合。在 iOS 中,数组有两种类型:
-
NSArray -
NSMutableArray
NSArray
NSArray 是一个不可变数组。在 Objective-C 中,默认情况下,数组是不可变的,也就是说,正如其名称所示,它初始化后的对象不能被更改或删除:
NSArray *xyz = [[NSArray alloc] init];
Xyz = @[@"Harry", @"Tom", @"jack"];
NSMutableArray
NSMutableArray 是 NSArray 的子类。这是一个可修改的数组,其对象在数组初始化后可以被删除或修改:
NSMutableArray *xyz = [[NSMutableArray alloc] init];
Xyz = @[@"Harry", @"Tom", @"jack"];
注意
字符串的概念与 C 语言中相同。字符串也有两种类型:NSMutableString 和 NSString。以下是一个 NSString 类型的示例:
NSString *myName = @"Jack";
属性和合成
对象的属性被定义为让其他对象使用或改变它们的状态。然而,在面向对象编程中,无法直接从类外部访问对象的内部状态(除了公共访问器)。相反,使用访问器方法(获取器和设置器)与对象交互。@property 的目标是通过自动生成这些访问器方法来简化属性创建和配置:
-
@property: 此方法自动在我们的代码中实现设置器/获取器方法;我们不必手动编写代码。 -
@synthesize: 此方法使用给定的属性合成属性,编译器将为我们的变量生成设置器和获取器方法。然而,现在我们不使用synthesize;代替@synthesize,我们使用下划线(_)或self关键字。
让我们通过一个代码片段来理解这些方法。这是接口文件:
#import <Foundation/Foundation.h>
@interface MyClass : NSObject
@property void methodname;
@end
这是实现文件:
#import "MyClass.h"
@implementation Class
@synthesize methodname = _methodname;
@end
在 Objective-C 中,每个对象都持有引用计数。当创建一个对象时,其引用计数增加一;当释放对象时,引用计数减少一。当引用计数达到零时,它会自动释放内存。@property 的属性如下:
-
atomic:默认情况下,每个属性都是原子的。它将确保获取器方法总是返回整个值,或设置器方法总是设置整个值。一次只有一个线程可以访问一个变量以获取或设置值。因此,原子也是线程安全的。 -
nonatomic:在nonatomic中,无法保证从变量返回的值与设置器方法设置的值相同。同时,多个单线程可以同时访问一个变量。 -
strong:strong属性拥有对象。编译器将确保我们分配给此属性的任何对象只要我们(或任何其他对象)用强引用指向它,就不会被销毁。 -
weak:在weak引用中,我们不想控制对象的生存周期。我们弱引用的对象之所以存在,仅仅是因为至少有一个其他对象持有对该对象的强引用。 -
retain:这指定了在赋值时应在对象上调用retain。它获取对象的拥有权。 -
assign:这指定了设置器使用简单的赋值。它使用标量类型的属性,例如float或int。 -
copy:在赋值时复制一个对象,并增加引用计数一。
考虑以下使用属性的简单示例:
@property (nonatomic, assign) float radius;
@property (atomic, strong) NSString *name;
代理
代理是一种工具,通过它可以一个对象与另一个对象进行通信;反过来,对象可以保持相互连接。这是一种方法,其中一个对象可以代表另一个对象行事。委托对象保留对另一个对象的引用,并在适当的时候向它发送消息。消息通知委托对象,委托对象即将处理或将要处理的事件。
考虑以下如何使用代理的示例。
让我们定义 FirstViewController.h 接口文件如下:
#import "SecondViewController.h"
@interface FirstViewController : UIViewController <SecondViewControllerDelegate>
{
IBOutlet UITextField *userNameTextField;
}
@property (nonatomic, strong) UITextField *userNameTextField;
-(IBAction)goNext:(id)sender;
@end
现在,让我们定义 FirstViewController.m 实现文件:
#import "FirstViewController.h"
@interface FirstViewController ()
@end
@implementation FirstViewController
@synthesize userNameTextField;
-(void)goNext:(id)sender{
SecondViewController *secondVC = [[SecondViewController alloc]init];
secondVC.delegate = self;
[self.navigationController pushViewController:secondVC animated:YES];
}
-(void)done:(NSString*)name{
NSLog(@"BACK in firstVC");
userNameTextField.text = name;
}
@end
接下来,我们将定义 SecondViewController.h 接口文件如下:
#import "FirstViewController.h"
@protocol SecondViewControllerDelegate <NSObject>
-(void)done:(NSString*)someText;
@end
@interface SecondViewController : UIViewController{
IBOutlet UITextField *someText;
IBOutlet UIButton *returnButton;
id delegate;
}
@property (assign, nonatomic) id <SecondViewControllerDelegate> delegate;
@property (strong, nonatomic) UITextField *someText;
-(IBAction)goBack:(id)sender;
@end
现在,我们将定义 SecondViewController.m 实现文件如下:
#import "SecondViewController.h"
@interface SecondViewController ()
@end
@implementation SecondViewController
@synthesize someText;
@synthesize delegate = _delegate;
-(void)goBack:(id)sender{
[self.delegate done:someText.text];
FirstViewController *firstVC = [[FirstViewController alloc]init];
[self.navigationController pushViewController:firstVC animated:YES];
}
@end
构建我们的第一个 iPhone 应用
让我们通过以下步骤制作我们的第一个 iPhone 应用:
-
打开 Xcode;你将看到以下屏幕。在右侧的面板中,你可以看到你的现有项目。你可以直接从项目列表中选择打开项目;另一方面,对于新项目,选择创建一个新的 Xcode 项目。
![构建我们的第一个 iPhone 应用]()
-
Xcode 提供了八个模板。在左侧面板中,您可以看到有两个选项:iOS 和 OS X。iOS 用于苹果触摸设备,而 OS X 用于桌面设备。最初,选择单视图应用程序。然后,点击下一步,如图所示:
![构建我们的第一个 iPhone 应用]()
-
现在,是时候给你的项目命名,并从设备下拉菜单中选择iPhone。然后,点击下一步。以下截图将出现:
小贴士
下载示例代码
您可以从您在
www.packtpub.com购买的所有 Packt 书籍的账户中下载示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。![构建我们的第一个 iPhone 应用]()
-
将您的项目保存在您选择的目录中:
![构建我们的第一个 iPhone 应用]()
-
现在,您的编辑器将看起来如下截图所示。在左侧面板中,有一个类的声明。从左侧面板中选择故事板。故事板为您的应用程序提供视图。
![构建我们的第一个 iPhone 应用]()
这是您的故事板。目前,它是一个空视图控制器。故事板有许多区域,例如导航区域、编辑区域、工具区域和调试区域,以下将逐一描述:
-
导航区域:在这个面板中,我们可以通过导航选择栏在多个导航器之间切换。我们将经常使用的三个导航器是项目导航器、搜索导航器和问题导航器。
-
编辑区域:编辑区域是我们可能花费大部分时间的地方!所有编码都发生在这里。
-
工具区域:Xcode 的工具区域由两个面板组成:检查器面板和库面板。检查器面板将给我们提供关于文件详情。然而,当我们查看故事板时,检查器面板将显示您可以修改的所选元素的各个属性。库面板在我们查看故事板之前不会非常有用。当我们使用 Xcode 的界面构建部分时,我们可以从库面板拖动 UI 元素到编辑区域,以将它们添加到用户界面中。
-
调试区域:调试区域将在您运行应用程序时显示控制台输出和各种变量的状态。
在以下截图,我们可以看到一个箭头在视图之前;这表示该视图是应用程序的起始视图。当应用程序启动时,此视图将首先启动:
![构建我们的第一个 iPhone 应用]()
-
-
在右侧,有许多组件,如按钮、标签和文本字段(我们将在接下来的章节中学习这些组件)。按照以下截图所示,从右侧面板拖放一个按钮;通过双击重命名它;给它任何名字。例如,命名为
Hello。![构建我们的第一个 iPhone 应用]()
这就是我们的 UI 部分。现在,让我们继续到编码部分。
-
前往
ViewController.h并在其中编写以下方法。.h文件是我们项目的接口文件,其中我们声明属性和方法。如果我们想声明变量,那么它们将在接口的大括号内声明;属性和方法定义在大括号外:@interface ViewController : UIViewController { Int x; } @property (nonatomic, strong) NSString*recipeName;注意
回到我们的程序,我们必须描述一个方法,
showMessage,其中我们描述了UIAlertview函数;这会显示一个带有消息的弹出窗口。以下截图显示了包含前面代码片段的编辑区域:
![构建我们的第一个 iPhone 应用]()
让我们逐行理解代码:
-
#import <UIKit/UIKit.h>: 这是我们代码中导入的一个头文件。UIKit是一个框架,包含所有 UI 部分的内置库文件。UIKit头文件导入了UIKit框架中可用的所有其他头文件;导入此头文件后,我们不需要手动导入其他UIHeader文件,如UIViewController.h、UIView.h或UIButton.h。 -
@interface ViewController: UIViewController: 这是对ViewController.h类的一个接口。它继承自UIViewController类,用于处理我们屏幕或视图的流程。 -
(IBAction)showMessage: 这是一个我们手动创建的方法。当我们想要在按钮点击时执行某些操作时,我们将使用IBAction。这是 iOS 中的一种返回类型。这里,方法的名称是showMessage(我们可以给出任何名字)。IBAction告诉 UI 构建器该方法可以用作选择器(事件接收器)。 -
@end: 这段代码表示我们的接口部分已经结束。
-
-
现在,前往
ViewController.m并描述在.h文件中定义的方法。这也被称为implementation文件。以下截图展示了实现文件中使用的代码:![构建我们的第一个 iPhone 应用]()
再次逐行理解代码:
-
#import "ViewController.h": 这导入了我们的.h类(接口类)。 -
@implementation ViewController: 从这个片段开始,我们的方法实现。在接口文件中声明的我们将在这里实现。它还包含一些内置方法,如–(void)viewDidLoad、-(void)didRecieveMemoryWarning等。我们可以在这些方法中按需编写代码。如果我们选择 Empty application 模板,则不会提供这些内置方法。 -
UIAlertView: 弹出视图是出现在当前视图之上的弹出窗口或消息。我们可以通过创建它的对象来使用它。在这里,Alert是一个对象。alloc是一个关键字,用于为对象分配内存。 -
[Alert show]: 这个片段用于在屏幕上显示弹出窗口。
现在我们必须将这个按钮连接到我们声明的那个方法。如果不连接按钮,它将不会工作。使用以下步骤来连接按钮。
-
-
右键点击按钮。会出现一个黑色弹出窗口。选择Touch Up Inside并将其连接到View Controller,如图所示。释放鼠标后,从弹出窗口中选择showMessage。
![构建我们的第一个 iPhone 应用]()
-
现在,你的按钮已经连接到了
Alert方法。现在是执行你的项目的时候了。运行你的项目并点击Hello按钮。你的输出将类似于以下截图:![构建我们的第一个 iPhone 应用]()
摘要
在本章中,我们学习了 iOS 的基础知识,例如方法、数组、属性、代理等。在本章之后,我们也将能够使用 Xcode,我们可以使用 UI 组件制作简单的应用。在下一章中,我们将学习更多关于组件的内容。
第二章. 探索更多 UI 组件
UI 元素是我们可以在应用程序中看到的视觉部分。这些元素响应用户交互,如按钮、文本字段和其他标签。一些 UI 元素用于构建我们的图形应用程序;例如,图像、选择器、地图工具包等。Xcode 帮助你使用界面构建器构建许多界面。UIKit 框架提供了构建和管理应用程序组件或用户界面所需的类。UIKit 框架负责处理 UIComponents,管理视图和窗口,以及创建组件和代码之间的连接。UI 元素可以使用两种方式使用:
-
通过从界面构建器拖放它们
-
通过编程将组件添加到视图中
在本章中,我们将涵盖以下主题:
-
通过编程添加 UI 组件
-
一些特色 UI 组件
-
理解表格视图的解剖结构
-
滚动视图及其用法
-
导航控制器
通过编程添加 UI 组件
如介绍中所述,我们可以通过编码添加组件。让我们从一些简单的例子开始。
添加视图
在 Xcode 中,在左侧面板中,你可以找到如以下截图中的1点所示的 AppDelegate.m 文件。导航到该文件。文件中有很多代码,但我们将只关注一个函数,即以下截图中的2点所示的 application:didFinishLaunchingWithOptions:

只需在函数(didFinishLaunching)中添加以下代码,使其看起来类似于以下内容:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIView *view = [[UIView alloc] initWithFrame:self.window.bounds];
[view setBackgroundColor:[UIColor blueColor]];
[self.window addSubview:view];
[self.window makeKeyAndVisible];
return YES;
}
构建并运行程序;如果在模拟器上看到蓝色屏幕,这意味着我们已经完成了。以同样的方式,我们可以为所有 UI 组件编写代码。你可以在本章中找到更多示例,以更快地学习。
添加一个标签
要添加子视图,你必须在 AppDelegate.m 类中编写代码,就像上一个例子中做的那样。以下是应在 [self.window addSubview: view]; 行之后添加到同一函数中的代码:
CGRect labelFrame = CGRectMake( 10, 40, 100, 30 );
UILabel* label = [[UILabel alloc] initWithFrame: labelFrame];
[label setText: @"HELLO"];
[label setTextColor: [UIColor orangeColor]];
[view addSubview: label];
在之前的代码中,我们创建了一个 UILabel 类的对象。分配内存并初始化对象。然后,我们可以使用内置方法设置文本和文本颜色。最后,将标签添加到视图中。以下截图显示了屏幕上的标签名为 HELLO:

创建一个新的按钮
要添加一个新按钮,在 [view addSubview:label] 行之后编写以下代码,并运行它:
CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
[button setTitle: @"My Button" forState: UIControlStateNormal];
[button setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
[button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview: button];
此代码将创建一个按钮,点击按钮时将调用 buttonAction 方法。在这里,UIButton 类的对象是 button。将 button 的目标设置为 self;@selector 调用我们的 buttonAction 方法并将事件设置为 TouchUpInside。buttonAction 函数应类似于以下内容:
- (void)buttonAction:(id)sender
{
NSLog(@"button clicked");
}
当您运行代码时,您将在标签下方看到按钮。您可以通过更改代码中的坐标来更改位置。现在,您可以逐个尝试每个组件。在附加所有之前的代码后,文件应该看起来像这样:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIView *view = [[UIView alloc] initWithFrame:self.window.bounds];
[view setBackgroundColor:[UIColor blueColor]];
[self.window addSubview:view];
CGRect labelFrame = CGRectMake( 10, 40, 100, 30 );
UILabel* label = [[UILabel alloc] initWithFrame: labelFrame];
[label setText: @"HELLO"];
[label setTextColor: [UIColor orangeColor]];
[view addSubview: label];
CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
[button setTitle: @"My Button" forState: UIControlStateNormal];
[button setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
[button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview: button];
[self.window makeKeyAndVisible];
return YES;
}
一些特色 UI 组件
iOS 中有一些新组件,以及一些现有组件的变化。让我们在本节中讨论这些组件。
地图视图
地图视图是处理位置和地图的一个非常好的方法。它在组件列表中可用。我们可以在我们的故事板中直接拖放地图视图(如下面的截图所示),或者我们可以通过编码来创建它:

为了使用地图视图,您需要将 MapKit 框架添加到您的项目中。您可以通过以下步骤添加框架:
-
点击左侧面板中的项目名称,您可以看到如下所示的屏幕截图:
![地图视图]()
-
在前面的屏幕截图中,您可以看到 Linked Frameworks and Libraries 选项。框架默认包含在我们的项目中。如果您想添加一个新的框架,请点击 +(加号)符号,找到
MapKit框架,然后点击 Add。 -
现在,如果您想显示用户的位置,请转到右侧面板中的检查器工具栏并选择第四个选项卡,即 Attributes 检查器。点击 Shows User Location 复选框,如下面的截图所示:
![地图视图]()
UIPickerView
UIPickerView 是一个 UI 元素,可以用来从多个选项中进行选择(类似于下拉菜单在网页上所做的工作)。我们可以在组件面板中找到 UIPickerView 元素,并通过拖放的方式简单地使用它,如下面的截图所示。不需要为 UIPickerView 添加框架。

将 Picker 视图拖放到故事板中并运行程序。您可以在模拟器中看到 Picker 视图。但是,为了更好地使用 Picker 视图,或者在我们的 Picker 视图中插入数据,我们需要以下方式编码:
-
首先,将
UIPickerVIew元素连接到.h文件。 -
在连接时,给 Picker 视图起一个名字(例如:
Picker)。 -
连接后,在
.h文件中创建一个属性,如下所示:@property (weak , nonatomic) IBOutlet UIPickerView *PickerPicker是我们给 Picker 视图起的名字。我们设置属性如下:-
weak:这会在使用后自动释放对象。 -
nonatomic:这是线程安全的。一次只有一个线程可以使用该对象。
-
-
为了使用 Picker 视图,我们需要在
.h文件中添加两个协议;它们是UIPickerViewDataSource和UIPickerViewDelegate,如下面的代码片段所示:#import <UIKit/UIKit.h> @interface ViewController : UIViewController<UIPickerViewDataSource, UIPickerViewDelegate> @property (weak, nonatomic) IBOutlet UIPickerView *picker; @end -
现在,转到
.m文件。有一个名为viewDidLoad的函数。向其中添加以下代码:- (void)viewDidLoad { [super viewDidLoad]; // Initialize Data NSArray *pickerData; pickerData = @[@"Item 1", @"Item 2", @"Item 3", @"Item 4",@"Item5"]; // Connect data self.picker.dataSource = self; self.picker.delegate = self; } -
为了正确使用
UIPickerView,我们需要在.m文件中添加 Picker 视图方法,如下所示:- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } // The number of columns of data - (int)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 1; } // The number of rows of data - (int)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { return pickerData.count; } // The data to return for the row and component (column) that's being passed in - (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { return pickerData[row]; } -
现在,编译并运行代码。我们的模拟器将如下截图所示:
![UIPickerView]()
网页视图
我们需要网页视图来与网页内容交互。网页视图在我们的设备上显示网站。Safari 和 Chrome 是网页视图的例子,但它包含许多 UI 元素,如标签、刷新按钮、下一页按钮和后退按钮。然而,我们不会深入探讨。首先,我们将只在我们的设备上显示网站。与所有组件一样,网页视图也在左侧组件面板中可用。
让我们从以下步骤开始:
-
将网页视图拖放到故事板中。
-
将网页视图连接到
.h文件,并为网页视图命名(例如,viewSite)。连接后,会自动创建一个属性,如下所示:@property (weak, nonatomic) IBOutlet UIWebView *viewSite; -
现在,在
.m文件中,转到viewDidLoad函数并添加以下代码:- (void)viewDidLoad { [super viewDidLoad]; NSString *String = @"http://www.facebook.com"; NSURL *url = [NSURL URLWithString:String]; //passing string in url NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; [self.viewSite loadRequest:requestObj]; } -
简单地编译并运行代码。你将在模拟器上看到 Facebook 登录页面,如下截图所示:
![网页视图]()
图像视图
图像视图用于在屏幕上显示图像。为了使用图像视图,在界面构建器中有一个名为UIImageView的组件可用。我们可以使用图像视图创建许多动画,并且根据需要可以同时使用多个图像视图。不需要为图像视图添加任何框架。图像视图接受多种格式的图像,如 PNG、JPEG、BMP 等。我们可以以编程方式或通过拖放两种方式添加图像。
使用图像视图
让我们看看以下步骤,了解如何使用图像视图:
-
在界面构建器中找到图像视图,并将其拖放到故事板中。在插入图像视图后,我们的视图看起来如下:
![使用图像视图]()
-
通过简单地从另一个文件拖放到 Xcode 右侧面板下所有类下面的模拟器中,将图像添加到程序中。会出现一个弹出窗口,你需要勾选目标复选框。
-
现在,转到检查器工具栏中的属性检查器,它在 Xcode 的左侧面板中可用。给图像命名,使其与我们使用的图像名称相同(例如,
image.png),如下截图所示:![使用图像视图]()
-
编译并运行程序。你的图像将在模拟器上显示,如下截图所示:
![使用图像视图]()
理解表格视图的结构
表格视图是 iOS 中的一个常见 UI 元素。它用于以表格形式显示数据列表。我们每天都在许多形式中使用它,如歌曲播放列表、电话中的联系人等。它用于显示一个垂直可滚动的视图,该视图由多个单元格组成。它具有特殊功能,如标题、页脚、行和部分。还有一个用于重用单元格的功能。因此,我们可以根据需要创建无限数量的单元格。
一个空的表格视图看起来如下截图所示:

表格视图的重要方法
有许多可用于表格视图的方法。它们在代码片段中描述如下:
-(UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPathswithRowAnimation:(UITableViewRowAnimation)animation
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifierforIndexPath:(NSIndexPath *)indexPath
- (void)reloadData
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation
- (NSArray *)visibleCells
与表格视图一起工作
表格视图是一个在界面构建器中可用的常见元素。以下步骤将帮助我们了解如何使用表格视图:
-
从界面构建器中选择表格视图组件,将其拖放到我们的视图中,并在视图控制器中正确调整表格视图。
-
插入表格视图后,我们的故事板看起来如下截图所示:
![与表格视图一起工作]()
-
为了使用表格视图,我们需要在我们的
.h文件(我们的接口文件)中添加两个协议,UITableViewDelegate和UITableViewDataSource:@interface TableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> -
现在,在
.m文件中,我们需要声明一个数组。在这里,数组用于按单元格存储数据。在声明数组后,将数据插入到在viewDidLoad方法中定义的数组中,如下所示:@implementation TableViewControler { NSArray *Recipelist; } -(void)viewDidLoad { [super viewDidLoad]; // Initialize table data Recipelist = @[@"Crispy Egg",@"Braised Pork", @"Domestic Cheese", @"Roasted baby beets"];} -
现在,我们必须在我们的
.m文件中的代码中添加两个表格视图方法,numberOfRowsInSection和cellForRowAtIndexPath。第一个方法用于通知表格视图该部分中存在的行数。第二个方法简单地返回tableData数组中的项目数。 -
添加代码,如以下所示:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [Recipelist count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //create identifier for cell NSString *TableIdentifier = @"TableCell"; // dequeReusableCell is method to reuse the cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableIdentifier]; } //set the text for each cell as per our array objects cell.textLabel.text = [Recipelist objectAtIndex:indexPath.row]; return cell; } -
现在,转到故事板。我们需要建立数据源和代理之间的连接。
-
从我们的视图中选择表格视图。右键单击它,连接到下面的黄色按钮,这是我们表格视图控制器。
-
选择两个数据源,并按以下截图所示逐个委托它们:
![与表格视图一起工作]()
-
最后,我们可以编译并运行程序。我们的模拟器看起来如下:
![与表格视图一起工作]()
滚动视图及其用法
滚动视图用于显示比屏幕尺寸更大的内容。它可以包含所有其他 UI 元素,如图像视图、标签、文本视图,甚至另一个滚动视图。UIScrollView是 iOS 中最有用的控件之一。它是一种表示比屏幕更大的数据的好方法;数据可以是图像、列表、论坛等。滚动视图元素存在于界面构建器中,我们可以通过将其拖动到故事板中来直接使用它。让我们通过以下简单活动来了解滚动视图:
-
创建一个单视图应用程序,并从左侧面板选择故事板。
-
移动到故事板,并从视图控制器中选择视图。
-
现在,前往检查器工具栏,然后转到视图坐标;将视图的高度更改为1,000,如图所示。因此,您的视图将超过屏幕。
![滚动视图及其用法]()
-
在界面构建器中从右侧面板搜索滚动视图,并将其拖动到视图中,如图所示。不要忘记正确调整视图上的滚动视图:
![滚动视图及其用法]()
-
我们需要添加一些元素来检查滚动视图是否工作。因此,添加两个标签,一个位于视图顶部,另一个位于视图底部,并给它们命名。
-
编译并运行程序。
您将只看到一个顶部标签;现在,向下滚动并查看底部标签。因此,通过滚动视图,我们可以滚动到任何元素,如图像、表格等。
导航控制器
导航控制器是 iOS 应用程序中常见的另一个 UI 元素。许多应用程序,如照片应用、YouTube 和联系人,都是由导航控制器构建的。导航控制器管理多个子视图,包括顶部的导航栏和返回按钮。我们可以根据应用程序需求显示或隐藏导航栏。当我们在单个应用程序中使用多个视图时,会使用它。通过导航控制器,我们可以从一个视图移动到另一个视图。导航控制器也是一种元素,并在界面构建器中可用。导航控制器通过提供返回按钮来提供返回前一个视图的功能。我们可以在故事板中添加多个视图,并通过使用切换将它们连接起来。切换是导航控制器提供的一种特殊功能,它将导航控制器推送到下一个视图。
让我们通过一个简单的应用程序来创建它,这样我们可以轻松地理解导航控制器:
-
创建一个单视图应用程序,并移动到故事板。
-
从左侧面板删除现有的视图。
-
在界面构建器中搜索导航控制器,并将其拖动到故事板。现在,您的屏幕看起来像这样:
![导航控制器]()
-
视图之间的链接符号是切换符号。从界面构建器中选择一个按钮,并将其拖动到根视图控制器。
-
从组件中找到一个视图。将其拖动到根视图控制器附近,并将图像视图插入到新插入的视图中。
-
从您的目录中复制任何图像到 Xcode 左侧面板的所有类下面。
-
前往检查器工具栏,并将图像命名为与我们选择的图像相同的名称。
![导航控制器]()
-
最后,将按钮连接到新视图。右键单击按钮,将蓝色线条拖动到新视图,并在弹出窗口中选择Push。
-
是时候编译并运行程序了。当你看到模拟器时,屏幕顶部将有一个按钮和导航栏。当你点击按钮时,将出现一个新屏幕,显示你的图片,并且还有一个返回按钮可供使用,以获取前一个视图。以下截图显示了第一种视图:
![导航控制器]()
-
这里是第二种视图:
![导航控制器]()
摘要
在本章中,我们学习了组件及其如何通过编程创建。我们掌握了表格视图的概念及其使用方法。此外,我们还使用导航控制器制作了一个简单的应用程序。现在,我们将讨论下一章中的框架。
第三章:探索各种框架
框架是一组资源的集合。它将静态库及其头文件收集到一个单一的结构中,Xcode 可以轻松将其包含到我们的项目中。框架是动态库。在 iOS 中,有两种类型的框架:公开框架和私有框架。从 iOS 3.1 开始,所有私有和公开库都合并到一个大缓存文件中。苹果为不同的功能提供了许多框架。框架的扩展名为.framework。对于每个独特的功能,都定义了一个框架——例如,使用地图时,有MapKit框架可用,使用位置时,有CoreLocation框架可用。各种框架位于<Xcode.app>Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/<iOS_SDK>/System/Library/Frameworks目录中,其中Xcode.app是我们的应用程序路径(我们保存应用程序的地方)。
在本章中,我们将涵盖以下主题:
-
表格形式的框架描述
-
iOS 中的数据库
-
社交集成
-
以更好的方式理解每个主题的活动
通常,框架是包含可链接库(.dylib)及其相关资源和头文件的包。每个框架都包含与其相关的示例代码和其他资源。苹果提供了许多框架。框架在以下表格中列出,并附有描述:
| 名称 | 描述 |
|---|---|
Accelerate.framework |
此框架处理数学、数字信号处理、大数和图像处理。 |
Accounts.framework |
此框架提供对Accounts数据库中账户的访问。如果不存在账户,它允许创建账户。 |
AddressBook.framework |
此框架提供对AddressBook数据库的访问。 |
AddressBookUI.framework |
此框架包含用于显示系统定义的人选选择器和编辑接口的类。 |
AdSupport.framework |
此框架提供访问用于广告的标识符以及一个表示是否启用了有限跟踪的标志。 |
AssetsLibrary.framework |
此框架提供对用户照片和视频的访问。 |
AudioToolbox.framework |
此框架提供用于录制、播放和音频流的接口。 |
AudioUnit.framework |
此框架用于加载音频单元及其用途。 |
AVFoundation.framework |
此框架用于播放和录制音频和视频。 |
CFNetwork.framework |
此框架用于通过 Wi-Fi 或蜂窝网络访问网络。 |
CoreAudio.framework |
此框架提供用于 Core Audio 的数据类型。 |
CoreBluetooth.framework |
此框架提供对蓝牙(硬件)的访问。 |
CoreData.framework |
此框架包含应用程序数据模型的接口。 |
CoreFoundation.framework |
此框架提供软件服务和数据的基本管理。 |
CoreGraphics.framework |
此框架包含 Quartz 引擎的 API,并提供 2D 视图。 |
CoreImage.framework |
此框架是用于操作图像和视频的接口。 |
CoreLocation.framework |
此框架是用于确定用户位置的接口。 |
CoreMedia.framework |
此框架包含操作音频和视频的底层例程。 |
CoreMIDI.framework |
此框架包含处理 MIDI 数据的底层例程。 |
CoreMotion.framework |
此框架是访问加速度计和陀螺仪数据的接口。 |
CoreTelephony.framework |
此框架允许访问运营商信息和当前通话的相关信息。 |
CoreText.framework |
此框架包含文本布局和渲染引擎。 |
CoreVideo.framework |
此框架包含使用音频和视频的底层例程。建议不要直接使用它。 |
EventKit.framework |
此框架接口用于访问日历和事件。 |
EventKitUI.framework |
此框架包含用于显示日历界面的类。 |
ExternalAccessory.framework |
此框架是一个用于与附加硬件通信的接口。 |
Foundation.framework |
此框架包含管理字符串、数组、集合和底层数据类型的接口。 |
GameController.framework |
此框架是用于与游戏相关硬件通信的接口。 |
GameKit.framework |
此框架用于点对点连接和创建社交游戏。 |
GLKit.framework |
此框架用于构建 OpenGL ES 应用程序。 |
GSS.framework |
此框架提供安全相关服务。 |
iAd.framework |
此框架用于显示广告。 |
ImageIO.framework |
此框架包含读取和写入图像数据的类。 |
IOKit.framework |
此框架是一个底层框架,用于与内核和硬件通信。 |
JavaScriptCore |
此框架包含评估 JavaScript 代码和解析 JSON 的文件。 |
MapKit.framework |
此框架用于在我们的应用程序中嵌入地图和使用反向地理编码。 |
MediaAccessibility.framework |
此框架在 iOS 7 中定义。它管理媒体文件中闭路电视内容的呈现。 |
MediaPlayer.framework |
此框架包含全屏模式播放视频的接口。 |
MediaToolbox.framework |
此框架包含播放音频内容的接口。 |
MessageUI.framework |
该框架包含用于编写电子邮件的接口。 |
MobileCoreServices.framework |
此框架定义系统支持的 UTIs。 |
MultipeerConnectivity.framework |
此框架在 iOS 7 中引入。它负责在设备之间实现点对点网络。 |
NewsstandKit.framework |
此框架提供了在后台下载杂志和报纸的接口。 |
OpenAL.framework |
此框架包含跨平台音频库的接口。 |
OpenGLES.framework |
此框架提供了 OpenGL ES 库的接口,并包含用于 2D 和 3D 图形的类。 |
PassKit.framework |
此框架包含创建数字通行证的接口,用于替换如票、会员卡等物品。 |
QuickLook.framework |
这提供了预览文件的接口。 |
SafariServices.framework |
此框架是在 iOS 7 中引入的。它支持在 Safari 中创建阅读列表项。 |
Security.framework |
此框架提供了管理密钥、信任策略和证书的接口。 |
Social.framework |
此框架包含用于通信或集成社交网络服务的接口。 |
SpriteKit.framework |
此框架是在 iOS 7 中引入的。它便于创建基于精灵的动画。 |
StoreKit.framework |
此框架负责处理与应用程序相关的财务交易。 |
SystemConfiguration.framework |
此框架包含确定网络是否可用的接口。 |
Twitter.framework |
此框架包含发送推文的接口。 |
UIKit.framework |
此框架包含 iOS UI 组件和应用程序用户界面的类。 |
VideoToolbox.framework |
此框架包含设备使用的接口。建议不要直接使用它。 |
这些是苹果提供的各种框架,我们可以根据需要使用它们。
使用库探索各种 UI 组件
您已经在上一章中学习了 UI。现在我们可以通过探索一些 UI 组件和框架来开始。我们将通过以下步骤制作一个简单的“学生注册”应用:
-
首先,在 Xcode 中创建一个新的单视图项目。
-
现在,转到故事板并导航到编辑器 | 嵌入 | 导航控制器,如图下截图所示:
![使用库探索各种 UI 组件]()
-
现在您的故事板看起来像这样:
![使用库探索各种 UI 组件]()
-
通过点击它来选择第二个视图的导航栏,并将其重命名为“学生注册”。
-
向视图中添加一个按钮(命名为“添加学生”),并从右侧的检查器面板设置按钮文本和背景颜色。
-
在界面构建器中搜索新的视图控制器,并将其拖到第二个视图旁边。
-
对于新的视图控制器,我们需要添加新的类文件。转到 Xcode 顶部的文件,然后导航到文件 | 新建 | 文件...。
-
现在,创建一个新的Objective-C 类文件,并为该类命名一个合适的名称(例如,
StudentRegistration),如下面的截图所示:![使用库探索各种 UI 组件]()
-
前往故事板,选择我们的第三个视图,并在检查器元素中为其提供一个与我们的新 Objective-C 类相似的名称。
-
右键单击按钮,选择操作选项,拖动到新视图(第三个视图),并选择推送。
-
现在,如以下截图所示,在第三个视图中拖动四个文本字段。选择一个文本字段,并转到检查器面板,在那里您将找到一个选项占位符。在这里,为所有文本字段输入文本。
-
在界面构建器中找到工具栏按钮,并将其拖动到第三个视图的工具栏上,并命名它(例如,
Save)。
通过遵循前面的步骤,我们的故事板将看起来像这样:

这里是我们的简单 UI,但这次我们需要拉伸我们的 UI 组件。我们可以更改导航栏的颜色以提高可见性。要更改我们的导航栏颜色,我们需要应用以下代码。我们可以在AppDelegate.m中的didFinishLaunchingWithOptions:方法中插入以下代码:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:YES];
// Change navigation bar color
[[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:41.0f/255.0f green:128.0f/255.0f blue:185.0f/255.0f alpha:1.0f]];
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
// Change font of navigation bar
[[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor],NSForegroundColorAttributeName, nil]];
现在,我们需要在StudentRegistration-Info.plist文件中进行一些更改。根据以下截图进行更改:

运行我们的项目后,输出将看起来像这样:

现在,点击+ 添加学生按钮;它将推动我们的应用进入另一个视图,我们的屏幕将看起来像这样:

数据库集成
数据库是将我们的数据保存在计算机内存中的方式。数据库是一组保存在计算机中的数据,可以通过多种方式访问。在 iOS 中,主要只有一种类型的数据库:SQLite。
SQLite
SQLite 是 SQL 的嵌入式实现。SQLite 是一个进程内库,它实现了一个自包含、零配置、无服务器和事务性的 SQL 数据库引擎。SQLite 的源代码存在于公共领域,并且对私人用途和商业用途都是免费的。SQLite 适用于多种编程语言,如 C、C++、Java 等。它也适用于多种操作系统,如 iOS、Android、Symbian、Blackberry 等。SQLite 用于在 iOS 中处理数据文件或创建数据文件。它可以通过使用 SQL 查询轻松执行。SQLite 基于 RDBMS 的原则工作,其中数据存储在表中,关系也存储在表中。
因此,我们可以在项目中使用 SQLite,Apple 提供了一个库和一个框架:ibsqlite3.dylib和sqlite3.h。SQLite 有几个功能。重要的功能如下:
-
sqlite3_open(): 此函数创建并打开一个空数据库。如果数据库已存在,它将仅打开数据库。 -
sqlite3_close(): 此函数用于关闭已打开的 SQLite 数据库连接。它将释放与数据库连接关联的所有资源。 -
sqlite3_prepare_v2(): 此函数用于将 SQL 语句编译成字节码。它基本上将 SQL 语句转换成可执行的代码片段。 -
sqlite3_step(): 此函数将调用之前准备好的 SQL 语句。 -
sqlite3_finalize(): 此函数从内存中删除之前准备的 SQL 语句。 -
sqlite3_exec(): 此函数将sqlite3_prepare_v2()、sqlite3_step()和sqlite3_finalize()的功能合并为单个函数调用。
让我们进行一个小活动,以更好地理解 SQLite。我们将扩展之前的应用程序。打开该应用程序,按照以下步骤进行:
-
前往 项目设置 | 常规。现在,向下滚动并点击 + 添加框架。搜索 SQLite 框架并点击 添加,如图所示:
![SQLite]()
-
在之前步骤中添加的新类中导入以下 SQLite 头文件:
#import <sqlite3.h> -
现在,在界面部分创建 SQLite 对象,如下所示:
sqlite3 *_database; -
现在,将所有文本字段和 保存 按钮链接到新添加的类文件的
.h文件。 -
现在,转到
.m文件中的Save方法,并编写以下代码以实现 SQLite 集成:- (IBAction)Save:(id)sender { NSArray *paths= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *databasePath =[documentsDirectory stringByAppendingPathComponent:@"mydbs.sqlite"]; // Check to see if the database file already exists bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath]; // Open the database and store the handle as a data member if (sqlite3_open([databasePath UTF8String], & _database) == SQLITE_OK) { // Create the database if it doesn't yet exists in the filesystem if (!databaseAlreadyExists) { // Create the SIGNUP table const char *sqlStatement = "CREATE TABLE IF NOT EXISTS REGISTRATION (NAME TEXT, FATHERNAME TEXT, ADDRESS TEXT, CONTACT TEXT)"; char *error; if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK) { NSLog(@"Database and tables created."); } else { NSLog(@"Error: %s", error); } } //-----------SAVING DB----------- // Create insert statement for the person NSString *insertStatement = [NSString stringWithFormat:@"INSERT INTO REGISTRATION (NAME, FATHERNAME, ADDRESS, CONTACT) VALUES (\"%@\", \"%@\", \"%@\", \"%@\")", Name.text,Fathername.text,Address.text,Contact.text]; char *error; if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK) { NSLog(@"Database SAVED."); } else { NSLog(@"Error: %s", error); } sqlite3_close(databaseHandle); } Name.text=@""; Fathername.text=@""; Address.text=@""; Contact.text=@""; } -
编译并运行您的应用程序,在所有文本字段中输入文本,然后点击 保存,如图所示。在控制台中,输出将是 数据库已保存。
![SQLite]()
-
点击 保存 按钮将清除所有文本字段(如图所示)并将我们的输入保存到数据库中:
![SQLite]()
这就是将 SQLite 数据库与我们的应用程序集成的全部内容。
Core Data
它被成千上万的应用程序和数百万的人使用,包括 iOS 和 OS X。Core Data 由苹果公司维护,并且有很好的文档记录。它是一个成熟的框架。Core Data 利用 Objective-C 语言及其运行时,并易于与 Core Foundation 框架集成。结果是,一个易于使用且在内存使用方面非常高效的框架,用于管理易于使用且内存使用效率高的对象图。Core Data 是苹果公司提供的强大框架,用于在应用程序中包含数据。它甚至更受欢迎,因为它不使用进程,并且可以轻松维护数据之间的关系。
苹果定义了一个名为CoreData.framework的框架,用于 Core Data。在我们应用中使用 Core Data 之前,我们需要将此框架添加到我们的项目中。Core Data 本身不是我们应用程序的数据库;它是一个管理对象图的框架。使用 Core Data,我们可以轻松地将我们应用中的对象追踪到数据库中的表记录,而无需执行任何 SQL 查询。在 Core Data 中,我们需要添加三个不同的实例来处理数据库,如下所示:
-
持久存储协调器:正如其名所示,它是一个协调器,在管理对象上下文和存储在我们数据库(SQLite 文件)中的低级文件之间进行协调。我们无法直接使用它;它只会在设置
NSManageObjectContext时使用。为持久存储协调器创建链接会自动为 SQLite 文件创建链接。 -
托管对象上下文:我们可以将其视为一个草稿本(一个小型、快速的临时数据存储内存)。当我们从持久存储中检索对象时,我们会将其临时副本放入草稿本中。然后我们可以随意使用和修改这些对象,而不保存这些数据;持久性保持不变。
-
NSManageObjectModel:一个托管对象模型是
NSManagedObjectModel类的实例。它描述了一个对象(也称为实体)的模式(包含定义)。
让我们通过一个活动来了解 Core Data:
-
要创建示例应用程序项目,请启动 Xcode 并选择创建新项目的选项。在新项目窗口中,选择空应用程序选项。在产品名称字段中输入
CoreData,启用使用 Core Data复选框,然后点击下一步以选择存储项目文件的位置,如图所示:![Core Data]()
-
除了创建新项目时通常存在的文件外,这次还创建了一个名为
CoreData.xcdatamodeld的附加文件。这个文件将用于存储我们数据模型的实体描述。实体描述定义了我们的数据模型,就像模式定义了数据库表的模式一样。要为CoreData应用程序创建实体,选择CoreData.xcdatamodeld文件以加载实体编辑器,如图所示:![Core Data]()
-
要创建一个新的实体,点击位于底部面板中的 添加实体 按钮。在 实体 标题下双击新出现的 实体 项目,并将实体名称更改为
Contacts。实体创建后,下一步是添加一些属性来表示要存储的数据。为此,点击 添加属性 按钮。在 属性 面板中,将属性命名为name并将其类型设置为String。重复这些步骤添加另外两个名为address和phone的 String 属性,如图所示:![Core Data]()
-
现在,我们需要创建自己的故事板文件和视图控制器类。要添加故事板文件,导航到 文件 | 新建 | 文件…,在弹出的对话框中,从左侧面板的 iOS 下方选择 用户界面 类别。在主面板中,选择 故事板 选项,然后点击 下一步,如图所示:
![Core Data]()
-
将故事板命名为
Main(如图所示),选择项目中的新文件位置,然后点击 创建。![Core Data]()
-
接下来,编辑
AppDelegate.m文件并修改didFinishLaunchingWithOptions方法,使其仅返回YES,而不是为应用程序创建窗口(因为我们现在使用的是故事板文件来设计用户界面,这不再需要):- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { return YES; } -
现在,我们需要一个新的 Objective-C 文件。因此,从菜单中添加一个新文件,并将其命名为
CoreDataViewController。 -
在故事板中,从界面构建器拖动
ViewController并设计如图所示的界面:![Core Data]()
不要忘记在 Xcode 右侧的检查器编辑器中为这个视图命名。
-
现在,是时候在故事板和新增的 Objective-C 文件(
CoreDataViewController.h)之间建立连接了。我们还需要将AppDelegate.h类导入到我们的新类中,如下所示:#import<AppDelegate.h>我们的
.h类可能看起来如下所示:![Core Data]()
-
当用户触摸 保存 按钮时,将调用
Save方法。因此,我们必须在这个方法中实现代码以获取托管对象上下文,并创建和存储包含用户输入数据的托管对象。选择CoreDataViewController.m文件,向下滚动到模板Save方法,并实现以下代码:- (IBAction)Save:(id)sender { CoreDataAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context =[appDelegate managedObjectContext]; NSManagedObject *newContact; newContact = [NSEntityDescriptioninsertNewObjectForEntityForName:@"Contacts"inManagedObjectContext:context]; [newContact setValue: _name.text forKey:@"name"]; [newContact setValue: _address.text forKey:@"address"]; [newContact setValue: _phone.text forKey:@"phone"]; _name.text = @""; _address.text = @""; _phone.text = @""; NSError *error; [context save:&error]; _status.text = @"Contact saved"; } -
最后一步是构建和运行应用程序。点击主 Xcode 项目窗口工具栏中的 运行 按钮,并将示例文本输入到文本框中,如图所示:
![Core Data]()
-
点击 保存 按钮后,我们的数据将保存在数据库中,并且标签将变为 联系人已保存,如图所示:
![Core Data]()
我们应用中的社交集成
在应用中集成社交网站现在非常普遍。在 iOS 6 中,引入了一个名为 Social.framework 的新框架。社交框架让我们能够将 Facebook 和 Twitter 等社交网络服务集成到我们的应用中。我们不需要下载任何 SDK 或使用任何 API;社交框架处理一切。对于集成社交网络服务,一个重要的类是 SLComposeViewController。SLComposeViewController 类提供了一个标准的视图,供用户编写推文或 Facebook 帖文。此类还允许用户在不添加任何额外代码的情况下分享位置。
SLComposeViewController 类的工作是在我们的设备上获取 Twitter 或 Facebook 的屏幕,以实现分享。
现在,为了处理 API 和连接,Social 框架中提供了一个新的类:SLRequest。SLRequest 类允许 iOS 应用通过基于 HTTP 的请求直接与社交网络 API 交互。
让我们进行一项简单的活动,以更好地理解社交集成:
-
再次,继续使用之前的项目。打开该项目,并添加一个新 Objective-C 类,就像我们之前做的那样,并给它命名。
-
将新的视图控制器拖动到第三个视图控制器附近或下方,并分别添加名为
Tweet和Facebook的两个按钮。 -
在第二个视图中(在 添加学生 按钮下方)添加一个按钮,并给它命名(例如,
分享到 Facebook 或 Twitter)。 -
现在,在检查元素中将新的视图(第四个视图)的名称与新的类相同。
-
点击 分享到 Facebook 或 Twitter 按钮,选择操作并将其链接到第四个(新)视图。故事板将看起来像这样:
![我们应用中的社交集成]()
-
将两个按钮(Tweet 和 Facebook)链接到新的类。
-
将新的框架
Social.framework添加到我们的代码中。 -
将头文件导入到新的
.h类文件中:#import<Social/Social.h> -
在新的
.m类文件中添加以下代码:- (IBAction)Tweet:(id)sender { SLComposeViewController *tweetSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; [tweetSheet setInitialText:@" Share on Twitter -_- "]; [self presentViewController:tweetSheet animated:YES completion:nil]; } - (IBAction)PostOnFB:(id)sender { SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook]; [controller setInitialText:@"Post to your wall -_- "]; [self presentViewController:controller animated:YES completion:Nil]; } -
编译并运行项目。输出结果将类似于以下截图:
![我们应用中的社交集成]()
-
点击 分享到 Facebook 或 Twitter 按钮。输出结果如下:
![我们应用中的社交集成]()
-
现在,点击 Tweet 按钮。输出结果如下:
![我们应用中的社交集成]()
这种输出出现是因为我们没有在模拟器中管理设置。当你在 设置 中设置你的账户时,它将能够在我们 Twitter 上发布推文;或者当我们实际在设备上运行它时,它将正常工作。
-
现在,点击 Facebook 按钮,输出结果如下:
![我们应用中的社交集成]()
此活动仅用于在我们的帖子中分享一些内容。为了创建登录功能,我们需要 Facebook SDK。使用 Facebook 登录不仅可以将社交特性添加到您的应用中,还可以用作登录系统,而不是创建一个自定义的登录系统。要创建 Facebook 登录,我们需要从developers.facebook.com/docs/ios下载 Facebook SDK。此 SDK 中提供了许多类。大多数任务都由这些类处理。开发者无需担心所有事情。他们只需将登录视图添加到视图控制器中即可。
此活动稍微复杂一些,方法也较长,因此您可以查看以下链接:www.appcoda.com/ios-programming-facebook-login-sdk/,获取 Facebook 登录的适当示例。按照链接中给出的步骤操作,您将得到类似于以下截图的输出:

摘要
在本章中,您学习了更多关于 UI 以及如何在项目中集成框架的知识。您通过探索每个部分,制作了一个简单的应用,学生注册。在章节的结尾,您创建了一个具有 UI 及其功能的完整应用。您还学习了关于数据库和社交媒体集成的知识。
在下一章中,您将学习关于新的框架和 API 以及各种新概念。
第四章:iOS 7 中引入的 API
API 代表 应用程序编程接口。API 是一组命令、函数、代码段和协议,程序员可以在他们的应用程序中使用。它们是程序员可用的预定义函数,而不是从头开始编写。虽然 API 使程序员的任务变得更容易,但 API 是软件到软件的接口,而不是用户界面。使用 API,应用程序可以在没有任何用户干预的情况下相互通信。一些流行的 API 包括 Google Map API、Twitter API、YouTube API 等。
本章将涵盖以下主题:
-
使用 AirDrop 远程发送/接收文件
-
iOS 的第一个原生游戏引擎
-
使用文本工具管理你的排版
-
样本项目
苹果为开发者做了很多重大更新,以便他们将其纳入他们的应用程序中。用户界面已经完全重新设计。iOS 7 引入了一个新的动画系统来创建 2D 和 2.5D 游戏。多任务增强、对等连接以及许多其他重要功能都已添加。
使用 AirDrop
苹果在 iOS 7 中引入了一个名为 AirDrop 的新功能。AirDrop 用于轻松与其他附近的 iOS 设备共享图片、联系人、笔记等。它使用蓝牙来检测附近的设备。当通过蓝牙建立连接时,它将创建一个临时的 Wi-Fi 网络来连接两个设备。有一个名为 UIActivityViewController 的类可用于在我们的应用程序中集成 AirDrop。我们只需要告诉这个类我们想要分享哪些对象,它就会处理其余的事情。UIActivityViewController 类是一个标准的视图控制器类,它提供了一些标准服务,例如将项目复制到剪贴板、在社交媒体网站上共享内容、通过消息发送项目等。在 iOS 7 SDK 中,这个类与内置的 AirDrop 功能一起提供:
UIImage *Image1 = [UIImage imageNamed:@"Image.png"];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:[NSArray arrayWithObjects:@"Share Image", Image1, nil] applicationActivities:nil];
让我们通过一个小活动来了解这个功能:
-
打开 Xcode 并创建一个新的项目。
-
将图像视图和按钮从界面构建器拖放到故事板中。我们的故事板将如下截图所示:
![使用 AirDrop]()
-
将任何图像拖放到 Xcode 中你想要使用的类文件下方。
-
从故事板中选择图像视图,并移动到属性检查器。然后,在 Image 文本框中,给它赋予与我们在 Xcode 中拖拽的图像相同的名称,如下截图所示:
![使用 AirDrop]()
-
现在,将图像视图和按钮链接到
viewcontroller.h并创建一个数组,我们将在此数组中存储从 AirDrop 想要传输的内容。- (IBAction)share:(id)sender; @property (weak, nonatomic) IBOutlet UIImageView *image; @property (nonatomic,strong) NSArray *activityItems;以下截图将展示我们想要传输的内容:
![使用 AirDrop]()
-
移动到
viewController.m并在viewDidLoad方法中添加以下代码:NSString *shareString = @"This is my Development Machine."; UIImage *shareImage = [UIImage imageNamed:@"image.jpg"]; self.activityItems = @[shareString,shareImage];在前面的代码中,我们创建了一个我们想要分享的字符串,并创建了一个
UIImage对象来存储我们的图像。然后,我们将它们两个都添加到了我们定义的数组中。现在,让我们在我们的按钮事件中添加一些代码,如下所示:
UIActivityViewController *avController = [[UIActivityViewController alloc]initWithActivityItems:self.activityItemsapplicationActivities:nil]; NSArray *excludedActivities = @[ UIActivityTypePostToTwitter, UIActivityTypePostToFacebook, UIActivityTypePostToWeibo, UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePrint, UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll, UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr, UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo]; avController.excludedActivityTypes = excludedActivities; [self presentViewController:avController animated:YES completion:nil]; }在前面的代码中,我们使用我们的
activityItems数组创建了UIActivityViewController。通过excludedActivityTypes属性,我们排除了所有不需要的活动,使得 AirDrop 成为唯一的共享选项。最后,我们展示了活动视图控制器。以下截图说明了前面的代码片段:![使用 AirDrop]()
-
现在是编译和运行我们的程序的时候了。执行后,我们的模拟器将看起来像以下截图:
![使用 AirDrop]()
-
点击通过 AirDrop 分享按钮。我们的模拟器现在将看起来像这样:
![使用 AirDrop]()
在这里,没有可用的选项,因为我们排除了除了 AirDrop 选项之外的所有选项,并且模拟器不支持 AirDrop。当我们将此代码放在真实设备上时,它将显示可用于共享的设备和具有 AirDrop 的设备。
SpriteKit
苹果推出了其首个游戏引擎,SpriteKit,它允许我们在不依赖第三方游戏库的情况下为 iOS 创建游戏。它非常强大,并且在用法上倾向于传统的 iOS 框架方法。它也非常容易采用和学习。此外,它支持许多功能,如物理模拟、纹理图集、重力、恢复力和游戏中心支持。此外,它还提供了非常丰富的开发者文档,位于苹果开发者中心。它非常有用且编写得很好。您可能需要首先了解游戏开发的解剖结构,才能开始使用 SpriteKit。因此,这里有两个基本且最重要的术语:一个是场景(Scenes),另一个是精灵(Sprites)。场景可以被认为是游戏中的关卡。因此,在任何游戏中,得分层、HUD(抬头显示)层和游戏玩法层都可以作为不同的场景。然而,场景中的任何对象,如玩家或敌人,都可以被认为是精灵。
iOS 原生游戏框架
苹果推出了其自家的原生 2D 游戏框架,称为 SpriteKit。SpriteKit 是一个出色的 2D 游戏引擎,它提供了对精灵、动画、滤镜和遮罩的支持。最重要的是,它还提供了对物理引擎的支持,为游戏提供真实世界的模拟。
苹果公司提供了一个名为 Adventure Game 的示例游戏,以帮助用户开始使用 SpriteKit。您可以在 developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/CodeExplainedAdventure/AdventureArchitecture/AdventureArchitecture 查看示例 SpriteKit 游戏项目。这个示例项目展示了该框架的能力。然而,该项目理解起来比较复杂;为了学习目的,您只需创建一些简单易懂的内容即可。为了更深入地理解基于 SpriteKit 的游戏,本书中将构建一系列迷你游戏。为了理解 SpriteKit 游戏编程的基础,我们将在本章中构建一个迷你 AntKilling 游戏。
让我们按照以下步骤开始构建 AntKilling 游戏:
-
启动 Xcode。导航到 文件 | 新建 | 项目。然后,在提示窗口中,导航到 iOS | 应用程序 | SpriteKit 游戏 并点击 下一步,如图所示:
![iOS 原生游戏框架]()
-
在提示窗口中填写所有项目详细信息,并将 产品名称 输入为
AntKilling。同时提供您组织的名称,选择设备名称为 iPhone,并将 类前缀 选择为 AK。点击 下一步,如图所示:![iOS 原生游戏框架]()
-
在您的驱动器上选择一个位置以保存项目,然后点击 创建。
-
然后,构建示例项目以检查其输出。一旦您使用播放按钮构建并运行项目,您将在设备上看到以下屏幕:
![iOS 原生游戏框架]()
如您所见,示例 SpriteKit 项目播放了一个带有背景色的标签。SpriteKit 的工作原理是场景,可以理解为游戏的关卡或屏幕。可以同时运行多个场景;例如,在游戏中可以同时运行游戏玩法场景、HUD 场景和得分场景。
现在,我们可以通过以下步骤查看入门项目的更详细设置:
-
在主目录中,您已经默认创建了一个场景;这个场景被称为
AKMyScene。现在,点击AKMyScene.m以探索屏幕上添加标签的代码。您应该看到类似于以下截图的内容:![iOS 原生游戏框架]()
现在,我们必须更新此文件以添加我们的 AntKilling 游戏代码。为了开始编写代码,我们必须满足一些先决条件;例如,我们必须将方向锁定为横屏,因为我们想要一个横屏游戏。
-
要更改游戏的朝向,请打开AntKilling项目设置并导航到目标 | 常规。你将看到以下截图所示的内容:
![iOS 原生游戏框架]()
-
现在,在常规选项卡中,取消选中设备方向选项下的肖像,以便最终设置看起来类似于以下截图:
![iOS 原生游戏框架]()
-
现在,构建并运行项目。你将能够看到应用程序以横幅方向启动。
![iOS 原生游戏框架]()
-
现在,是时候将
AKMyScene更新为包含我们的蚂蚁精灵了。只需下载并打开你为此章节获得的所有资源,并将它们添加到你的 Xcode 项目中。 -
在将资源添加到 Xcode 项目时,请确保选定的目标是AntKilling,并且如果需要,请勾选将项目复制到目标文件夹。
-
现在,从
AKMyScene.m中删除所有现有代码,使其看起来类似于以下截图:![iOS 原生游戏框架]()
现在,这里是对我们迄今为止所做工作的解释:
-
首先,我们创建了一个私有接口来声明私有变量:
@interface AKMyScene () @property (nonatomic) SKSpriteNode *ant; @end -
然后,在
init方法中,我们打印了一个日志来打印屏幕的大小:NSLog(@"Size: %@", NSStringFromCGSize(size)); -
我们使用以下代码行将屏幕背景色更改为白色:
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; -
在以下代码行中,我们使用
spriteNodeWithImageNamed方法创建了一个精灵对象,并将图像名称传递给它。然后,我们将精灵对象定位到屏幕的(100, 100)位置,这是屏幕的左下角。然后,最后,我们将其作为子方法添加:self.ant = [SKSpriteNode spriteNodeWithImageNamed:@"ant.jpg"]; self.ant.position = CGPointMake(100, 100); [self addChild:self.ant];
-
-
现在,构建并运行你的应用程序。你将看到以下截图所示的内容:
![iOS 原生游戏框架]()
现在,正如你所看到的,屏幕颜色已经变成了白色,但屏幕上没有蚂蚁。这意味着代码中有些问题。所以现在,让我们检查我们的日志,它应该打印以下内容:
2014-07-22 19:13:27.019 AntKilling[1437:60b] Size: {320, 568}因此,我们发现场景大小不正确;它应该打印
568作为宽度,320作为高度,但它打印的是相反的。 -
要调试这个问题,请导航到你的
AKViewController.m中的viewDidLoad方法。这将是以下截图所示的内容:![iOS 原生游戏框架]()
因此,从这个方法中,我们可以看到我们的场景从视图的边界中吸收大小,并且这个
viewDidLoad方法在视图被添加到视图层次结构之前就被调用了。因此,它没有响应布局更改。由于视图边界的不一致,我们的场景以错误的边界启动。 -
要解决这个问题,我们必须将场景启动代码移动到
viewWillLayoutSubviews方法中。 -
在从
viewDidLoad方法中移除代码并将其粘贴到viewWillLayoutSubviews之后,代码文件将类似于以下截图所示:![iOS 原生游戏框架]()
-
现在,再次构建并运行应用程序。你会看到以下输出:
![iOS 原生游戏框架]()
恭喜!你已经修复了这个问题;现在,你的蚂蚁已经出现在你指定的屏幕位置上了。如果你仔细观察,你会发现状态栏出现在游戏的顶部,这看起来并不好。
-
要从屏幕上移除状态栏,打开你的
AntKilling-Info.plist文件,并添加UIViewControllerBasedStatusBarAppearance属性,并将值设置为NO。你的.plist文件应该类似于以下截图:![iOS 原生游戏框架]()
-
再次构建并运行你的项目。你现在应该能够看到没有状态栏的游戏,如下面的截图所示:
![iOS 原生游戏框架]()
现在看起来很完美;我们的蚂蚁正如预期的那样驻留在屏幕上。所以,我们的下一个目标是当我们点击蚂蚁时使它动画化。
-
为了完成这个任务,我们需要在
AKMyScene.m文件中initWithSize方法下方添加以下代码:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint positionInScene = [touch locationInNode:self]; SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:positionInScene]; if (touchedNode == self.ant) { SKAction *sequence = [SKAction sequence:@[[SKAction rotateByAngle:degreeToRadian(-3.0f) duration:0.2], [SKAction rotateByAngle:0.0 duration:0.1], [SKAction rotateByAngle:degreeToRadian(3.0f) duration:0.2]]]; [touchedNode runAction:[SKAction repeatActionForever:sequence]]; } } float degreeToRadian(float degree) { return degree / 180.0f * M_PI; }最终的代码文件将类似于以下截图所示:
![iOS 原生游戏框架]()
让我们逐行分析,了解我们到目前为止所做的工作:
-
首先,我们添加了
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event方法来抓取场景上的所有触摸。 -
现在,在函数中,第一行代码允许我们获取
UITouch *touch = [touches anyObject];touch。 -
在下一行,我们抓取了触摸并将其转换为
CGPoint positionInScene = [touch locationInNode:self];位置。 -
使用以下行,我们获取了被触摸的精灵:
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:positionInScene]; -
一旦你有了精灵对象,比较并检查选定的对象是否是蚂蚁虫。如果是蚂蚁虫,那么通过添加以下代码行来使对象动画化:
SKAction *sequence = [SKAction sequence:@[[SKAction rotateByAngle:degreeToRadian(-3.0f) duration:0.2], [SKAction rotateByAngle:0.0 duration:0.1], [SKAction rotateByAngle:degreeToRadian(3.0f) duration:0.2]]]; [touchedNode runAction:[SKAction repeatActionForever:sequence]];
-
-
现在,这段代码将使选定的精灵动画化。构建并运行项目,当你点击蚂蚁时,你会看到蚂蚁在动画化。
你很快就会注意到,当我们点击蚂蚁时,它开始动画化,但没有办法停止它。所以,让我们添加一种方法,当你点击场景中的任何地方时停止这种动画。转到 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 方法,并将其更新为以下代码:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:positionInScene];
if (touchedNode == self.ant) {
SKAction *sequence = [SKAction sequence:@[[SKAction rotateByAngle:degreeToRadian(-3.0f) duration:0.2],
[SKAction rotateByAngle:0.0 duration:0.1],
[SKAction rotateByAngle:degreeToRadian(3.0f) duration:0.2]]];
[touchedNode runAction:[SKAction repeatActionForever:sequence]];
} else {
[self.ant removeAllActions];
}
}
如果你仔细观察,你会发现我们添加了一个 if/else 条件来检查当我们点击蚂蚁时它是否动画化,以及当我们点击屏幕外的任何地方时是否停止所有动作。为了在精灵上停止所有动作,我们可以使用精灵上的 removeAllActions 方法。
Text Kit
UIKit框架包括几个类,其目的是在用户的 App 中显示文本,例如UITextView、UITextField、UILabel和UIWebView。从UITextView类创建的文本视图旨在在屏幕上显示不同类型的文本。UITextView是一个名为Text Kit的强大布局引擎。Text Kit 建立在Core Text之上,因此它提供了与 Core Text 相同速度和功能。UITextView完全集成到 Text Kit 中;它提供编辑和显示功能,使用户能够输入文本、指定格式属性并查看结果。其他 Text Kit 类提供文本存储和布局功能。以下图表显示了 Text Kit 在 iOS 文本和图形框架中的位置:

我们知道 Text Kit 是一系列类和函数的集合。然而,Text Kit 有三个主要类:
-
NSTextStorage(文本存储类) -
NSLayoutManager(布局管理类) -
NSTextContainer(文本容器类)
NSTextStorage
NSTextStorage类负责存储所有与文本属性相关的信息,例如字体、大小或段落信息。NSTextStorage类是NSMutableAttributedString类的子类,这就是为什么它负责保持所有文本属性。除此之外,它的作用还在于确保所有编辑过的文本属性数据在整个可能执行的管理和编辑操作中保持一致。
NSLayoutManager
如其名称所暗示的,NSLayoutManager类管理存储在NSTextStorage对象中的文本数据在视图中显示的方式。其任务是处理并支持任何文本可以显示的视图对象,并执行任何必要的 Unicode 字符到符号的转换,以确保每个字符都能正确地显示在屏幕上。此类对象由NSTextStorage通知关于对文本及其属性所做的任何修改,因此每次更改都会立即反映在相应的视图中。
NSTextContainer
NSTextContainer类实际上指定了文本将出现的位置视图,并处理有关此视图的信息(例如其框架或形状)。然而,此类的一个相当重要的特性是它能够保持一个 Bezier 路径数组,这些路径定义了应该从文本将出现允许区域中排除的区域。这使得文本工具包具有独特的可能性,允许文本绕过图像或其他非文本对象流动,并允许开发者以令人印象深刻或要求严格的方式显示文本。
文本工具包是UIKit框架中一系列类和协议的集合,它提供高质量的排版服务,使应用能够以各种排版存储、布局和显示文本:字距调整、连字符、换行和文本对齐。
字距调整
所有字符都有不同且不规则的外形,并且这些外形必须精确地相邻放置。文本布局库考虑了这一点;例如,大写字母T在其“翅膀”下有很多空白空间,并将下面的小写字母移动得更近。这显著提高了文本的可读性,尤其是在较长的文本片段中。以下截图说明了字距调整:

连字符
这是 Text Kit 的一个艺术特性。一些字符组合起来看起来很漂亮,例如当f与l组合时。这些组合符号被称为字符。

行断开
为了从字符序列创建行,布局引擎必须通过找到一个点来结束一行并开始下一行来进行行断开。在文本系统中,您可以在单词边界或字符边界处指定行断开。在罗马文本中,如果单词在字符之间被断开,需要在断点处插入连字符字符。
对齐
文本行也可以进行对齐;对于水平文本,行通过调整单词间和字符间的间距,在左右边距上对齐,如下面的截图所示。系统在将文本流拆分成行、添加连字符和其他字符替换之后,如果需要,将执行对齐和对齐。

让我们再次转向排除路径。我们知道 Text Kit 包含许多类,Text Container 是其中之一。它的一个伟大特性是它可以存储一个UIBezierPath数组并强制文本绕过这些路径;由于它们被排除在文本绘制区域之外,因此被称为排除路径。让我们通过以下步骤来了解它们是如何工作的:
-
打开 Xcode 并创建一个新项目。转到故事板,将文本字段拖到视图中,并编辑您想要的文本(如下面的截图所示):
![对齐]()
-
将图像视图拖放到文本视图中,如下所示:
![对齐]()
-
在图像视图中添加一个图像,就像我们之前做的那样。将任何图像拖入 Xcode,如下面的截图所示,并通过属性检查器将图像视图的名称与拖动的图像的名称相同:
![对齐]()
-
编译并运行代码;我们的模拟器将如下所示。它切掉了图像下面的文本部分;这不是我们预期的输出。
![对齐]()
-
要实现专用路径文本,我们需要添加一些代码。首先,将文本字段和图像视图链接到
viewController.h文件,如下面的截图所示:![对齐]()
-
在
viewController.m文件中,在viewDidLoad方法中编写以下代码:[_imageView setHidden:YES]; if ([_imageView isHidden]) { CGRect convertedFrame = [_textView convertRect:_imageView.frame fromView:self.view]; [[_textView textContainer] setExclusionPaths:@[[UIBezierPath bezierPathWithRect:convertedFrame]]]; } else{ [[_textView textContainer] setExclusionPaths:nil]; } [_imageView setHidden:![_imageView isHidden]];在前面的代码中,我们首先检查图像视图是否隐藏(
_propertyName也是访问除self.property之外任何属性的一种方式)。如果它被隐藏(初始状态),那么我们必须将排除路径设置为文本视图的文本容器对象,以便文本围绕图像视图流动,当然,也要使图像视图可见:CGRect convertedFrame = [_textView convertRect:_imageView.frame fromView:self.view];这行代码用于将图像视图坐标从
self.view视图转换为文本视图,因此图像视图和浮动文本存在于同一区域。接下来,使用UIBezierPath类的bezierPathWithRect:类方法,我们通过其参数指定的框架创建了贝塞尔路径;在我们的情况下,这是图像视图的框架(转换为文本视图的坐标)。我们将这个贝塞尔路径添加到NSArray中,最后,我们设置了排除路径。请注意,在else情况下,我们将排除路径设置为nil,因为我们不希望在图像视图不存在时文本流动。我们的
viewController.m文件将类似于以下截图所示:![对齐]()
-
运行代码。现在,我们的文本不会因为图像而被截断。看下面的截图;它没有截断我们的文本,并且每行文本都在图像之后继续。文本通过
UIBezierPath类为图像留出空间,并为图像视图创建了一个不同的类。所以,现在,你可以尝试圆形图像或其他任何形状的图像。![对齐]()
摘要
在本章中,我们学到了很多有趣的东西,包括如何创建游戏,在文本之间添加图像,以及通过 AirDrop 分享。所有这些 API 都是在 iOS 7 中引入的。在本章之后,尝试扩展在这里执行的所有活动。这将阐明你的概念。在下一章中,我们将学习 iOS 8 API,包括 PhotoKit、手动相机控制和 Handoff 概念。
第五章。iOS 8 中引入的框架
在本章中,我们将涵盖以下主题:
-
使用 PhotoKit 的工作
-
在所有设备之间无缝恢复活动的 Handoff 功能
使用 PhotoKit 的工作
iOS 8 中的一项新功能已被揭晓;它被称为 PhotoKit,也称为照片框架。这是一个新的扩展,允许开发者更有效地与设备中存储的照片和视频工作。
PhotoKit 由以下两个新的框架组成:
-
照片框架:这将允许开发者检索和编辑照片和视频。它负责处理来自外部应用所做的更改,并提供构建完整应用(如 iOS 默认提供的照片应用)的工具。
-
照片用户界面:这负责提供创建编辑扩展的能力——换句话说,就是可以直接从 iOS 相册中用我们的自定义应用编辑照片。
照片框架
照片框架提供了访问、添加、编辑和删除模型对象(图像、视频、相册和时刻)的工具。
这些模型实例有一个关键特性——那就是它们是只读的。所以,例如,我们可以编辑一个图像,它不会修改原始内容;然而,它将创建一个新的。
不同的模型对象如下:
-
资产:它们指的是图像和视频,由
PHAsset类表示。它提供了指定媒体内容类型(照片或视频)、创建日期、位置以及是否为收藏夹的能力。 -
资产集合:它们也被称为时刻,指的是有序的资产集合,例如相册和智能相册。这些对象由
PHAssetCollection表示,其属性包括标题、类型以及开始和结束日期。 -
集合列表:它们是有序的集合集合,通常代表文件夹或年份时刻。管理它们的类
PHCollectionList存储列表的类型、标题以及开始和结束日期。
照片框架还引入了 瞬态集合,它们引用一组由搜索或用户选择产生的资产,并且可以与普通集合进行交换。在使用此框架时,还需要注意的一点是,我们将需要使用以下类方法等来处理资产:
[PHAsset fetchAssetsWithMediaType: options:];
[PHAssetCollection fetchMomentsWithOptions:];
[PHAssetCollection transientAssetCollectionWithAssets:title:];
照片用户界面
照片用户界面是一个非常有趣的框架,它允许我们在应用中创建照片编辑扩展,这些扩展将在内置的照片应用中可用。
这意味着,当在相册中编辑图片时,我们可以选择我们想要使用的应用程序;此编辑的结果将通过 iCloud 对其他应用程序和设备可用,而不会修改原始内容,因为它只读。为了实现这一点,我们需要创建一个应用程序扩展目标,该目标将提供一个视图控制器,该控制器将采用 PHContentEditingController 协议。这很简单,因为 Xcode 的新版本提供了一个创建照片编辑扩展的模板;我们只需要关注以下协议方法的实现:
[startContentEditingWithInput:]
[finishContentEditingWithCompletionHandler:]
[canHandleAdjustmentData:]
[cancelContentEditing]
我们可以使用照片框架来处理照片应用管理的照片和视频资产,包括 iCloud 照片库。使用此框架检索用于显示和播放的资产,编辑它们的图像或视频内容,或者处理资产集合,例如相册、时刻和 iCloud 共享相册。
PhotoKit 的功能
PhotoKit 的功能如下:
-
获取实体和请求更改:Photos 框架模型类的实例(
PHAsset、PHAssetCollection和PHCollectionList)代表了用户在照片应用中工作的实体。这些实体是资产(图像或视频)、资产的集合(例如相册或时刻),以及集合的列表(例如相册文件夹或时刻集群)。这些对象也称为照片实体,是只读的、不可变的,并且只包含诸如资产的媒体类型和创建日期之类的元数据。我们通过获取我们感兴趣的图片实体,然后使用这些对象来获取我们处理所需的数据来处理资产和集合。要更改照片实体,我们创建更改请求对象,并明确地将它们提交到共享的
PHPhotoLibrary对象。这种架构使得从多个线程或多个应用程序和应用程序扩展中处理相同的资产变得容易、安全且高效。 -
观察变化:使用共享的
PHPhotoLibrary对象为获取的图片实体注册一个变化处理器。PhotoKit 会通知您的应用程序,当其他应用程序或设备更改了资产的内容或元数据,或者更改了集合中资产的列表时。PHChange对象提供了关于每个变化前后对象状态的信息,这些语义使得更新集合视图或类似界面变得容易。 -
支持照片应用功能:使用
PHCollectionList类来查找与照片应用中时刻层次结构对应的资产。使用PHAsset类来识别连拍照片、全景照片和高帧率视频。当启用 iCloud 照片库时,Photos 框架中的资产和集合反映了同一 iCloud 账户下所有设备上的内容。 -
资产和缩略图加载和缓存:使用
PHImageManager类请求指定大小的资产图像或使用 AV Foundation 对象处理视频资产。Photos 框架会根据您的指定自动下载或生成图像,并将它们缓存以供快速重用。对于大量资产(例如,当用缩略图填充集合视图时)的更快性能,PHCachingImageManager子类添加了批量预加载功能。 -
资产内容编辑:
PHAsset和PHAssetChangeRequest类定义了请求编辑照片或视频内容的方法,并将您的编辑提交到照片库。为了支持在不同应用程序和扩展之间保持编辑的连续性,Photos 会保留每个资产的当前版本和上一个版本,以及描述最后编辑的PHAdjusmentData对象。如果您的应用程序支持来自先前编辑的调整数据,您可以允许用户撤销或更改编辑。
有许多 PhotoKit 类;我们将在下表中讨论它们:
| 类别 | 描述 |
|---|---|
PHAdjustmentData |
当用户编辑资产时,Photos 会保存此对象以及修改后的图像或视频数据 |
PHAssetChangeRequest |
您可以在照片库中创建和使用此对象,并更改块以创建、删除或修改 PHAsset 对象 |
PHAssetCollectionChangeRequest |
您可以在照片库中创建和使用此对象,并更改块以创建、删除或修改 PHAssetCollection 对象 |
PHChange |
Photos 提供此对象以通知您的应用程序 Photos 应用程序管理的资产和集合的任何更改 |
PHCollectionListChangeRequest |
您可以在照片库中创建和使用此对象,并更改块以创建、删除或修改 PHCollectionList 对象 |
PHContentEditingInput |
此对象描述了用于编辑的资产 |
PHContentEditingInputRequestOptions |
您可以使用此对象在请求编辑 PHAsset 对象的图像或视频内容时指定选项 |
PHContentEditingOutput |
此对象表示编辑 Photos 资产的照片或视频内容的结果 |
PHFetchOptions |
您可以使用此对象在调用 PHAsset、PHCollection、PHAssetCollection 和 PHCollectionList 类的方法时指定选项,以检索照片实体 |
PHFetchResult |
此对象是一个有序照片实体对象的容器 |
PHFetchResultChangeDetails |
此对象提供了有关两个检索结果之间差异的详细信息——您之前获得的那个和您再次执行相同检索将得到的更新后的那个 |
PHImageManager |
这是一个共享对象,它提供了加载与 PHAsset 对象关联的图像或视频数据的方法 |
PHCachingImageManager |
此对象用于获取或生成照片或视频资产的图像数据 |
PHImageRequestOptions |
您可以使用此对象在从 PHImageManager 对象请求照片资源的图像表示时指定选项 |
PHObject |
此类是照片实体对象的抽象基类 |
PHAsset |
此对象表示在照片应用中出现的图像或视频文件,包括 iCloud 照片内容 |
PHCollection |
此类是一个抽象类,定义了照片集合类之间共享的行为 |
PHAssetCollection |
此对象表示一组照片或视频资源 |
PHCollectionList |
此对象表示一组资产集合 |
PHObjectPlaceholder |
此对象是一个只读代理,表示尚未创建的对象 |
PHObjectChangeDetails |
此对象提供了关于照片实体两个状态之间差异的详细信息——一个是您之前获取的状态,另一个是您再次获取此实体时的更新状态 |
PHPhotoLibrary |
这是一个共享对象,表示用户的照片库——由照片应用管理的整个资产和集合集,包括存储在本地设备上的对象以及(如果启用)在 iCloud 照片中存储的对象 |
PHVideoRequestOptions |
您可以使用此对象在从 PHImageManager 对象请求视频资源时指定选项 |
PhotoKit 通过多种获取方法使查询模型数据变得简单。例如,要检索所有图像,您可以调用 PFAsset.Fetch,传递 PHAssetMediaType.Image 媒体类型:
PHFetchResult fetchResults = PHAsset.FetchAssets (PHAssetMediaType.Image, null);
PHFetchResult 实例将包含所有代表图像的 PFAsset 实例。要获取图像本身,您可以使用 PHImageManager(或缓存版本,PHCachingImageManager)通过调用 requestImageForAsset 来请求图像。例如,以下代码为 PHFetchResult 中的每个资产检索图像,以在集合视图单元格中显示。此示例使用 Swift 语言,因此我们需要了解 Swift。为了更好地开始 Swift,您可以访问 www.raywenderlich.com/tutorials。
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
var imageCell = (ImageCell)collectionView.DequeueReusableCell(cellId, indexPath);
imageMgr.RequestImageForAsset ((PHAsset)fetchResults[(uint)indexPath.Item],
thumbnaillSize,
PHImageContentMode.AspectFill, new PHImageRequestOptions (),(img, info) => {
imageCell.ImageView.Image = img;
});
return imageCell;
}
这就是处理查询和读取数据的方式。您还可以将更改写回库中。由于多个感兴趣的应用程序能够与系统的照片库交互,您可以使用 PhotoLibraryObserver 注册观察者以通知任何更改。然后,当有更改发生时,您的应用程序可以相应地更新。例如,以下是一个简单的实现,用于重新加载集合视图:
class PhotoLibraryObserver : PHPhotoLibraryChangeObserver
{
readonly PhotosViewController controller;
public PhotoLibraryObserver (PhotosViewController controller)
{
this.controller = controller;
}
public override void PhotoLibraryDidChange (PHChange changeInstance)
{
DispatchQueue.MainQueue.DispatchAsync (() => {
var changes = changeInstance.GetFetchResultChangeDetails (controller.fetchResults);
controller.fetchResults = changes.FetchResultAfterChanges;
controller.CollectionView.ReloadData ();
});
}
}
要实际将更改从您的应用程序写回,您可以创建一个更改请求。每个模型类都有一个相关的更改请求类。例如,要更改 PHAsset,您可以创建 PHAssetChangeRequest。执行写回照片库并发送到观察者的更改的步骤如下:
-
执行编辑操作。
-
将过滤后的图像数据保存到
PHContentEditingOutput实例。 -
提出更改请求以发布从编辑输出中更改的内容。
这里有一个示例,将更改写回应用核心图像 noir 滤镜的图像。您还可以参考blog.xamarin.com/build-great-photo-experiences-in-ios-8-with-photokit/以更详细地了解:
void ApplyNoirFilter (object sender, EventArgs e)
{
Asset.RequestContentEditingInput (new PHContentEditingInputRequestOptions (), (input, options) => {
//
// perform the editing operation, which applies a noir filter in this case
var image = CIImage.FromUrl (input.FullSizeImageUrl);
image = image.CreateWithOrientation ((CIImageOrientation)input.FullSizeImageOrientation);
var noir = new CIPhotoEffectNoir {
Image = image
};
var ciContext = CIContext.FromOptions (null);
var output = noir.OutputImage;
var uiImage = UIImage.FromImage (ciContext.CreateCGImage (output, output.Extent));
imageView.Image = uiImage;
//
// save the filtered image data to a PHContentEditingOutput instance
var editingOutput = new PHContentEditingOutput(input);
var adjustmentData = new PHAdjustmentData();
var data = uiImage.AsJPEG();
NSError error;
data.Save(editingOutput.RenderedContentUrl, false, out error);
editingOutput.AdjustmentData = adjustmentData;
//
// make a change request to publish the changes form the editing output
PHPhotoLibrary.GetSharedPhotoLibrary.PerformChanges (
() => {
PHAssetChangeRequest request = PHAssetChangeRequest.ChangeRequest(Asset);
request.ContentEditingOutput = editingOutput;
},
(ok, err) => Console.WriteLine ("photo updated successfully: {0}", ok));
});
}
当用户选择按钮时,应用过滤器,如下面的截图所示:

多亏了PHPhotoLibraryChangeObserver,当用户导航回照片库时,更改会在集合视图中反映出来。
您可以从github.com/mikebluestein/PhotoKitDemo下载示例项目。
无缝恢复活动的 Handoff
Handoff 是 OS X 和 iOS 中的一个功能,它扩展了跨设备的用户体验的连续性。Handoff 允许用户在一个设备上开始一项活动,然后切换到另一个设备并继续在同一设备上完成该活动。例如,一个在 Safari 中浏览长篇文章的用户移动到已登录相同 Apple ID 的 iOS 设备;现在相同的网页在 iOS 的 Safari 中自动打开,滚动位置与原始设备相同。Handoff 使这种体验尽可能无缝。
要参与 Handoff,一个应用采用基础库中的一个小 API。应用中的每个持续活动都由一个包含恢复活动所需数据的用户活动对象表示。当用户选择恢复此活动时,该对象被发送到恢复设备。每个用户活动对象都有一个委托对象,在适当的时候被调用以刷新活动状态,例如在用户活动对象在设备之间发送之前。
如果继续活动需要比用户活动对象容易传输的数据更多,恢复应用有选项打开到原始应用的流。基于文档的应用自动支持使用 iCloud 文档的用户的活动继续。Apple 应用使用公共 API 实现 iOS 8 和 OS X v10.10 的 Handoff。第三方开发者可以使用相同的 API 在共享开发者团队 ID 的应用中实现 Handoff。此类应用必须通过 App Store 分发或由注册的开发者签名。
Handoff 的兼容性
首先,让我们看看什么与 Handoff 兼容:
-
配备 Lightning 连接器的 iOS 设备和 2012 年或更新的 Mac 型号支持 Handoff。两者都有支持蓝牙低功耗和 Wi-Fi Direct 的无线电芯片。
-
如果您的设备兼容,您可以在 Mac 的通用系统偏好设置中以及 iOS 的设置应用下的通用面板中启用 Handoff;在两种情况下,寻找包含Handoff一词的选项。
-
所有设备都必须登录到相同的 iCloud 账户。Handoff 不与其他用户一起工作(这是 AirDrop 的作用)。
-
最后,确保所有设备都打开了蓝牙和 Wi-Fi。
到目前为止,苹果公司宣布 Handoff 将与以下应用一起工作:
-
邮件
-
Safari
-
页面
-
数字
-
演示文稿
-
地图
-
消息
-
提醒
-
日历
-
联系人
使用它们,我们可以开始撰写或阅读电子邮件或网站;编辑文档、电子表格或演示文稿;查找位置;输入文本;选择提醒;输入约会;或在您的 Mac 上查找地址,然后在 iPhone(iPad 或反之亦然)上继续或完成。
应用框架支持
UIKit和AppKit在文档、响应者和应用代理类中提供了对 Handoff 的支持。尽管在不同平台之间存在一些行为上的细微差异,但使应用能够保存和恢复用户活动的根本机制是相同的,并且 API 也是相同的。
Handoff 交互
传递用户活动涉及以下三个步骤:
-
为用户参与的每个活动创建一个用户活动对象。
-
定期更新用户活动对象,以包含用户正在执行的信息。
-
当用户请求时,在另一台设备上继续用户活动。
直接实现 Handoff
在您的应用中采用 Handoff 需要您编写使用 UIKit 和 AppKit 中的 API 创建用户活动对象、更新对象状态以跟踪活动,并在另一台设备上继续活动的代码。
创建用户活动对象
每个可以被传递给继续使用的设备用户活动都由从NSUserActivity类实例化的用户活动对象表示。这个类为它支持的每个用户活动创建一个用户活动对象。这些用户活动的类型取决于应用。例如,Safari 允许用户在相同网站上继续使用浏览器。
以下代码创建了NSUserActivity实例。myactivity.userinfo对象存储了当前的 URL 和滚动位置。becomeCurrent对象包含我们项目的当前状态,并且每秒更新一次。创建当前状态的对象是必要的;否则,其他设备将无法理解从哪里开始。
NSUserActivity* myActivity = [[NSUserActivity alloc]
initWithActivityType: @"com.myCompany.myBrowser.browsing"];
myActivity.userInfo = @{ ... };
myActivity.title = @"Browsing";
[myActivity becomeCurrent];
在终止或完成应用后,用户活动对象将自动释放。然后该对象将从所有设备中删除。
指定活动类型
活动类型标识符是一个短字符串,它出现在你的应用的Info.plist属性列表文件中的NSUserActivityTypes数组中,该数组列出了你的应用支持的所有活动类型。当你创建活动时,传递相同的字符串,此时活动对象以com.myCompany.myBrowser.browsing的活动类型创建,这是一种反向 DNS 风格的表示法,旨在避免冲突。当用户选择继续活动时,活动类型(以及应用的团队 ID)确定在接收设备上启动哪个应用以继续活动。
注意
你可以在创建实例时指定NSUserActivity对象的活动类型。创建后,你不能更改对象的活动类型。
例如,一个类似提醒事项的应用序列化用户正在查看的提醒列表。当用户点击新的提醒列表时,应用在NSUserActivityDelegate中跟踪该活动。以下代码显示了在用户切换到不同的提醒列表时被调用的方法的可能实现。此应用将活动名称追加到应用的捆绑标识符中,以创建在创建其NSUserActivity对象时使用的活动类型。
// UIResponder and NSResponder have a userActivity property
NSUserActivity *currentActivity = [self userActivity];
// Build an activity type using the app's bundle identifier
NSString *bundleName = [[NSBundle mainBundle] bundleIdentifier];
NSString *myActivityType = [bundleName stringByAppendingString:@".selected-list"];
if(![[currentActivity activityType] isEqualToString:myActivityType]) {
[currentActivity invalidate];
currentActivity = [[NSUserActivity alloc]
initWithActivityType:myActivityType];
[currentActivity setDelegate:self];
[currentActivity setNeedsSave:YES];
[self setUserActivity:currentActivity];
} else {
// Already tracking user activity of this type
[currentActivity setNeedsSave:YES];
}
之前的代码使用了setNeedsSave访问器方法来标记用户活动对象,当它需要更新时。这使系统能够合并更新并延迟执行。
填充活动对象的用户信息字典
活动对象有一个包含所需数据的用户信息字典,以便将活动传递给继续应用。用户信息字典可以包含NSArray、NSDate、NSDictionary、NSNull、NSNumber、NSSet、NSString和NSUrl对象。系统修改使用file:方案的NSUrl对象,并指向指向接收设备上相应容器中相同项的 iCloud 文档:
NSUserActivity* myActivity = [[NSUserActivity alloc]
initWithActivityType: @"com.myCompany.myReader.reading"];
// Initialize userInfo
NSURL* webpageURL = [NSURL URLWithString:@"http://www.myCompany.com"];
myActivity.userInfo = @{
@"docName" : currentDoc,
@"pageNumber" : self.pageNumber,
@"scrollPosition" : self.scrollPosition
};
在响应者中采用手势传递
如果你将活动设置为响应者的userActivity属性,你可以将响应者对象(在 OS X 上继承自NSResponder或在 iOS 上继承自UIResponder)与给定的用户活动关联。系统会在适当的时候自动保存NSUserActivity对象,调用响应者的updateUserActivityState:重写方法,使用活动对象的userInfoEntriesFromDictionary:方法将当前数据添加到用户活动对象中:
- (void)updateUserActivityState:(NSUserActivity *)userActivity {
. . .
[userActivity setTitle: self.activityTitle];
[userActivity addUserInfoEntriesFromDictionary: self.activityUserInfo];
}
继续活动
手势传递自动宣传可在 iOS 和 OS X 设备上继续的活动,这些设备与原始设备物理邻近,并登录到与原始设备相同的 iCloud 账户。当用户选择继续某个活动时,手势传递启动相应的应用,并通过AppDelegate发送消息来确定如何使用AppDelegate恢复活动。
实现方法 application:willContinueUserActivityWithType: 以让用户知道活动将很快继续。使用 application:continueUserActivity:restorationHandler: 方法来配置应用以继续活动。当活动对象及其 userInfo 字典中的活动状态数据对继续应用可用时,系统会调用此方法。
注意
对于在 NSUserActivity 对象的 userinfo 字典中传输的 URL,我们必须调用 startAccessingSecurityScopedResource,并且它必须在我们可以访问 URL 之前返回 YES。在完成文件使用后,调用 stopAccessingSecurityScopedResource。
此要求的例外是 UIDocument 文档的 URL 和为 specifyingNSUbiquitousDocumentUserActivityType 应用自动创建的 NSDocument 的 URL,这些 URL 从 :continueUserActivity:restorationHandler: 应用返回 NO(或者留空不实现)。请参阅 developer.apple.com/library/mac/documentation/UserExperience/Conceptual/Handoff/AdoptingHandoff/AdoptingHandoff.html#//apple_ref/doc/uid/TP40014338-CH2-SW17 中的 在基于文档的应用中采用 Handoff。
您可以通过将对象传递给与 application:continueUserActivity:restorationHandler: 消息一起传递的恢复处理程序块来选择性地执行应用以继续活动的额外配置。以下代码展示了此方法的简单实现:
- (BOOL)application:(NSApplication *)application
continueUserActivity: (NSUserActivity *)userActivity
restorationHandler: (void (^)(NSArray*))restorationHandler {
BOOL handled = NO;
// Extract the payload
NSString *type = [userActivity activityType];
NSString *title = [userActivity title];
NSDictionary *userInfo = [userActivity userInfo];
// Assume the app delegate has a text field to display the activity information
[appDelegateTextField setStringValue: [NSString stringWithFormat:
@"User activity is of type %@, has title %@, and user info %@",
type, title, userInfo]];
restorationHandler(self.windowControllers);
handled = YES;
return handled;
}
在这种情况下,应用代理有一个 NSWindowController 对象数组 windowControllers。这些窗口控制器知道如何配置应用的所有窗口以恢复活动。在将此数组传递给 restorationHandler 块之后,Handoff 会向这些对象中的每一个发送一个 restoreUserActivityState: 消息,传递恢复活动的 NSUserActivity 对象。窗口控制器从 NSResponder 继承了 restoreUserActivityState: 方法;每个控制器对象都重写此方法以配置其窗口,使用活动对象 userInfo 字典中的信息。
为了支持优雅的失败,应用代理应该实现 application:didFailToContinueUserActivityWithType:error: 方法。如果您不实现此方法,应用框架仍然会显示包含在传入的 NSError 对象中的诊断信息。
注意
本节中描述的用于 Handoff 的 UIApplicationDelegate 方法,当任一应用代理方法 application:willFinishLaunchingWithOptions: 或 application:didFinishLaunchingWithOptions: 返回 NO 时,不会被调用。
原生应用到网页浏览器 Handoff
当在原始设备上使用原生应用时,用户可能希望在相应的原生应用中继续活动。如果有与活动相对应的网页,它仍然可以被传递。例如,视频库应用允许用户浏览可观看的电影,而邮件应用允许用户阅读和撰写电子邮件。在许多情况下,用户可以通过网页界面执行相同的活动。在这种情况下,原生应用知道网页界面的 URL,可能包括指定正在浏览的视频或正在阅读的消息的语法。因此,当原生应用创建NSUserActivity对象时,它会设置webpageURL属性。如果接收设备没有支持用户活动activityType属性的应用,它可以在继续平台的默认网络浏览器中恢复活动。
想以这种方式继续活动的 OS X 网络浏览器应声明NSUserActivityTypeBrowsingWeb活动类型(通过在应用的Info.plist属性列表文件中的NSUserActivityTypes数组中输入此字符串)。这确保了,如果用户选择任何其他浏览器作为他们的默认浏览器,它将接收活动对象而不是 Safari。
网络浏览器到原生应用的手动传递
在相反的情况下,如果用户在原始设备上使用网络浏览器,而接收设备是一个具有声明webpageURL属性域名部分的原生应用(iOS 设备),那么 iOS 将启动原生应用并向其发送一个activityType值为NSUserActivityTypeBrowsingWeb的NSUserActivity对象。webpageURL属性包含用户正在访问的 URL,而userInfo字典为空。
接收设备上的原生应用必须通过在com.apple.developer.associated-domains权限中声明一个域名来适应这种行为。此权限的值具有<service>:<fully qualified domain name>格式,例如,activitycontinuation:example.com。在这种情况下,服务必须是activitycontinuation。在 Xcode 中,在Target设置的Capabilities标签下的Associated Domains部分添加com.apple.developer.associated-domains权限的值。
如果此域名与webpageURL属性匹配,手动传递将下载来自域的批准应用 ID 列表。域批准的应用有权继续活动。在您的网站上,您可以在名为apple-app-site-association的签名 JSON 文件中列出批准的应用;例如,网页地址变为https://example.com/apple-app-site-association(您必须使用实际设备而不是模拟器来测试下载 JSON 文件)。
JSON 文件包含一个字典,该字典指定了在 目标 设置的 常规 选项卡中 <team identifier>.<bundle identifier> 格式的应用程序标识符列表,例如 YWBN8XTPBJ.com.example.myApp。以下代码显示了一个格式化的 JSON 文件示例,用于读取:
{
"activitycontinuation": {
"apps": [ "YWBN8XTPBJ.com.example.myApp",
"YWBN8XTPBJ.com.example.myOtherApp" ]
}
}
要对 JSON 文件进行签名(以便它从服务器返回时带有正确的 application/pkcs7-mime 内容类型),请将内容放入一个文本文件并对其进行签名。您可以使用以下代码中显示的终端命令等执行此任务,通过删除文本中的空白字符以方便操作。使用 openssl 命令和由受 iOS 信任的证书颁发机构签发的身份证书和密钥(在 support.apple.com/kb/ht5012 中列出)。它不必是托管 Web 凭证的同一身份(例如示例代码中的 https://example.com),但它必须是针对所涉及域名的有效 TLS 证书:
echo '{"activitycontinuation":{"apps":["YWBN8XTPBJ.com.example.myApp",
"YWBN8XTPBJ.com.example.myOtherApp"]}}' > json.txt
cat json.txt | openssl smime -sign -inkey example.com.key
-signer example.com.pem
-certfile intermediate.pem
-noattr -nodetach
-outform DER > apple-app-site-association
openssl 命令的输出是您放在网站上的签名 JSON 文件,位于 apple-app-site-association URL 上——在本例中为 https://example.com/apple-app-site-association。
应用程序可以将 webpageURL 属性设置为任何 Web URL,但它只能接收 webpageURL 域在 com.apple.developer.associated-domains 权限中的活动对象。此外,webpageURL 的方案必须是 http 或 https。任何其他方案都会抛出异常。
使用延续流
如果恢复活动需要比初始 Handoff 有效载荷能够有效传输的数据更多,则延续应用程序可以回调到原始应用程序的活动对象以在应用程序之间打开流并传输更多数据。在这种情况下,原始应用程序将其 NSUserActivity 对象的布尔属性 supportsContinuationStreams 设置为 YES,设置用户活动代理,然后调用 becomeCurrent,如下面的代码所示:
NSUserActivity* activity = [[NSUserActivity alloc] init];
activity.title = @"Editing Mail";
activity.supportsContinuationStreams = YES;
activity.delegate = self;
[activity becomeCurrent];
在继续使用的设备上,在用户表示他们想要恢复活动后,系统将启动相应的应用程序并开始向应用程序代理发送消息。然后应用程序代理可以通过向其用户活动对象发送 getContinuationStreamsWithCompletionHandler 消息来请求将流返回到原始应用程序,如下面的代码覆盖实现所示:
- (BOOL)application:(UIApplication *)application
continueUserActivity: (NSUserActivity *)userActivity
restorationHandler: (void(^)(NSArray *restorableObjects))restorationHandler
{
[userActivity getContinuationStreamsWithCompletionHandler:^(
NSInputStream *inputStream,
NSOutputStream *outputStream, NSError *error) {
// Do something with the streams
}];
return YES;
}
在原始设备上,用户活动代理在其 userActivity:didReceiveInputStream:outputStream 方法的回调中接收流,它实现了该方法以提供在恢复设备上使用流继续用户活动所需的数据。
NSInputStream 提供了对流数据的只读访问,而 NSOutputStream 提供了只写访问。因此,在原始端写入输出流的数据,将在继续端从输入流中读取;反之亦然。流设计用于请求-响应模式,也就是说,继续端使用流从原始端请求更多续传数据,然后原始端使用流提供所需数据。
续传流是 Handoff 的一个可选功能;大多数用户活动不需要它们来实现成功的续传。即使需要流,在大多数情况下,应用之间应该有最少的来回交互。一个简单的请求来自继续应用,并伴随来自原始应用的响应,对于大多数续传事件来说应该就足够了。你可以从 github.com/dokterdok/Continuity-Activation-Tool 下载示例项目。
摘要
在本章中,我们讨论了新的 iOS 8 API 和 Swift 的简短代码片段。你学习了 PhotoKit 框架和 Handoff,并附带了一些代码片段。在下一章中,我们将讨论 iOS 中的 iCloud 和安全服务,以及它们的实现。
第六章。使用 iCloud 和安全服务
在本章中,您将学习如何将数据存储在云端,即 iCloud 和 iOS 的安全服务中,通过这些服务我们可以保护我们的数据、密码等。
在本章中,我们将涵盖以下主题:
-
与 iCloud 一起工作
-
使用密钥链过程保存数据
-
iOS Touch ID 验证
与 iCloud 一起工作
基本上,iCloud 是一种帮助用户在不同设备间同步数据的云服务。其主要目的是让用户能够轻松存储文件或文档等数据,以便在任何 iOS 设备上访问。虽然您可以使用其他云服务来保存文件或数据,但 iCloud 的核心思想是消除设备之间的显式或有线连接。苹果公司不希望用户将云服务器和同步过程联系起来。一切工作都简单无缝地完成。
同样的设计理念也适用于开发者。当我们采用 iCloud 时,我们不需要知道如何与云服务器交互或上传数据到 iCloud。iOS 会处理所有繁重的工作。我们的重点是内容,例如管理数据的变化或开发云与设备之间的连接。
iCloud 提供三种类型的存储:
-
键值存储:用于存储设置、偏好设置和应用程序状态等内容。
-
文档存储:用于存储文件类型内容,如 WordPress 文档、绘图和复杂的应用程序状态。
-
核心数据存储:用于结构化内容的跨设备数据库解决方案。iCloud 核心数据存储建立在文档存储之上,并使用相同的 iCloud API。
让我们了解 iCloud 实际上是如何工作的。要使用 iCloud,我们需要一个 iOS 开发者账户。假设我们有一个 iOS 开发者账户,请按照以下步骤操作:
-
访问
idmsa.apple.com/IDMSWebAuth/login?&appIdKey=891bd3417a7776362562d2197f89480a8547b108fd934911bcbea0110d07f757&path=%2F%2Faccount%2Findex.action。我们首先创建具有可用 iCloud 功能的应用程序 ID。 -
登录到 iOS 配置文件门户,选择应用程序 ID,然后创建一个新的应用程序 ID。
-
通过选择 iCloud 选项为您的应用程序启用 iCloud 服务。选择您正在使用的 Xcode(如果您正在使用 Xcode 5,则选择 Xcode 5)并点击 继续,如图所示:
![与 iCloud 一起工作]()
-
现在,在 名称 文本框中输入我们项目的名称,在 包 ID 文本框中写入包 ID,然后点击 继续,如图所示:
![与 iCloud 一起工作]()
-
现在,它将显示我们应用程序启用的服务。确保 iCloud 已启用或可配置,如图所示:
![与 iCloud 一起工作]()
-
然后,进入Xcode并构建一个新的单视图应用程序。之后,进入Xcode中的功能并开启iCloud;这将包括 iCloud 权限、iCloud 容器以及到 Cloud 框架的链接,如图下所示:
![与 iCloud 一起工作]()
-
确保权限是自动创建的。以下截图显示了创建的权限:
![与 iCloud 一起工作]()
-
删除故事板中的
ViewController类和现有视图。 -
现在,我们将清空故事板并创建一个新的 Objective-C 类,它是
tableViewController的子类,并将其命名为(例如,NoteListViewController)。 -
将一个表格视图控制器拖到故事板中,并将其嵌入到导航控制器中。现在,从属性检查器为这个表格视图控制器命名;它应该与类名相同。为一个新的类重复此操作一次,并将其命名为(例如,
AddNoteViewController)。 -
现在,我们在故事板中有两个表格视图。对于
NoteListViewController,将一个按钮拖到导航栏的右上角,并将标识符设置为add。这将自动将按钮更改为+按钮,如图下所示。接下来,选择原型单元格并将其样式更改为Basic。![与 iCloud 一起工作]()
导航控制器和笔记列表视图控制器
-
对于
AddNoteViewController,将栏按钮拖到导航栏中。将一个命名为Cancel,另一个命名为Save。 -
然后,选择表格视图并将其内容设置为静态单元格。最后,向表格视图中添加一个静态行并添加一个文本字段,如图所示:
![与 iCloud 一起工作]()
导航控制器和笔记列表视图控制器
-
当用户点击+按钮时,将显示
AddNoteViewController。因此,按住Ctrl键,点击+按钮,并将其拖到添加笔记视图控制器。选择模态作为过渡动作。现在,我们的故事板应该看起来像这样:![与 iCloud 一起工作]()
显示添加笔记视图控制器的表格视图
-
最后,将文本字段链接到
AddNoteViewController并创建两个动作方法:Cancel和Save。我们的AddNoteViewController.h中的代码应该如下所示:#import <UIKit/UIKit.h> @interface AddNoteViewController : UITableViewController - (IBAction)cancel:(id)sender; - (IBAction)save:(id)sender; @property (weak, nonatomic) IBOutlet UITextField *noteTextField; -
我们需要实现
NoteListViewController类,以便我们可以在表格视图中显示云上可用的笔记。打开NoteListViewController.h并将此属性添加到其中:@property (strong, nonatomic) NSMutableArray *notes; -
该属性的目的是在本地保存笔记。当需要时,我们将在表格视图中显示笔记。接下来,我们需要对此属性进行懒加载实例化。如果有一个只需要配置一次且涉及一些我们不想在
init方法中混淆的配置的对象,懒加载实例化是一个很好的技术。在NoteListViewController.m中添加以下代码:- (NSArray *)notes { if(!_notes){ _notes = [NSMutableArray array]; _notes = [[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:@"AVAILABLE_NOTES"] mutableCopy]; } return _notes; } -
在
viewDidLoad方法中,我们需要创建如下通知方法:- (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.leftBarButtonItem = self.editButtonItem; // Observer to catch changes from iCloud NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore]; [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(storeDidChange:)name: NSUbiquitousKeyValueStoreDidChangeExternallyNotificationobject:store]; [[NSUbiquitousKeyValueStore defaultStore] synchronize]; // Observer to catch the local changes [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(didAddNewNote:)name:@"New Note"object:nil]; }使用
NSUbiquitousKeyValueStore类实现 iCloud 数据同步。NSUbiquitousKeyValueStore是NSObject的子类,并在 iOS 5.0 及以后的版本中可用。NSNotificationCenter对象提供了一种在程序内部广播信息或消息的机制。它也是一个NSObject的子类,并在 iOS 2.0 及以后的版本中可用。我们的
viewDidLoad方法可能看起来像这样:![与 iCloud 一起工作]()
-
现在,我们必须实现当调用前面的通知时执行的方法。当用户保存新笔记时,将调用
didAddNewNote方法:#pragma mark - Observer New Note - (void)didAddNewNote:(NSNotification *)notification{ NSDictionary *userInfo = [notification userInfo]; NSString *noteStr = [userInfo valueForKey:@"Note"]; [self.notes addObject:noteStr]; // Update data on the iCloud [[NSUbiquitousKeyValueStore defaultStore] setArray:self.notes forKey:@"AVAILABLE_NOTES"]; // Reload the table view to show changes [self.tableView reloadData]; } #pragma mark - Observer - (void)storeDidChange:(NSNotification *)notification{ // Retrieve the changes from iCloud _notes = [[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:@"AVAILABLE_NOTES"] mutableCopy]; // Reload the table view to show changes [self.tableView reloadData]; } -
我们需要在表格视图中显示笔记。我们已经检索了保存在 iCloud 中的笔记;其余的实现是在表格视图中显示笔记。在
NoteListViewController.m中添加以下代码:- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.notes count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; NSString *note = [self.notes objectAtIndex:indexPath.row]; [cell.textLabel setText:note]; return cell; } -
我们现在已经到达了过程的最后部分:实现
AddNoteViewController以将笔记添加到云端。转到AddNoteViewController的实现文件,并添加以下代码:- (IBAction)cancel:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } - (IBAction)save:(id)sender { // Notify the previous view to save the changes locally [[NSNotificationCenter defaultCenter] postNotificationName:@"New Note" object:selfuserInfo:[NSDictionary dictionaryWithObject:self.noteTextField.text forKey:@"Note"]]; [self dismissViewControllerAnimated:YES completion:nil]; } -
为了测试笔记应用程序,我们必须编译并将其部署到实际设备上。如果您使用的是支持 iOS 7.1 或更高版本的模拟器,那么您将能够使用模拟器进行测试。确保您在两个设备上都启用了 iCloud。启动应用程序,在一台设备上添加笔记,您将在另一台设备上看到笔记出现。
密钥链服务
密钥链服务 是一个编程接口,允许开发者添加、查找、修改和删除密钥链项。在 iOS 和 OS X 中,密钥链是一个加密容器,用于存储需要安全保护的秘密数据,如密码和其他私人数据。在 iOS 中,每个应用程序都有自己的密钥链,它可以访问。这确保了我们的数据由第三方和其他用户得到保护。
密钥链提供了一个小空间,我们只能在其中存储特定的数据,如密码、账户号码、私人号码等。通过这篇文章,我希望说服你使用 iOS 和 OS X 中的密钥链而不是,例如,应用程序的用户默认数据库的价值,该数据库以纯文本形式存储其数据,没有任何安全形式。在密钥链中保存我们的数据比默认数据库更好,因为密钥链更加安全和健壮。
在 iOS 中,应用程序可以通过密钥链服务 API 使用密钥链。此 API 提供了多种功能来操作应用程序密钥链中存储的数据。API 如下:
-
SecItemAdd:此 API 用于在密钥链中添加数据 -
SecItemCopyMatching:此 API 用于在密钥链中查找现有数据 -
SecItemDelete:此 API 用于从应用程序的密钥链中删除数据 -
SecItemUpdate:此 API 用于更新应用程序密钥链中的数据
Keychain Services API 是一个基于 C 的 API,但这并不妨碍我们使用它。前面提到的每个函数都接受一个字典(CFDictionaryRef)。
加密和解密
我们大多数人知道两种加密类型:对称加密和非对称加密。对称加密一方面使用一个共享密钥来加密和解密数据。另一方面,非对称加密使用一个密钥来加密数据,并使用另一个单独但相关的密钥来解密数据。
在 iOS 中,为了加密和解密数据,有一个安全框架可用。这个过程在幕后进行,因此我们不会直接与这个框架交互。在我们的示例应用中,我们将使用对称加密。
安全框架提供了一些其他服务,例如随机化服务来生成加密安全的随机数;证书、密钥和信任服务来管理证书;公钥和私钥;以及信任策略。安全框架是一个在 iOS 和 OS X 中都可用且基于 C 的 API 的低级框架。
iOS 密钥链概念和结构
密钥链是一种安全且加密的方式来存储我们的宝贵数据。确保你的应用及其所有后续版本都由相同的移动配置文件签名非常重要。如果不是这样,你以后会遇到很多麻烦。
密钥链是在你的应用中存储的敏感数据单元。密钥链项附带一个或多个属性。属性描述了密钥链项,我们可以使用哪些属性取决于密钥链项的类。项类指的是我们打算存储的数据类型。这可以是一个用户名/密码组合、一个证书、一个通用密码等等。
理解应用流程
在我们开始构建应用之前,我们需要了解应用流程,这在此处解释:
-
当用户启动应用时,它会向用户展示一个用于登录的视图。
-
如果它还没有创建账户,其凭证将被添加到密钥链并登录。如果它有账户但输入了错误的密码,会显示错误消息。
-
一旦登录,它就可以访问应用收集的数据。应用会安全地存储这些数据。
-
每次它使用文本字段获取数据时,这些数据都会被加密并存储在应用中的
Documents目录下。 -
每次它切换到另一个应用或设备被锁定时,它会自动登出。
让我们在密钥链上开始一个活动。只需遵循以下步骤来完成活动:
-
打开 Xcode,创建一个单视图应用。然后,转到KeyChainSample | Capabilities并开启Keychain Sharing选项,如图所示:
![理解应用流程]()
-
打开钥匙串共享选项后,它将自动为我们创建权限文件,如下截图所示:
![理解应用程序流程]()
-
不要忘记添加安全框架(如下截图所示);没有它,钥匙串将无法工作:
![理解应用程序流程]()
-
现在,在故事板中创建一个如下所示的用户界面:
![理解应用程序流程]()
-
将一个新的 Objective-C 文件添加到项目的
NSObject子类中。然后,在新文件的接口文件中写下以下代码。同时,在文件顶部导入security.h:#import <security.h> @interface KeyChain : NSObject { NSString * service; NSString * group; } -(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_; -(BOOL) insert:(NSString *)key : (NSData *)data; -(NSData*) find:(NSString*)key;我们的用户界面文件将看起来像这样:
![理解应用程序流程]()
-
在我们添加的新文件的实现文件中写下以下代码:
@implementation KeyChain -(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_ { self =[super init]; if(self) { service = [NSString stringWithString:service_]; if(group_) group = [NSString stringWithString:group_]; } return self; } -(NSMutableDictionary*) prepareDict:(NSString *) key { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding]; [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric]; [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount]; [dict setObject:service forKey:(__bridge id)kSecAttrService]; [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible]; if(group != nil) [dict setObject:group forKey:(__bridge id)kSecAttrAccessGroup]; return dict; } -(BOOL) insert:(NSString *)key : (NSData *)data { NSMutableDictionary * dict =[self prepareDict:key]; [dict setObject:data forKey:(__bridge id)kSecValueData]; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL); if(errSecSuccess != status) { NSLog(@"Unable add item with key =%@ error:%ld",key,status); } return (errSecSuccess == status); } -(NSData*) find:(NSString*)key { NSMutableDictionary *dict = [self prepareDict:key]; [dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; [dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; CFTypeRef result = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result); if( status != errSecSuccess) { NSLog(@"Unable to fetch item for key %@ with error:%ld",key,status); return nil; } return (__bridge NSData *)result; }_bridge对象用于在 Objective-C 和核心基金会之间传递指针,不涉及所有权的转移。我们的接口文件可能看起来像这样:![理解应用程序流程]()
-
将 UI 组件连接到我们的
viewController.h文件。在文件顶部导入KeyChain(新添加的 Objective-C 文件),并在接口中创建KeyChain类的对象,如下所示:![理解应用程序流程]()
-
在
viewController.m文件中按钮的IBAction方法中添加一些代码,如下所示:#define SERVICE_NAME @"ANY_NAME_FOR_YOU" #define GROUP_NAME @"iOS" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; keychain = [[KeyChain alloc]initWithService:SERVICE_NAME withGroup:nil]; } -(void) showMessage:(NSString*)text { UIAlertView * alert =[[UIAlertView alloc ] initWithTitle:@"Info"message:text delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles: nil]; [alert show]; } - (IBAction)save:(id)sender { NSString *keyString =self.keyTextField.text; NSData *data = [self.dataTextField.text dataUsingEncoding:NSUTF8StringEncoding]; if([keychain insert:keyString :data]) { [self showMessage:@"Successfully added data"]; } else [self showMessage:@"Failed to add data"]; self.dataTextField.text =@""; self.keyTextField.text =@""; } - (IBAction)find:(id)sender { NSString *keyString =self.keyTextField.text; NSData * data =[keychain find:keyString]; if(data) { [self showMessage:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; } else { [self showMessage:@"Failed to get Data"]; } } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES; }我们的接口文件可能看起来像这样:
![理解应用程序流程]()
-
编译并运行项目;我们的模拟器看起来如下截图所示。输入值并点击保存按钮:
![理解应用程序流程]()
-
点击保存按钮后,将出现一个弹出窗口(如下截图所示),显示我们的数据已安全保存在钥匙串中。
![理解应用程序流程]()
-
现在请在钥匙串存储中存在的文本字段中写入Key值,然后点击查找按钮。它将以如下弹出消息显示数据:
![理解应用程序流程]()
Touch ID API
苹果在 iOS 7 中引入了一个名为Touch ID 身份验证的新功能。之前,iPhone 中只有四位数密码安全;现在,苹果扩展了安全性,并在 iPhone 中引入了一种新的安全模式。在 Touch ID 身份验证中,我们的指纹充当密码。去年,苹果在 iPhone 5S 上启动了 Touch ID 指纹识别技术后,现在它通过 iOS 8 为开发者提供这项技术。现在,第三方应用将能够在新的 iPhone 和 iPad OS 中利用 Touch ID 进行身份验证。会计应用和其他包含个人和重要数据的应用将受到 Touch ID 的保护。现在,你可以用你的指纹密码保护所有应用。

在我们的 iOS 8 应用中将 Touch ID 用作身份验证机制有两种方法。它们将在以下章节中解释。
通过触摸身份验证使用 Touch ID
本地身份验证 API 是一个返回布尔值的 API,用于接受和拒绝指纹。如果有错误,则执行错误代码并告诉我们问题所在。
使用本地身份验证时必须满足某些条件。它们如下:
-
应用程序必须在前台(此功能不适用于后台进程)
-
如果你使用的是直接的本地身份验证方法,你将负责处理所有错误,并使用你的 UI 正确响应,以确保有其他登录应用的方法
通过 Keychain Access 使用 Touch ID
Keychain Access 在 iOS 8 中包含了新的 Touch ID 集成。在 Keychain Access 中,我们不需要处理实现细节;它将自动使用用户的密码处理密码实现。可以通过使用新的访问控制列表(ACLs)选择多个密钥链项,通过代码请求使用 Touch ID 解锁项。ACL 是 iOS 8 的一个特性。如果 Touch ID 已被锁定,则允许用户输入设备的密码,无需任何中断地继续操作。
Keychain Access 有一些特性使其成为我们的最佳选择。它们在此列出:
-
Keychain Access 使用 Touch ID,并且其属性不会被任何云服务同步。因此,这些功能使其非常安全使用。
-
如果用户叠加了多个查询,则系统会混淆正确的用户,并会弹出包含多个触摸问题的对话框。
使用本地身份验证框架
苹果提供了一个名为本地身份验证的框架,用于在我们的应用中使用 Touch ID。此框架是为 iOS 8 引入的。为了创建一个包含 Touch ID 身份验证的应用,我们需要在我们的代码中导入此框架。它存在于苹果的框架库中。让我们看看如何使用本地身份验证框架:
-
按以下方式导入本地身份验证框架:
#import<localAuthentication/localAuthentication.h>此框架将在 Xcode 6 及以上版本中工作。
-
要使用此 API,我们必须创建一个本地身份验证上下文,如下所示:
LAContext *passcode = [[LAContext alloc] init]; -
现在,检查 Touch ID 是否可用以及是否可用于身份验证:
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError * __autoreleasing *)error; -
要显示 Touch ID,请使用以下代码:
- (void)evaluatePolicy:(LAPolicy)policy localizedReason:(NSString *)localizedReason reply:(void(^)(BOOL success, NSError *error))reply; -
查看以下 Touch ID 的示例:
LAContext *passcode = [[LAContext alloc] init]; NSError *error = nil; NSString *Reason = <#String explaining why our app needs authentication#>; if ([passcode canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { [passcode evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:Reason reply:^(BOOL success, NSError *error) { if (success) { // User authenticated successfully } else { // User did not authenticate successfully,go through the error } }]; } else { // could not go through policy look at error and show an appropriate message to user }
摘要
在本章中,你学习了如何将你的数据推送到 iCloud 以及如何将你的私人数据,如密码、账户号码、ATM 密码等,保存到 Keychain。我们还重点介绍了 iOS 8 中引入的 Touch ID API。在下一章中,你将学习如何将你的应用推送到 App Store。
第七章。应用分发计划
本章将帮助您了解成为 iOS 应用开发者解剖结构。我们将从无账户开始,到在 App Store 上发布。
在本章中,我们将涵盖以下主题:
-
理解和设置您的开发者账户
-
设置配置文件
-
在商店发布应用
设置开发者账户
在本章中,您将学习如何注册苹果的 iOS 开发者计划,如何生成所需的各个证书,配置我们的应用的方法,以及最终将应用提交到 App Store 进行审核的步骤。为了本章,我们创建了一个全新的 App Store 账户,并且稍后我们还向苹果的 App Store 提交了一个新的应用。
我们将提交我们在 第四章 中创建的 AntKiller 游戏应用改进版本,使用 SpriteKit 游戏引擎。
完成本章所需的先决条件如下:
-
您需要一个网络浏览器
-
您需要一个有效的信用卡
-
您还需要 $99。
-
开发应用必须使用 Mac 电脑,最好安装了 OS X 10.9 Mavericks 操作系统。
-
最后,您将需要 Xcode 6,这是苹果的开发软件;您可以从
developer.apple.com/downloads/index.action?name=Xcode下载它。
在您注册为苹果 iOS 开发者后,可以下载 Xcode。完成本教程的前几节后,您将注册到苹果开发者计划。
开始使用
在苹果的开发者门户上注册是我们将应用发布到 App Store 的第一步。要成为 iOS 开发者,您必须支付上述 $99 美元费用,或者支付 $299 美元的企业账户费用。然而,对于企业账户,在注册账户之前,您需要一个额外的 DUNS(数据通用编号系统)编号。
如果您已经拥有苹果的开发者账户,请随意跳过本节,继续下一节。如果您没有苹果开发者账户,请访问 iOS 开发中心 (developer.apple.com/devcenter/ios/index.action) 并点击以下截图所示的 免费注册 链接:

点击如图中红色高亮框所示的 开始 按钮:

在这个页面上,您可以选择创建一个新的 Apple ID,或者使用现有的 Apple ID。如果您想节省时间,可以使用您已经用于 iTunes 账户购买的相同 Apple ID,例如。然而,为了遵循本章的步骤,我们建议您创建一个新的、全新的 Apple ID。这将帮助您确保所有数据都是正确的。
因此,要创建一个新的 Apple ID,请选择如图所示的创建 Apple ID:

使用以下截图所示的表格填写所有必需的详细信息,例如您的电子邮件、密码和安全信息。建议您使用经常检查的电子邮件地址,因为 Apple 会发送您开发者计划的所有更新以及您提交的待批准应用程序的状态。

通过向下滚动填写所有个人信息,然后点击创建 Apple ID,如图所示:

下一页包含与您的专业资料相关的问题。此页面还会询问您开发的应用程序等。根据您的最佳知识填写所有答案。向下滚动并完成以下表格,然后点击继续:

继续按钮
下一页是法律协议。阅读文档,选择复选框,然后点击我同意,如图所示:

法律协议
现在,注册已完成。只需验证您用于注册的电子邮件地址。您应该会收到一封确认电子邮件地址的邮件。点击以下截图所示的 Apple 注册确认邮件中发送的链接:

恭喜!您现在已成为 Apple 开发者。在您开始开发 iOS 应用程序并将它们提交到 Apple 的 App Store 之前,您需要点击继续(如图所示)并购买 iOS 应用程序开发计划:

继续按钮
加入 iOS 开发者计划
使用您的开发者 Apple ID 可为您提供访问许多资源和信息。然而,它仍然不允许您在 Apple 的 App Store 上发布应用程序。要获得更多访问 Apple 应用发布功能的权限,您必须注册 Apple 的 iOS 开发者计划。这反过来将每年花费您 99 美元,
在前一个屏幕上点击继续后,您将被重定向到正确的位置。否则,如果您已经拥有 Apple 开发者账户并选择跳过前面的部分,请转到开发者会员中心,登录,您将保持同步。
登录后,只需点击页面右上角的加入今日链接,如图所示:

加入今日链接
然后,点击以下截图中的继续:

继续按钮
现在,选择现有 Apple 开发者并点击继续,如图所示:

选择现有苹果开发者选项和继续按钮
在下一页,我们需要决定我们是想作为个人还是作为公司注册。在本章中,我们将作为个人注册,如图所示。作为公司注册要复杂一些,因为它需要您提交更多文件来证明您在公司中的参与。

个人按钮
现在,当您点击个人时,下一个屏幕将是登录屏幕。在这里,您将需要再次提供您新创建的凭据。之后,点击登录。
现在,在登录后的下一个屏幕中,您将需要提供您的账单信息以验证您的身份,如图所示。为了确保您已提供正确的卡信息,苹果会尝试与您的信用卡公司确认此信息。请始终确保您提供了正确的详细信息。

通过向下滚动填写完整的表格,然后点击继续,如图所示:

继续按钮
点击继续将重定向您到计划选择页面。现在,我们必须选择一个计划。在本章中,我们的主要重点是 iOS 开发者计划,因此我们只将选择此选项。
在选择您的计划后,点击继续,如图所示:

继续按钮
现在,一旦您点击了继续,您将需要审查您的信息以纠正任何可见的错误(如有必要)。审查完毕后,您可以通过点击继续来提交,如图所示:

现在,接受许可协议并点击我同意,如图所示:

许可协议
您将被重定向到以下截图所示的支付信息页面。填写与您的支付和送货地址相关的所有详细信息,然后点击继续。

支付信息页面
现在,按照支付程序进行操作;一旦成功支付 99 美元,您将被重定向回苹果支付确认页面,如图所示:

支付确认页面
开始使用 iOS 开发者账户
一旦您提交并支付了您的 iOS 开发者注册流程,您将不得不等待一天左右,以便苹果处理您的订单。
一旦您的订单成功处理,您将收到来自苹果公司的电子邮件,如图所示:

您还将收到一封关于您的iTunes Connect账户的电子邮件,如图所示:

现在,如果您还没有这样做,可以使用您新的 iOS 开发者凭证下载 Xcode,这是主要用于 iOS 开发的工具。我们将简要使用 Xcode 来了解将应用程序发布到 App Store 的过程。
现在,前往iOS 开发中心(developer.apple.com/devcenter/ios/index.action),并点击登录,如图所示:

使用您的凭证登录后,您最终将观察到变化。现在,iOS 开发中心为您提供了大量信息。您将找到各种编程指南、下载、全球开发者大会(WWDC)视频和文档;最重要的是非常有帮助的开发者论坛和支持中心。
在本章中,我们主要关注在开发 iOS 应用程序时将广泛使用的两个门户:iOS 配置文件门户和iTunes Connect,如图所示:

iOS 配置文件门户
事实上,未越狱的 iOS 设备只能安装和运行苹果公司批准并在 App Store 上可用的应用程序。
苹果公司为此进行了特殊的安全检查;在 iOS 设备上运行的每个应用程序都必须有一个签名的 Apple 证书。然而,对于 App Store,应用程序附带一个捆绑的证书,系统在允许应用程序运行之前会对其进行验证。如果没有找到签名,则认为应用程序无效,系统拒绝运行此类应用程序。
开发者在开发过程中必须非常频繁地编译和运行他们的应用程序。为了在开发过程中编译和运行应用程序,开发者需要一种方法来使用他们自己的证书创建和签名他们的应用程序。
这就是 iOS 配置文件门户发挥作用的地方。您可以使用此门户生成您的配置文件。配置文件也可以理解为“代码签名标识”。这些文件由 iOS 配置文件门户生成,并由 Xcode 用于签名您的应用程序。
了解两种类型的配置文件类型:开发配置文件和发布配置文件非常重要。它们在这里进行解释:
-
开发配置文件:这些配置文件与特定的设备相关联,因此应用程序只能在这些设备上运行。开发者可以在开发者门户中添加他们的设备 UDID;这将允许配置文件在设备上安装应用程序时通过验证。
-
分发配置文件:这些配置文件专门用于在您将应用提交给苹果公司进行审核之前对应用进行签名。这些配置文件不包含任何设备特定的详细信息;因此,您自己不能使用此配置文件在设备上安装应用。这些配置文件是无设备配置文件,因为苹果公司在批准应用后将对应用进行签名并发布给所有用户。
这些配置文件门户也用于生成推送通知证书,如果您希望您的应用发送它们。
iTunes Connect
iTunes Connect 门户控制您的应用提交流程。在此门户中,您将注册您的新的应用及其描述、元标签、截图、价格和在应用内购买,以便将它们发布到 App Store。
这还允许您同意新的合同。您还可以在此门户中设置您的财务数据并检查您的销售。此门户提供了对各种分析工具的访问,例如崩溃报告、下载信息等。
证书、标识符和配置文件
在本节中,我们将探讨证书、标识符和配置文件,以设置我们的应用部署流程的配置文件;此应用将在稍后发布到 App Store。现在,点击证书、标识符 & 配置文件,如图所示,或点击链接(developer.apple.com/account/overview.action)。结果截图如下:

点击证书,您将看到类似于以下截图的内容:

现在,点击+按钮创建一个新的证书,此页面将询问您想创建哪种证书。从开发部分选择iOS 应用开发证书,然后点击继续,如图所示:

现在,按照以下证书生成页面中指定的流程进行操作,并点击继续:

现在,使用列出的说明上传您刚刚创建的.certSigningRequest文件。页面应类似于以下截图:

现在,点击生成将创建一个开发证书;这可能需要几分钟。一旦证书创建完成,您将能够在证书列表中看到它,如图所示:

点击证书以下载或撤销。结果截图如下:

现在,我们已经成功创建了证书。是时候将其添加到 Xcode 中了。打开 Xcode,转到文件 | 偏好设置。现在,点击屏幕顶部的图标中的帐户。这应该看起来与以下截图类似:

现在,您可以看到,目前还没有与 Xcode 同步的帐户。因此,点击屏幕左下角的+按钮,并添加之前步骤中创建的帐户。帐户弹出窗口应该看起来与以下截图类似:

登录成功后,您将能够在列表中看到您的帐户和相关配置文件,如图所示:

现在,让我们回到我们的 Apple 开发者门户中的浏览器。下一步将是注册我们的设备。
现在打开浏览器,让我们继续。下一步是注册我们的设备。在左侧菜单中,点击设备并选择所有。然后,在右侧,点击+按钮以添加设备,如图所示:

要添加设备,您需要运行您应用的设备(们)的 UDID。您可以通过 Xcode 的组织者或 iTunes 获取设备的 UDID。您可以打开 iTunes 并点击摘要以获取设备的 UDID,如图所示:

摘要按钮
现在,我们已经拥有了添加设备所需的所有信息。点击+按钮,并提供设备的 UDID 以及设备名称。在提供详细信息后,点击继续,如图所示:

通过点击如图所示的注册来确认提交。您可以在列表中添加最多 100 台设备以调试您的应用。

现在,我们已经成功注册了设备,所以是时候注册应用 ID 了。我们构建的所有应用都有一个唯一的 app ID。点击屏幕左侧的App IDs以注册您的新 app ID。点击+按钮,并填写如图所示的所需详细信息。您还可以从屏幕顶部的 Apple 帮助文本中阅读关于 app ID 的完整描述。

填写所有必要的详细信息,并点击继续。成功创建后,应用 ID 将自动显示在列表中。
现在,是最后一步了。我们将创建一个配置文件,它将实际捆绑我们在上一节中遵循的所有步骤。配置文件将开发者的证书与应用 ID 绑定,以便允许应用在配置文件中允许的设备上运行。遵循创建证书和标识符时使用的相同步骤,点击配置文件部分,并选择全部。然后,点击屏幕右上角的+按钮。然后,在表单中,在开发部分下选择iOS 应用开发。您应该会看到以下截图:

现在,选择您在上一节中创建的应用 ID,并点击继续。选择屏幕将类似于以下截图所示:

然后,在下一屏,选择此配置文件可用的 iOS 证书列表,并点击继续。选择屏幕应类似于此处所示:

然后,我们将为创建的配置文件添加设备,此配置文件将可用。然后,点击继续。选择屏幕应类似于以下截图。检查您想要在设备上运行应用的所有设备。

此过程完成后,您的配置文件将成功创建。现在,是时候创建分发配置文件了,该配置文件将用于在 App Store 上部署您的应用。要创建分发配置文件,点击左侧面板中配置文件菜单中的相同+按钮。
现在,检查页面设置,如图所示:

要创建分发配置文件,我们将从分发部分选择App Store。现在,点击继续,在下一页,选择我们在上一节中创建的应用 ID。此分发配置文件将绑定到所选的特定应用 ID。
现在,在下一页,选择证书并点击继续。
这将使您进入下一页,该页将要求输入配置文件的名称。只需为您的分发配置文件提供一个名称,然后点击生成,如图所示:

现在,您的分发配置文件已准备好在 App Store 上部署应用。现在是时候将配置文件和证书链接到 Xcode 了。因此,打开 Xcode 并转到文件 | 偏好设置。
然后,点击本章前几节中添加的账户。点击查看详情...按钮,如图所示:

现在,你可以看到我们在前面的部分中创建的所有配置文件和证书,在列表中(如下所示):

现在,你可以通过在设备上运行你的应用程序来测试你的开发配置文件。只需将设备连接到你的系统,并在检测到后从 Xcode 中选择它。现在,你可以通过点击 Xcode 中的 播放 按钮在设备上运行你的应用程序。
太棒了!所以,我们已经拥有了发布应用程序所需的一切。现在,打开你的 iTunes Connect 账户并接受所有联系人以启动账户。它还会要求你提供银行详细信息,以便将你的利润转入。
提交应用程序
为了提交你的应用程序,你需要以下项目。在我们开始之前,我们需要确保我们拥有所有这些:
-
应用程序名称
-
应用程序的描述
-
应用程序的图标,尺寸为 512px x 512px
你至少需要一张应用程序的截图。截图的大小应为 640 × 920(视网膜,无状态栏);640 × 960(视网膜);或者对于横幅,960 × 640(视网膜)。
我们需要将这些内容组装起来,然后在 iTunes Connect 中点击 管理您的应用。现在,点击 iTunes Connect 中的 + 按钮,结果屏幕如下所示:

按照以下截图所示填写应用程序的所有必填字段:

选择为我们应用程序创建的捆绑 ID,并同样填写所有必填字段。一旦你点击 创建,你将被带到下一页(如下所示)并要求提供更多关于应用程序的信息;这些信息包括截图、视频(如果有)、应用程序的描述以及与你的应用程序相关的类别信息。

你可以在前面的截图所示的任何选项卡中选择和更新详细信息,并在提供完整信息后点击 保存。
使用 Xcode 提交
现在,是时候使用 Xcode 提交应用程序了,所以打开 Xcode 并转到 产品 | 存档。
一旦存档完成,点击 验证。这将检查你的二进制文件的验证情况,并列出所有问题(如果有);如果没有问题,它将表示验证成功。现在,点击 提交 按钮将应用程序提交到 App Store,如下所示。这将要求你选择 iOS 分发配置文件以上传二进制文件到 App Store。选择我们在前面的部分中创建的配置文件,然后点击 提交。现在,你的应用程序将开始上传到 Apple Store。

一旦二进制文件成功上传,你将收到一条成功消息。消息将类似于以下截图所示:

点击完成。现在,你可以打开你的 iTunes Connect 账户并检查你的二进制文件是否已接收。你还可以看到将几分钟后变为等待审核的上传接收消息。

上传接收消息
恭喜!你已经成功将你的应用提交到 App Store!
摘要
在本章中,你学习了如何设置并理解你的开发者账户。你还学习了如何设置配置文件以在商店发布你的应用,我们讨论了使用苹果企业账户进行企业级分发的结构。

































































































浙公网安备 33010602011771号