创设你的第一个iOS应用
创建你的第一个iOS应用
原文出处:iOS Developer Library
翻译:WXJ_Lake
也许是语言障碍,有一些程序员想学习iOS编程却一时间没有头绪。其实Apple的文档堪称傻瓜型,Xcode也很好用。对于初学者,最好的捷径是阅读大量经典代码,从头培养良好的编码习惯。而不是迷失在一大堆功能实现的代码堆砌中走火入魔。官方文档从一开始就强调面向对象编程,是很好的学习材料。希望这篇粗劣的译文能帮助大家更快入门。
对于专有名词我尽量保留不翻译。可能降低可读性请多包涵。
博客无图片,下载Page文件
创建你的第一个iOS应用
本文将向你介绍iOS应用开发的三个T:
- Tools工具。如何使用Xcode创建并管理一个项目。
- Technologies技术。如何建立一个能响应用户输入的应用。
- Techniques技巧。如何利用那些隐藏在所有iOS应用背后的基本设计模式。
在完成了本教程的所有步骤后,你将会拥有一个这样的应用:
如上所见,你创建的应用中有三个主要的用户界面元素:
- 一个文本框text field,用户可以在其中输入信息
- 一个标签label,信息被显示在这里
- 一个按钮button,触发应用将信息显示到标签中
当这个应用运行时,你点击文本框即可调出系统提供的键盘。用键盘输入姓名后,点击完成(Done)键隐藏键盘,然后点击Hello按钮可以看到字符串“Hello, 你的名字”显示在文本框和按钮之间的标签里。
为了更好理解本文,你最好已经对计算机编程基础有所了解,特别是面向对象编程和Objective-C语言。如果你从未使用过Objective-C,本文的代码可能难以理解,但不用担心。只要读完Start Developing iOS Apps Today,你就能更好地理解代码了。
注意:即使你仅打算为iPad开发,你也可以通过本文开始学习iOS应用开发。虽然本文展示的都是iPhone用户界面,但用到的工具和技术与iPad应用开发是完全一样的。
开始
要创建本文描述的iOS应用,你需要安装Xcode 4.3或更新版本。Xcode是Apple公司iOS和Mac OS X的集成开发环境(IDE) 。一旦你在Mac上安装了Xcode,包含iOS平台编程界面的iOS SDK同时被安装了。
创建并测试一个项目
要开始开发应用,按如下步骤创建一个新的Xcode项目。
创建新项目 . . .
-
打开Xcode (它默认安装在 /Applications 下)。
如果你从未创建或打开Xcode,你将会看到这样一个欢迎窗口:
如果你之前在Xcode中创建或打开过项目,那你可能看到的是一个项目窗口。
-
在欢迎窗口里点击 "Create a new Xcode project"(或者选择菜单File > New > Project)。
Xcode打开一个新窗口显示可选择模版的对话框。Xcode内建数个应用模版,你可以用来开发通常样式的iOS应用。例如Tabbed模版可以创建一个界面类似于iTunes的应用,而Master-Detail模版可以创建界面类似于Mail的应用。
-
在对话框左边的iOS栏中,选择Application。
-
在对话框右侧选择Single View Application并点击Next。
出现一个新的对话框提示输入应用的名称以及一些附加的选项。
-
填入Product Name, Company Identifier, 以及Class Prefix字段。
你可以输入以下值:
- Product Name: HelloWorld
- Company Identifier: 你的公司定义符,如果你有的话。如果没有,你可以用edu.self。
- Class Prefix: HelloWorld
-
注意:Xcode用product name来命名你的项目和应用。Xcode用class prefix类前缀来命名它为你创建的所有类。例如,Xcode自动创建一个应用代理类并命名为HelloWorldAppDelegate。如果你在
class prefix 中输入不同的值,应用代理类将被命名为YourClassPrefixNameAppDelegate。(你可在文章"Find Out How
an App Starts Up."中了解跟多关于应用代理的知识。)
为了保持简单,本文假设你输入的名称是 HelloWorld, class prefix 是HelloWorld。
-
在弹出菜单Device Family中,确认选择了iPhone。
-
确认选择了Use Storyboards和Use Automatic Reference Counting选项,不要选Include Unit Tests选项。
-
点击Next。
出现另一个对话框,指定将项目保存到何处。
-
为你的项目指定一个目录(保持 Source Control不选),然后点击Create。
Xcode在新窗口(称为 workspace窗口)中打开项目,看上去如下图:
稍许花点时间熟悉一下这个workspace窗口。你将在本文余下部分用到这个窗口中的按钮和区域。
如果 workspace窗口右侧的utilities区域是打开的(如上图),你现在可以现在尝试关闭它。最右侧的View按钮控制着utilities区域。utilities区域可见时,按钮看上去如下:
如果需要,可以点击最右侧的View按钮关闭这个区域。
即使你还没有写任何代码,你已经可以编译这个应用并在Xcode自带的模拟器中运行它了。顾名思义,模拟器让你快速了解应用在真机上运行的表现。
在模拟器上运行应用 . . .
-
确认Xcode工具栏上的Scheme弹出菜单选择了HelloWorld > iPhone 5.0 Simulator。
如果弹出菜单没有显示那个选项,那就从菜单中选择iPhone 5.0 Simulator。
-
点击Xcode工具栏上的Run按钮(或选择菜单Product > Run)。
Xcode在工具栏中间的activity viewer中显示编译过程的消息。
Xcode完成编译后,模拟器将会自动启动(你可能要等几秒钟)应为你指定了iPhone产品(而不是iPad铲平),模拟器显示一个iPhone窗口。在模拟的iPhone屏幕上,模拟器打开你的应用,如下图:
到目前为止,你的应用还不是很有趣:它只是显示一个空白屏幕。为了理解这个空白屏幕哪里来的,你需要进一步学习代码中的对象,以及他们如何一起工作而启动应用的。现在,退出模拟器(选择菜单iOS Simulator > Quit iOS Simulator; 确认不要退出Xcode)。
当应用运行时,Xcode可能打开了 workspace窗口底部的Debug区域。在本文中你用不到这个面板,所以你可以关闭它以便留出更多空间。
关闭Debug区域 . . .
-
点击工具栏上的Debug View按钮。
Debug View按钮是中间的View按钮,如下图: .
理解应用如何启动
由于你在一个Xcode模版上建立了项目,许多基本的应用环境在运行时已经自动设立了。例如,Xcode创建了一个application对象,它在其它几个对象之间建立了运行循环(一个运行循环注册了输入来源并接收应用的输入事件)。大多数工作由UIApplicationMain函数完成(来自UIKit框架)并且在项目的main.m源文件中自动调用了。
注意: UIKit框架提供一个应用构建和管理它的用户界面所需的所有类。UIKit框架只是Cocoa Touch(iOS应用环境)提供的许多面向对象框架中的一个。
查看main.m源文件 . . .
-
确认 navigator区域中打开了project navigator。
所有项目文件均显示在project navigator中。如果project navigator没有打开,点击 navigator选择条中最左边的按钮:
-
点击小三角展开Supporting Files目录。
-
选择main.m.
Xcode在main editor区域打开源文件,如下图:
main.m 中的main函数在一个自动释放内存池中调用UIApplicationMain函数:
@autoreleasepool {
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([HelloWorldAppDelegate class]));
|
}
|
@autoreleasepool 语句支持自动引用计数 (ARC) 系统。ARC为应用提供自动的对象生命周期管理,确保对象在它们所需时存在,不需时消失。
对UIApplicationMain的调用创建了一个UIApplication 类的实例,和一个应用托管的实例(在本文中,应用托管是 HelloWorldAppDelegate,由Single View模版提供)。应用托管(app delegate) 的主要任务是提供绘制应用内容的window。应用托管还在应用显示出来之前进行一些应用配置任务。(托管Delegation 是一种设计模式,指某个对象代表另一个对象工作,或与另一对象协同工作。)
在 iOS应用中,一个window 对象提供这个应用可视内容的容器,帮助向应用对象传递事件,并且帮助应用响应设备朝向的变化。window 本身是不可见的。
对 UIApplicationMain 的调用也同时扫描应用的Info.plist 文件。Info.plist文件是一个属性列表(即一个由键值对构成的列表),包含这个应用的信息,如名称、图标等。
查看属性列表文件 . . .
-
在project navigator的Supporting Files目录中,选择HelloWorld-Info.plist。
Xcode会在 editor area 打开Info.plist文件,如下图所示:
在本文中你不再须要查看 Supporting Files 目录下任何其它文件了,所以你可以在 project navigator 中关闭这个目录。再次点击小三角即可。
应为你在本项目中选择使用storyboard,Info.plist文件也包含 application 对象须要载入的storyboard文件名。storyboard 是一个包含了对象、迁移,和连接的集合,它定义了整个用户界面。
在HelloWorld应用中,storyboard文件命名为MainStoryboard.storyboard (注意Info.plist 文件仅显示这个文件名的前面一部分)。当应用启动时,MainStoryboard.storyboard 被加载,同时initial view controller从中被实例化。view controller 是一个管理内容区域的对象;initial view controller 即是应用启动时的第一个view controller。
HelloWorld应用仅包含一个view controller(特指,HelloWorldViewController)。现在,HelloWorldViewController 管理由一个view提供的内容区域。view对象在屏幕的一块矩形区域中绘制内容,并处理用户触摸产生的事件。一个view对象也可以包含其它view,称为subview。当你向view添加一个subview,作为容器的view称为parent view,它的subview称为child view。parent view, child view (以及它们的child views, 如果有的话) 组成一个view hierarchy。一个view controller对应管理一个view hierarchy.
注意: HelloWorld应用的view和view controller代表了MVC (Model-View-Controller)设计模式定义的三个应用对象角色中的两个。第三个角色是model对象。在MVC中,model对象代表数据 (就像一个日历应用中的to-do,或是在一个画图应用中的形状),view对象知道如何显示model对象代表的数据,controller对象则介于model和view之间。在HelloWorld应用中,model对象是存储用户输入名称的字符串。你现在不必了解MVC太多,但从现在开始就思考应用中的对象如何扮演这些角色是有好处的。
在后面的步骤,你将会通过向view添加三个subview创建一个view hierarchy,它们受HelloWorldViewController的管理; 这三个subview分别代表文本框,标签,按钮。
你可以从 storyboard 中看到view controller和它的view。
查看storyboard . . .
-
在 project navigator 中选择 MainStoryboard.storyboard。
Xcode在editor区域中打开storyboard。(在storyboard对象后面的区域—也就是看上去象绘图纸的区域—称为canvas。)
当你打开默认的storyboard时,workspace窗口应该看上去如下图:
storyboard包含scene和segue。scene代表一个view controller,segue 代表两个scene之间的迁移。
由于Single View模版只提供一个view controller,现在你的应用只含有一个scene,而没有segue。在canvas上指向scene左侧的箭头是initial scene indicator,定义了须要应用启动时须要首先加载的scene。(通常,initial scene和initial view controller是一样的。)
你在canvas上看到的scene命名为Hello World View Controller,因为它正是由HelloWorldViewController 对象管理的。Hello World View Controller scene由一些显示在Xcode outline view中的项目组成。(outline view 就是出现在canvas和project navigator之间的面板)。到目前为止,view controller包含以下项目:
- 一个first responder占位符(一个橙色立方体)。所谓first responder 是一个动态占位符,它代表了在应用运行时首先接收各种事件的对象。这些事件包括编辑框焦点事件(比如点按文本框带出键盘),运动事件(比如摇晃设备),操作消息(比如用户按按钮发送的消息),等等。本文中你无需对first responder做任何修改。
- HelloWorldViewController 对象(黄色球形中一个灰白色方块)。当storyboard加载scene时,它创建一个view controller类的实例来管理这个scene。
- view被罗列在view controller之下(要在outline view中看到这个view,你可能须要展开 Hello World View Controller旁的小三角)。view的白色背景就是你运行应用时的样子。
注意:应用的window对象并不在storyboard中显示。
scene下方的区域称为scene dock。现在,scene dock显示了view controller的名称(也就是Hello World View Controller))。在其它时候,scene dock还可以包含代表first responder和view controller对象的图标。
回顾
在本章中你使用Xcode基于 Single View 模版创建了一个项目,并且编译和运行了模版定义的默认应用。然后你查看了项目的一些基本内容,比如main.m 源文件,Info.plist 文件和storyboard 文件,而且学习了应用是如何启动的。你还Model-View-Controller设计模式如何定义应用中对象的角色。
下一章,你将学到更多view controller和view的知识。
探究View Controller和View
如早先所学,一个view controller负责管理一个scene,后者代表了一个内容区域。你在这个区域中所见的内容即是在这个view controller的view中定义的。本章将带你进一步查看 HelloWorldViewController管理的scene,并学习如何修改它的view的背景颜色。
使用inspector来查看View Controller
当应用运行时,系统加载主storyboard文件,并实例化initial view controller。initial view controller管理着用户打开应用看到的第一个scene。由于Single View 模版只提供一个view controller,它被自动设为initial view controller。你可以通过Xcode的 inspector 检验一下view controller的状态,从而全方位了解它。
打开 inspector . . .
-
点击 project navigator中的 MainStoryboard.storyboard 以便在canvas上显示scene。
-
在outline view中选择Hello World View Controller(在First Responder下方)。
workspace窗口如下图:
注意scene和scene dock都出现了蓝框,并且scene dock中的view controller对象被选中。
-
点击工具栏中最右侧的View按钮, utilities区域显示在窗口右侧(或者选择菜单View > Utilities > Show Utilities)。
-
点击utilities区域的 Attributes inspector按钮即可打开。
在顶部的 inspector selector bar中,Attributes inspector按钮是自左边起第四个按钮:
Attributes inspector打开后,你的workspace窗口看起来如下图:(你可能须要最大化窗口来显示所有公司)
在 Attributes inspector的 View Controller栏目中,你可以看到Initial Scene选项已被选中:
注意如果你取消这个选项,initial scene indicator将从canvas上消失。在本文中,请确认Initial Scene选项选中。
更改View的背景颜色
在本文前段提到view提供了应用运行时的白色背景。为了确保应用的正确运行,你可以将view的背景颜色设为其它颜色,并再次运行应用以检验是否成功。
在修改颜色之前,请确认storyboard依旧在canvas中打开着。(点击 project navigator中的 MainStoryboard.storyboard 可打开storyboard。)
设置view的背景颜色 . . .
-
在outline view中展开Hello World View Controller,选择View。
Xcode会在canvas上高亮显示view区域。
-
点击 utilities顶部 inspector selector bar 中的Attributes按钮,打开Attributes inspector。
-
在Attributes inspector中,点击Background弹出菜单的白色方框,打开Colors窗口。
方框显示了当前背景颜色。Background弹出菜单如下图:
注意:如果不点击白色方框,你也可以点击Default打开弹出菜单,选择菜单中的Other。
-
在Colors窗口中,选择一个与白色不同的颜色。
workspace窗口(和Colors窗口)如下图:
注意由于Xcode会高亮显示选中的view,canvas上的颜色可能看起来与Colors窗口中的不同。
-
关闭Colors窗口。
点击Run按钮(或选择菜单Product > Run)来测试你的应用。请确认Xcode工具栏上的 Scheme 弹出菜单选中了HelloWorld > iPhone 5.0 Simulator。 你将会看到如下屏幕显示:
提示 你不必在运行应用前保存工作,因为当你 点击Run按钮(或选择菜单Product > Run)时,Xcode会自动保存所有已修改的文件。
在继续本文之前,请把view的背景颜色恢复成白色。
恢复view的背景颜色 . . .
-
在Attributes inspector中,点击箭头打开Background弹出菜单。
注意Background弹出菜单的方框已经变为显示你所选择的颜色。如果你点击这个颜色方框(而不是箭头),将再次打开Colors窗口。既然你打算重新使用view的初始颜色,显然从Background弹出菜单中选择要比从Colors窗口再次查找颜色来得方便。
-
在Background弹出菜单中选择排列在Recently Used Colors栏目中的白色方框。
- 点击Run按钮来编译并运行应用(并保存改变)。
验证应用确实显示回白色后,关闭模拟器。
回顾
在本章中你进一步探究了scene,修改(并恢复)了view的背景验色。
在下一章中,你将向view添加文本框,标签和按钮,制作一个可以进行交互的应用。
配置View
Xcode提供了一个可以添加到storyboard文件中的对象库。其中有些是属于view内的用户界面元素,比如按钮和文本框。另一些则是高级对象,比如view controllers和手势识别 (gesture recognizer)。
Hello World View Controller scene已经包含一个view—现在你需要添加一个按钮(button),一个标签(label),和一个文本框(text field)。然后你将连接这些元素和view controller类,使所有这些元素具备你所需的表现。
添加用户界面元素
你可以通过将用户界面 (UI) 元素从对象库拖放到canvas的view来添加它们。将UI元素放入view后,你可以任意拖动和缩放它们。
向view添加UI元素并合理编排它们 . . .
-
在project navigator 中选择 MainStoryboard.storyboard,在canvas上显示Hello World View Controller scene。
-
打开对象库(object library)。
对象库在utilities区域底部。如果看不到,你可以点击它的按钮,就是 library selector bar 的左起第三个按钮:
-
在对象库的 Objects弹出菜单中选择控件。
Xcode 在弹出菜单下显示一个控件列表。这个列表显示了每个控件的名称和外观,以及功能的简要描述。
-
从列表中将文本框,圆角按钮,和标签一个一个拖放到view中。
-
在view中,将文本框拖放到view的左上角。
当你移动文本框时(或其它任何UI元素),将会出现蓝色虚线—称为 alignment guides —来帮助你将元素居中对齐或靠边对齐。尝试在 alignment guides 辅助下把文本框放在左上角,如下图:
-
准备在view中缩放文本框。
你可以拖放UI元素的resize handles来缩放它,就是出现在元素边沿上的白色小方块。通常来说,在canvas或outline view中选中一个元素即可激活它的resize handles。在这个项目中,文本框如果和下图一样被选中,你就可以缩放它了;如果不是这样,那就到canvas或outline view中选中它。
-
拖放右边的resize handle直到它顶到最右边的alignment guide。
将文本框调整成如下图所示:
-
仍然选中文本框,打开Attributes inspector。
-
在 Text Field Attributes inspector顶部附近的Placeholder字段中输入文本Your Name。
顾名思义,Placeholder字段提供一个浅灰色的文本以帮助用户理解应当在文本框中输入什么类型的数据。在运行的应用中,一旦用户点按了文本框,placeholder文本就消失了。
-
仍旧在Text Field Attributes inspector中点击居中对齐按钮。
在输入placeholder文本并改变对齐设置后,Text Field Attributes inspector应当看起来如下图:
-
在view中拖放标签控件,将它与文本框左边对齐。
-
拖放标签右边的resize handle直到标签和文本框一样宽。
标签比文本框具有跟多的resize handles,因为你可以同时调节标签的高度和宽度(文本框只能调节宽度)。如果不想改变标签的高度,应当避免拖放标签四角上的resize handles,而是拖放标签右边中间的resize handle。
-
在Label Attributes inspector中点击居中按钮,使标签中显示的文本居中。
-
将按钮拖放到view底部附近,并使之水平居中。
-
在canvas上双击按钮,输入文本 Hello。
当你在view上双击按钮(输入文本前),你应当看到如下图所示:
你已经添加了文本框,标签和按钮的UI元素,并各自摆放到位。你的项目大致看上去如下图:
还可以对文本框做一些修改以让它的表现符合用户期望。首先,因为用户将要输入他们的名字,你可以让iOS对每个单词建议首字母大写。其次,你可以配制与这个文本框绑定的键盘只用于输入名字(而不是数字),而且键盘上显示一个Done按钮。
这些修改背后的原则在于:你在设计时即可明确文本框中应当包含什么类型的信息,你可以配置好以便它的运行时表现能更加符合用户的任务需求。在 Attributes inspector 中可以完成所有这些配置。
配置文本框 . . .
-
在view中选择文本框。
- 在Text Field Attributes inspector中进行如下选择:
- 在Capitalization弹出菜单中选择 Words。
- 确认Keyboard弹出菜单设为Default。
- 在Return Key弹出菜单中选择Done。
-
以上设置完成后,Text Field Attributes inspector如下图所示:
在模拟器中运行应用,确认添加的UI元素符合你的期望。如果你点击了Hello按钮它会高亮显示,如果你点击了文本框,键盘就会出现。然而到目前为止这个按钮什么也不做,标签仅仅显示"Label",而且一旦键盘出现就没有办法取消它。要添加功能,还需要将UI元素和view controller恰当连接起来。下文将描述如何建立连接。
注意:因为你是在模拟器上运行应用,不是真实设备,你只能通过鼠标点击而不是手指点按它们。
创建按钮的动作(Action)
当用户激活一个UI元素,这个元素将向一个知道如何执行相应动作方法的对象发送action消息(比如“将这个联系人添加到用户的联系人列表中去”)。这种交互属于目标-动作(target-action)机制的一部分,后者是另一种 Cocoa Touch设计模式。
在本文中,当用户点按Hello按钮,你希望它向 view controller(目标)发送一条“更新问候语”消息(动作)。这条消息更新 view controller管理的字符串(即model对象)然后,view controller更新标签显示的文本以反映model对象的值。
使用Xcode,你可以通过将控件从canvas上Control-drag到合适的源文件(通常就是 view controller的源文件)中的方法来给UI元素添加动作并设置它相应的动作方法。storyboard会完成这种方法建立的连接。以后当应用加载storyboard时,连接就会恢复。
为按钮添加动作 . . .
-
在 project navigator选择 MainStoryboard.storyboard,在canvas上显示scene。
-
在Xcode工具栏上点击Utilities按钮隐藏utilities区域,点击assistant editor按钮显示Assistant editor面板。
assistant editor按钮是中间的Editor按钮,如图:。
-
确认Assistant显示view controller的头文件(即HelloWorldViewController.h)。
-
在canvas上把Hello按钮Control-drag到 HelloWorldViewController.h 的方法申明区域(也就是在@interface
语句和@end 语句之间)。
Control-drag,就是按住Control键的同时拖放。这里将按钮拖放到 Assistant editor面板中的头文件上。
当你Control-drag时,你看见如下界面:
当你释放Control-drag,Xcode会显示一个悬浮窗口让你配置刚才作的连接:
注意: 如果你在 HelloWorldViewController.h方法申明区域之外的地方释放Control-drag,则有可能看到不同类型的悬浮窗口,或者根本没有窗口。如果是这样,在canvas内点击一下即可关闭悬浮窗口,然后尝试重新Control-drag。
- 在悬浮窗口内,配置按钮的动作连接:
- 在Connection弹出菜单中选择Action。
- 在Name字段,输入 changeGreeting: (确认包含了冒号)。在后面的步骤里,你将会实现changeGreeting: 方法,它获取用户输入到文本框中的文本,并显示到标签上。
- 确认Type字段选中 id。这个id 数据类型能够代表任何Cocoa对象。这里使用id 是因为无论哪种对象都可以发送这个消息。
- 确认Event弹出菜单选中 Touch Up Inside。指定Touch Up Inside事件是因为希望在用户从按钮上抬起手指时发出消息。
- 确认Arguments弹出菜单选中Sender。
-
完成配置动作连接后,悬浮窗口如下图所示:
-
点击悬浮窗口中的Connect。
Xcode 添加了新方法changeGreeting: 的空白实现,并且在这个方法的左边显示一个实心圆圈来指示连接已经被建立:
当你把Hello按钮Control-drag到 HelloWorldViewController.h 文件并配置好相关动作后,你其实完成了两件事:
- 向view controller类添加了适当的方法申明代码。具体来说,你向 HelloWorldViewController.h添加了以下动作方法的申明:
|
同时Xcode向HelloWorldViewController.m添加了以下空白实现:
|
|
注意:IBAction 是一个特殊关键字,用于告诉Xcode要把一个方法当作是 target-action 连接中的动作。IBAction
被定义为 void。
动作方法中的 sender 参数引用了发送该动作消息的对象(在这里,sender就是按钮)。
- 你在按钮和view controller之间建立了连接。
下一步,你将在view controller和其余两个UI元素之间建立连接(标签和文本框)。
为文本框和标签创建Outlet
一个outlet 描述了两个对象之间的连接。当你要让一个对象(比如 view controller)和另一个它所包含的对象(比如文本框)通讯,你就要指定容器对象是一个outlet。当应用运行时,在Xcode中创建的outlet将被恢复,从而允许对象之间在运行时互相通讯。
在本文中,你希望view controller从文本框获取用户的文本,然后显示到标签中。要确保view controller能够与这些对象通讯,你要在它们之间建立outlet连接。
为文本框和标签添加outlet的步骤与之前添加按钮动作的步骤非常相似。在开始前,请确认main storyboard文件仍然显示在canvas上,而HelloWorldViewController.h 也仍然打开在Assistant editor中。
为文本框添加outlet . . .
-
将文本框从view中Control-drag到头文件的方法申明区域。
Control-drag时应当看到如下界面:
只要Control-drag释放在方法申明区域里,具体放在那一行是无所谓的。在本文中,文本框和标签的outlet显示在Hello按钮的方法申明之上。
- 在释放 Control-drag 后出现的悬浮窗口中配置文本框的连接:
- 确认连接弹出菜单选中Outlet。
- 在Name字段输入textField。你可以将outlet命名成任何名称,但是一个outlet的名称和它代表的东西之间有所联系可以提高代码的可读性。
- 确认Type字段选中UITextField。将Type字段设置为UITextField 确保Xcode仅将这个outlet关联到文本框。
- 确认Storage弹出菜单选中Weak,这是默认值。
-
完成这些设置后,悬浮窗口应当如下图:
-
点击悬浮窗口中的Connect。
通过为文本框添加outlet,你实际上完成了两件事:
- 向view controller类添加了适当的申明代码。具体来说,你向 HelloWorldViewController.h添加了以下申明:
|
注意:IBOutlet 是一个特殊关键字,仅用于告诉Xcode把对象当作一个outlet。它实际上被定义为nothing,这样就在编译时没有任何影响。
同时,你向 HelloWorldViewController.m中的viewDidUnload方法添加了以下语句:
|
viewDidUnload 方法是由Xcode模版提供的,它由 UIKit 框架来实现。当view controller须要卸载它包含的view时就会调用viewDidUnload,因而方法就是将view的outlet设为nil的最佳位置。
- 你在view controller和文本框之间建立了连接。这个连接允许用户输入的文本被传给。正如changeGreeting: 方法申明一样,Xcode在文本框申明的左边显示一个实心圆圈来指示连接已经被建立。
接下来再为标签添加outlet并配置连接。在 view controller 与标签之间建立连接使得 view controller 能够将字符串更新到标签上,而该字符串包含用户输入的文本。以下步骤和之前为文本框添加outlet基本是一样的,但在配置阶段稍有不同。(请确认HelloWorldViewController.h 仍然显示在Assistant editor里。)
为标签添加outlet . . .
-
将标签从view中Control-drag到 HelloWorldViewController.h 的方法申明区域。
- 在释放 Control-drag 后出现的悬浮窗口中配置标签的连接:
- 确认Connection 弹出菜单选中Outlet。
- 在Name字段,输入label。
- 确认Type字段选中 UILabel。
- 确认Storage 弹出菜单选中Weak。
-
点击悬浮窗口中的Connect。
到目前位置,你已经为 view controller 创建了全部三个连接:
- 按钮的action连接
- 文本框的outlet连接
- 标签的outlet连接
你可以在 Connections inspector 中检验这些连接。
打开 Connections inspector 查看view controller . . .
-
点击Standard editor按钮关闭Assistant editor,切换到standard editor视图。
Standard editor按钮是最左边的Editor按钮,如图:
-
点击Utilities view按钮打开utilities区域。
-
选择 outline view 中的Hello World View Controller。
-
在 utilities 区域中打开Connections inspector。
Connections inspector按钮是 inspector selector bar上最右边的按钮,如图:
在Connections inspector中,Xcode会显示出所选对象的所有连接(现在就是view controller)。在你的workspace窗口,界面应当如下图:
注意在你创建的三个连接之外,还有一个 view controller和view之间的连接。Xcode自动提供这个默认的连接;其实你用不着访问它。
设立文本框代理的托管
这个应用还须要建立一个连接:文本框和它的托管之间的连接。本文使用 view controller 作为文本框的托管。
之所以要指定文本框的托管,是为了在用户点按键盘上Done按钮时,让文本框可以向它的托管发送消息。(回忆一下托管就是某个对象代表另一个对象工作)。在后面的步骤中,你将使用与此消息相关联的方法来隐藏键盘。
确认 storyboard 文件在canvas上打开着。如果没有打开,就在 project navigator 中选择MainStoryboard.storyboard。
设立文本框的托管 . . .
-
在view中,将文本框 Control-drag 到 scene dock 的黄色球形上(黄色球形代表view controller 对象)。
释放Control-drag后,界面应当如下图:
-
在出现的半透明面板上,选择Outlets栏下的 delegate。
测试应用
点击Run测试一下应用。
你会看到点击Hello按钮时它会高亮显示。点击文本框,键盘就会出现并可以在其中输入文本。然而还是不能取消键盘。要做到这一点,还必须实现相关的托管方法。下一章中你将能完成这一点。现在,关闭模拟器。
回顾
在你建立canvas中的view controller与 Assistant editor中的 HelloWorldViewController.h文件之间连接的同时,你还更新了实现文件(即 HelloWorldViewController.m)来支持outlet和action。可以通过 project navigator打开并查看对实现文件的修改。
在建立连接时,你不一定要在canvas和源文件之间 Control-dragging 让Xcode来自动添加代码。你完全可以自行向头文件添加属性和方法的申明,也可以自行添加文本框托管的连接。然而一般来说,尽量让Xcode完成它能做到的事情,可以让你少犯错误(也少打许多字)。
实现View Controller
实现view controller包括几部分工作:为用户的名称添加一个属性 property,实现changeGreeting: 方法,确保用户点按Done时隐藏键盘。
为用户的名称添加属性
要为保存用户名称的字符串添加一个属性申明(property declaration),这样你的代码就总是能引用它。将这个申明添加到view controller的头文件中(即HelloWorldViewController.h)。
所谓 property declaration 是一种指示符,用于告诉编译器如何生成变量的访问方法,比如用于保存用户名称的变量。(添加property declaration后将学到什么是访问方法。)
在下文中,你无须再对 storyboard 文件作任何修改。为了在后续步骤添加代码时有更多屏幕空间,可以再次点击 Utilities View 按钮来隐藏 utilities 区域(或者选择菜单View > Utilities > Hide Utilities)。
为用户的名称添加属性申明 . . .
-
在project navigator中选择 HelloWorldViewController.h。
-
在 @end 语句前,为字符串添加一行@property
语句。
属性申明应当如下:
@property (copy, nonatomic) NSString *userName; |
你可以复制粘贴以上代码或者在 editor pane中手工输入。如果你选择自己输入代码,可以注意到Xcode会对你正在输入的内容提示自动完成。例如,当你开始输入@pro... Xcode猜想你要输入@property,所以它就在随行浮动面板中显示出建议输入的符号。如下图:
如果建议是正确的(如上图的例子所示),只需按回车即可接受它。
当你继续输入,Xcode 可能会提供一个可选列表给你。例如,当你输入 NSSt...,Xcode可能显示以下自动完成列表:
当Xcode显示出自动完成列表,按回车即可接受高亮显示的那条。如果高亮显示的不对(如图中所示),使用上下箭头键选中合适条目即可。
为了完成实现 userName 属性,你还须要告诉编译器来合成访问方法。所谓访问方法accessor method 就是获取或设置对象属性值的方法(有时候访问方法也被称为 "getters" 和 "setters")。
Xcode通过在 activity viewer 中显示一个黄色警告图标向你提示这个需求:
在本例中,你已经知道Xcode的警告是什么,所以你不必去关注这个警告的更多信息。如果你想看到更多信息,点击 activity viewer中的警告图标即可在 issue navigator中显示详细信息:
本文中你不再用到issue navigator,所以可以点击 navigator selector bar最左边的按钮回到project navigator。
下文中你将写代码告诉编译器在 view controller 的实现文件(即 HelloWorldViewController.m)中合成访问方法。
为用户名称属性合成访问属性 . . .
-
在project navigator中选择 HelloWorldViewController.m。
-
在@implementation
HelloWorldViewController 这行后面添加如下代码
@synthesize userName = _userName; |
添加这行代码满足了Xcode所警告的需求,所以 activity viewer 中的警告图标消失了。
当编译器遇到 @synthesize 指令,它会自动产生以下两个访问方法:
- - (NSString *)userName
- - (void)setUserName:(NSString *)newUserName
在@synthesize 代码行中添加带下划线的userName,你告诉编译器使用 _userName 作为 userName属性的实例变量。由于你并没有在类中申明一个名为 _userName的实例变量,这行代码也要求编译器也要合成该变量。
注意:编译器仅对它生成的编译码添加访问方法;它不会修改你的源文件。
实现 changeGreeting: 方法
在前文“配置View”这一章中,你将Hello按钮配置为,当用户点按时发送 changeGreeting: 消息给view controller。作为回应,你希望view controller将用户输入在文本框的文本显示在标签中。具体来说,就是changeGreeting: 方法应当:
- 从文本框获取字符串,并赋值给view controller的 userName 属性。
- 基于 userName 属性创建一个新的字符串并显示在标签上。
实现changeGreeting: 方法 . . .
-
在 project navigator 中选择 HelloWorldViewController.m。
你可能得向下滚动到文件底部才能看到Xcode添加的changeGreeting: 代码块。
-
添加以下代码来完成实现 changeGreeting: :
- (IBAction)changeGreeting:(id)sender { |
|
self.userName = self.textField.text; |
|
NSString *nameString = self.userName; |
if ([nameString length] == 0) { |
nameString = @"World"; |
} |
NSString *greeting = [[NSString alloc] initWithFormat:@"Hello, %@!", nameString]; |
self.label.text = greeting; |
} |
changeGreeting: 方法中有值得注意的几点:
- self.userName = self.textField.text; 从文本框获取文本并将结果赋值给view controller的userName 属性。在本文中,你没有在其它任何地方用到保存用户名称的字符串,但是重要的是要记住它的角色:它是一个非常简单的model对象,由view controller管理。一般来说,应由 controller在它自己的model对象中维护关于应用数据的信息—应用数据不应当被保存在用户界面元素中,比如本例中的文本框。
- NSString *nameString = self.userName; 创建一个新变量(类型是NSString)并赋值为view controller的userName 属性。
- @"World" 是由一个NSString代表的字符串常量。如果用户运行这个应用但没有输入任何文本(即[nameString length] == 0),nameString 将会包含文本 "World"。
- initWithFormat: 方法由Foundation 框架提供。它创建一个可以指定格式的字符串(很像C语言的printf 函数)。在格式字符串中,%@ 作为字符串对象的占位符。格式字符串双引号内的所有其它字符都将按原样显示在屏幕上。
配置View Controller托管文本框
如果你现在编译并运行应用,当点击按钮时,标签中显示 "Hello, World!"。但如果你选择文本框并在键盘上输入,你会发现还是没有办法取消键盘。
在 iOS 应用中,当一个允许文本输入的元素成为第一响应者( first responder)时,键盘会自动出现;但这个元素失去first responder状态时,键盘自动消失。(回忆一下,first responder 是首先接收到各种事件通知的对象,比如点按文本框带出键盘。)虽然无法从你的应用直接发送消息给键盘,但可以通过切换文本输入UI元素的first responder状态来使它滑动出现或消失。
UIKit 框架定义了 UITextFieldDelegate 协议(protocol),它包括textFieldShouldReturn: 方法,当用户点按Return按钮时文本框就会调用这个方法(无论这个按钮的实际标题是是什么)。因为我们将设置view controller托管文本框,你可以实现这个方法来强迫文本框失去first responder状态,也就是发送给它一个resignFirstResponder 消息—这将触发键盘滑动消失的效果。
注意:协议protocol 基本上就是一系列方法。如果称一个类符合(或采用)一个protocol,就是保证它要实现所有protocol必需的方法(Protocols也可以包含可选的方法)delegate protocol 指定了一个对象可能发送给它的 delegate 的所有消息。
配置HelloWorldViewController 托管文本框 . . .
-
选择 project navigator 中的 HelloWorldViewController.m。
-
实现 textFieldShouldReturn: 方法。
该方法要告诉文本框放弃first responder 状态。实现代码如下:
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField { |
if (theTextField == self.textField) { |
[theTextField resignFirstResponder]; |
} |
return YES; |
} |
在这个应用中,并不一定要包含 theTextField == self.textField 的判断,因为这里只有一个文本框。然而这是一个值得使用的好模式,因为有可能发生你的对象托管了多个类型相同的对象,而且必须区别对待它们。
-
在 project navigator 中选择 HelloWorldViewController.h。
-
在@interface
行最后,添加<UITextFieldDelegate>。
接口申明如下:
@interface HelloWorldViewController : UIViewController <UITextFieldDelegate> |
... |
这个申明指定HelloWorldViewController 对象采用UITextFieldDelegate
协议。
测试应用
编译并运行应用。这次应当一切如你所愿。在模拟器中输入名字后点击Done取消键盘,然后点击Hello按钮就会在标签中显示 "Hello, Your Name!" 。
如果应用还是有问题则需要继续查错。对于某些须要调查的区域,请参阅下一章“查错和回顾代码”。
回顾
现在你已经完成实现view controller,,完成了你的第一个iOS 应用。恭喜!
可以返回到Start Developing iOS Apps Today 继续学习iOS应用开发的知识。
查错和回顾代码
如果你的应用运行有问题,请尝试本章描述的解决问题的手段。如果你的应用最终还是有问题,请将你的代码和本章最后列出的完整代码作比较。
代码和编译警告
代码编译后应当没有任何警告。如果你收到警告,建议你认为它们很可能就是错误。因为Objective-C 是一种非常同态的语言,有时编译器能给你的大多数信息就只是警告。
检查Storyboard文件
作为开发者,如果程序运行不正常,你的天然本能也许就是检查源代码的bugs。但是Cocoa Touch增加了另一种维度。很多应用配置可能被“编码”在storyboard中。例如,如果你没有配置正确的连接,你的应用就不能如你所愿。
- 如果点击按钮时文本不能更新,可能你没有将按钮动作连接到view controller,,或者你没有将view controller的outlet连接到文本框或标签。
- 如果点击Done时键盘不消失,可能你没有连接文本框的托管,或没有将view controller的textField outlet连接到文本框。确认检查过storyboard中文本框的连接:Control-click 文本框打开半透明连接面板。在delegate outlet 和textField referencing outlet旁应当有实心圆圈。如果你已正确连接了托管,则可能存在更微妙的问题(请看下一节“托管方法名称”)。
托管方法名称
托管中经常发生的一个错误就是托管方法名称的拼写错误。即使你已正确设置了托管对象,如果托管在他的方法实现中没有使用正确的名称,方法就不会被调用。通常最好从文档中复制粘贴托管方法申明,比如textFieldShouldReturn:。
代码清单
本节提供了 HelloWorldViewController类接口和实现文件的代码清单。注意清单中没有显示备注和其它由Xcode模版提供的方法实现。
接口文件:HelloWorldViewController.h
#import <UIKit/UIKit.h>
|
|
@interface HelloWorldViewController : UIViewController <UITextFieldDelegate>
|
|
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
@property (weak, nonatomic) IBOutlet UILabel *label;
|
@property (nonatomic, copy) NSString *userName;
|
|
- (IBAction)changeGreeting:(id)sender;
|
|
@end
|
实现文件 HelloWorldViewController.m
#import "HelloWorldViewController.h"
|
|
@implementation HelloWorldViewController
|
|
@synthesize textField=_textField;
|
@synthesize label=_label;
|
@synthesize userName=_userName;
|
|
|
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
|
if (theTextField == self.textField) {
|
[theTextField resignFirstResponder];
|
}
|
return YES;
|
}
|
|
- (IBAction)changeGreeting:(id)sender {
|
self.userName = self.textField.text;
|
|
NSString *nameString = self.userName;
|
if ([nameString length] == 0) {
|
nameString = @"World";
|
}
|
NSString *greeting = [[NSString alloc] initWithFormat:@"Hello, %@!", nameString];
|
self.label.text = greeting;
|
}
|
@end |