这节课将会让你对Xcode以及它里面你用来写app的工具更加熟悉。你将会学到一个Xcode项目的基本架构并学会如何使用基本项目组件构建一个互相跳转的app。在这节课中,你会为FoodTracker app创建一个简单的用户交互(UI)并可以在模拟器中进行查看。当你结束时,你的app将会拥有如下元件:一个表示美食名称的文本,一个输入框用来输入你自定义的美食名称,以及一个按钮来触发修改美食名称事件。

学习目标:
学习完这个课程,你要让你自己掌握如下要点。
1、在Xcode上创建一个项目
2、明白示例项目中所有文件创建的目的
3、开关一个项目中的文件
4、在storyboard上添加、移动和重新设置UI
5、在storyboard中使用属性检查器来对UI进行调整和设置
6、使用大纲视图进行查看并调整UI
7、使用助理编辑的预览模式来预览UI
8、使用Auto Layout来布置UI使得它可以自适应用户的设备尺寸
如何创建Project就不说了,这个无需翻译,直接进入后面的布局和开发阶段。
不过在此做个记录,创建project时出现这个页面:

这个页面中最后3个,一般都不用勾选,而Unit test(第二个)是单元测试,是为了从小功能就检验代码的正确,保证项目的正确。勾选后,在project file中创建一个tests文件。
查看源代码
AppDelegate.swift源代码文件
AppDelegate.swift有两个主要功能:
1、他定义了一个叫做AppDelegate的类,委托app创建了一个窗口,用于展现你app的内容并提供一个地方可以响应app的状态改变(比如active、background等)。
2、他创建了一个app的入口和一个传递进入事件的运行循环给你的app。上述工作是UIApplicationMain 中的属性。由文件的首行代码 @UIApplicationMain 来实现。
使用UIApplicationMain 属性其实等于调起UIApplicationMain 函数并把AppDelegate类的名称作为输入类型传给它(UIApplicationMain 函数),然后系统会创建一个应用对象,这个对象负责管理整个app的生命周期。系统也会创建AppDelegate类的实力,并把它和应用对象关联起来,最终,系统启动你的app。
AppDelegate会在你创建一个新项目时自动创建,你需要用这个类提供你的app的初始化并响应app总层面上的事件。这个AppDelegate遵守UIApplicationDelegate协议,这个协议定义了一系列的方法用于建立你的app;反馈app状态的改变,或处理其他应用级的事件。
AppDelegate类包含一个属性:window

这个属性存储对app窗口的引用,它表现为你app中的各级view层的根属性。也是你app所有内容绘制的地方,注意,这个window的type是optional。即在某些时候可能为空(nil)
AppDelegate.swift源代码文件包含这些函数:
1、application(_:didFinishLaunchingWithOptions:)函数

使用这个方法(以及孪生方法application(_:willFinishLaunchingWithOptions:)),两者一起来完成你的app的初始化并进行最终的调整。这个方法会在状态指令(下面的一些did指令)发起后,app屏幕和UI展示之前进行调用。这个方法调用后,系统才会调用其他delegate方法(下面几种)去让app进入活跃状态或进入后台状态。
如果你不在application(_:willFinishLaunchingWithOptions:)方法中进行修改launchOptionsdictionary中的键值,这个方法是你处理launchOptionsdictionary中的键值的最后机会,那么你就需要在这个didFinishLaunchingWithOptions方法中对它们进行设置。
其他不使用这个app delegate的项目通过观察didFinishLaunchingNotification的通知也可以获取到相同的lauchOptions dictionary值,也可以获取到通知的userInfo字典。这些通知将会在这个方法return后实时发出。
2、applicationWillResignActive函数:

这个方法会在app从活跃状态转入非活跃状态是被调用并通知app。这发生在一些打断查看程序的情况下(比如一个来电或者SMS信息),又或者用户退出了app然后它即将会转入后台运行。在后台运行时,app仍然在持续工作,但是无法传送信息。
你需要用这个方法去暂停运行中的人物、禁用计时器、并停止OpenGL的帧刷新。游戏需要用这个method去暂停游戏,一个app需要在非活跃状态下执行最小的工作。
如果你的app其他地方没有保存用户数据,你可以在这里保存并保证它不会丢失。它在用户执行app时保存用户数据,比如在用户关闭一个数据输入窗口时保存数据。不要把保存app的数据放在app进行数据转换时。
调用这个方法后,app还会广播一个willResignActiveNotification通知来让需要这些数据的项目反应,以便做好状态过渡的准备。
3、applicationDidEnterBackground函数:

使用这个方法来释放资源空间,废止计时器,然后存储足够的app状态信息来防止它可能发生的关闭行为。你还需要停止app的用户交互,避免使用共享系统资源(比如用户数据库)。另外,你必须关闭OpenGL ES在后台中的应用。
这个方法将会提供5秒钟的时间来让你的app准备最后的其他任务和return一些东西。如果你需要额外的时间来完成最后的一些任务,你可以调用beginBackgroundTask(expirationHandler:)方法请求额外执行时间。但一般来说,你需要让方法尽快的返回需要的值,以避免任务未完成而app关闭并从内存中清除。
你需要在这个方法完毕前执行任何app最前面交互的任务。而其他的一些任务(比如保存状态)等可以被移至时间列或辅助线程中。比如一些background任务,applicationDidEnterBackground(_:)在结束前,不需要跑这些任务,你需要请求新增的background额外处理时间来搞定这些任务。使用的还是那个beginBackgroundTask(expirationHandler:)方法。
这个方法会在被调用时及时发出通知,并让其他程序和功能及时反应并处理任务。
4、applicationWillEnterForeground函数:

在iOS 4.0以及更高的版本中,这个方法被用来从background状态唤醒app转换为活跃状态。你可以使用这个方法撤销app进入后台时的各种限制。这个方法后面永远跟着一个方法applicationDidBecomeActive(_:),用来让app从非活跃状态进入活跃状态。
调用此方法时,会发送willEnterForegroundNotification通知。
5、applicationDidBecomeActive函数:

这个方法用于app从非活跃状态转换至活跃状态时,通知app的。这发生在用户启动、系统启动或用户拒接或者关闭了某些打断程序比如电话、短信等等。
你可以用这个方法恢复当app进入非活跃状态时的各种限制,比如重计计时器,调起OpenGL ES帧刷新,如果你的app以前在background状态下,你也可以使用这个方法来刷新用户交互。
这个方法会返回didBecomeActiveNotification通知
6、applicationWillTerminate函数

这个方法会让在app即将关闭并从内存清空时调用,你可以用这个方法做最后的清除任务,比如释放共享资源(比如用户数据库),保存用户数据,使计时器无效。这个方法最多为你提供5秒钟的处理时间,时间完毕后,系统会清除掉这个app的所有进程。
这个方法往往不会被调用,因为app往往是进入了后台运行,不过在一些特殊情况下,比如长时间后台未使用的app还是会被系统自动关闭,还是可以用这个方法。方法调用后,会发送willTerminateNotification通知。
这些方法让应用对象与app委托紧紧相连,在一个app的状态过渡中,比如app启动;进入后台状态;app关闭等等——应用对象都会调起对应的委托方法,让你的app去根据状态进行处理。你不需要做任何事情来保证这些方法会在合适的时间去调用,app对象已经为你干了这些事儿。
每一个委托方法都有一个默认运行方法,如果你设置他们的运行代码为空甚至删除他们,你会在上述情况发生时获得默认运行的结果。当然,你也可以加入你自己的代码到这些方法里来,自定义这些方法执行时的行为。
模版也包含了每个根方法的示例注解,这些注解描述了这些方法可以为你的app做些什么。你可以参照这个蓝图来设计很多通用的应用层的事件。
在这节课中,你不会用到任何自定义的app delegate代码,所以你不需要对你的AppDelegate.swift文件进行任何修改。
View Controller代码文件
这个视窗还有另外一个资源代码文件:ViewController.swift。在project navigator中选择它并打开。

这个文件定义了一个UIViewController类的子类,命名为ViewController。现在这个类继承了UIViewController类的所以内容。为了实现你自己的方法,你可以对UIViewController类中的方法进行重写(override)。
你可以看到在ViewController.swift文件中,模版中对viewDidLoad()和didReceiveMemoryWarning()方法进行了重写(没看到didReceiveMemoryWarning()),尽管这些模版中的重新没有做任何事情,仅仅是调用了这些在UIViewController 中定义的方法镜像。你可以加入你自己的代码去自定义view controller的反馈在这些事件中。
在这个课程中,你不需要用到didReceiveMemoryWarning() 这个方法,忽略或者删除它。然后你的ViewController.swift文件中应该像这样:

你将会在这个课程的后面在这个文件中写入一些代码。
Open Your Storyboard
现在你要开始在storyboard上去创建你的app了。storyboard是一个app用户交互页面的展示面板,展示页面上的内容以及页面之间的交互关系。你使用storyboard去布局层级或关系让你的app启动起来。当你创建了一个东西你可以马上看到它,马上得到反馈哪些在工作哪些没有,然后可以对你的用户交互随时进行改变。

从上面这个图可以看出,你的app中的storyboard仅仅包含一个视觉页面,这个即展示你的app内容。视窗左边的箭头表示App从这里进来,也就是说当前这个视窗是app开始的第一个视窗。这个视窗包含了一个view层以及它的view controller。你将会很快学习到更多关于view视图和视图控制器的知识。
如果你选择在iPhone 7的模拟机上运行你的app,视窗中的视图就是你将在对应硬件设备上看到的样子。当然,画布的视窗展示可能与模拟器上的不太一样,你可以选择屏幕尺寸和方向在画布的底部。在这个例子中,它默认是iPhone 7的纵向屏幕,所以看起来画布和模拟器是一样的。
尽管画布展示特定的机型和设备朝向,但是创建一个自适应的交互视图仍然非常重要,自适应视图可以自动适应任何苹果设备和任何朝向。当你开发你的交互页面时,你可以改变画布的视窗,可以直观的感受到在不同尺寸的屏幕中,交互布局是如何改变的。
Build the Basic UI
是时候来建立一个基础的交互界面了。让我们为我们的app:FoodTracker添加一个新美食。
Xcode提供一个庞大的项目库,用于在storyboard文件中添加元件和事件。一些是你用户界面的组成部分,比如按钮、文本字段。也包括一些试图控制器和手势识别,用于定义你的app行为,而且不显示屏幕上。
最常用的组件莫过于view了。Views展示内容给用户,他们是一个个块来组合你的用户界面,并干净、优雅且有用的展现你的内容。Views有一系列的有用的内置行为,包括屏幕上的展示和用户输入的交互等等。
所有的iOS view组件都是UIView类型,或者是UIView类型的子类。这些UIView的子类往往在展现和行为上更加专业,我们先添加一个文本字段(UITextField),这是一个UIView的子类。在你的视窗上,一个文本框允许用户输入一行文本,这个将会用来输入美食的名称。
你可以输入command+shift+L来调起这个选择器:

选择后拖拽出来到canvas上,并为它设定它的constrain,比如上面离safeArea的top 50个point,左右各20 point等,当然你也可以自己拖拉它到你想要的形状。

设置文本输入框的提示文本
当你选择了文本框组件时,右边将会打开对应的属性检查器。
属性检查器位于第四个图标,它可以让你设置你刚才拖拽的组件的属性。

在属性检查器中,找到placeholder这一项,输入meal name

当你编辑文本输入框的属性时,你还可以设置系统输入键盘的属性,比如当用户选择这个文本输入框时,系统键盘会显示出来。
设置系统键盘
首先你要确认在canvas或者页面层级树中,你的目标text field是被选中的。
在属性检查器中,找到输入键盘的返回键然后选择Done,这将让你这个文本输入框的最下角的返回键变成Done。文本改变了。然后你在它的下方勾选Auto-enable Retrun Key,这个表示用户只能输入了文本在文本框中才能点击Done键。防止用户输入空。

下一步,我们添加一个文本在页面的上部,纯文本是无交互属性的,它只是向用户展示你设定的文本内容(无交互指的是不能像按钮那样有“点击”等事件)。为了更好的展示组件之间如何联系在一起,我们设定这个Label去展示用户在输入框中输入的文本内容,这可以很好的测试输入框是否获取到用户的输入值并让它执行任务。
然后我们再添加一个button,这个button可以用来绑定一个事件,即点击时,获取输入框内的文本值,并用这个文本值修改Label上的值。我们会获取到下面这种布局展示。

但是当我们在canvas的左下角进行service和orientation的切换时,比如切换到iPhoneXs,可能按钮和输入框显小,而放在iPhone4里面,输入框却显得很大。这些问题都可以使用Auto Layout来帮助你。Auto layout可以帮助你减少页面布局的工作量,而由它接管你的布局在各种版本的device和orientation之间的自适应问题。

Auto Layout中有一个非常重要的组件称之为stack view(UIStackView)。一个stack view提供横向或竖向的序列布局。而且它相当于一个小的自动布局,根据它里面的元件大小和位置进行自适应管理。这让你会更好的处理复杂的布局问题,从而领略到Auto Layout的魅力所在。
你可以像这样把几个组件组合到一个stack view里面,然后我们再为这个stack view添加一些限制条件。

我们注意到如果你如果勾选了“Constrain to margins”,这个会让你的scene是位于superview的左右间距位置(margin只对左右进行限制),可以发现一些空白,而不是顶格展示。top位置要考虑状态栏,要注意选择数值的参考对象时,是否为状态栏的底部,如果不是,记得要预留一些空白。

然后我们就能得到这个界面啦。
Debugging Auto layout
如果你没有在上述行动中取得预期成果,你可以使用Auto Layout调试器来帮助你。你可以点击“刷新布局”和“自动布局检验菜单”时,你也可以在树状目录上方寻找发现有无红色或黄色的箭头,点击箭头就可以进入到问题界面。

Update frames其实就是根据最新的限制条件对canvas进行刷新展示,如果有冲突,会在canvas中以红色线标识出来。而Resolve Auto Layout Issues菜单是弹出一个包含解决办法的菜单

当布局没有按照我们所期望的去布局,点击canvas右下角的Resolve Auto Layout Issues按钮去升起这个调试命令菜单。这里面的所有命令都包含两种形式,一个影响选择的view,另一个影响这个view controller中包含的所有视图。如果全部命令为灰色,请重新在树状view层中选择一个或view controller再重新打开这个功能菜单。
选择Reset to Suggested Constraints其实是让Xcode按照系统建议的来设定constraints。而另一个clear就是这个选中组件的constraints全清,然后你自己重新添加。
下一课我们要进入的是把UI与code联系起来。
网友评论