源码见github:03_使用视图控制器
本例子是在FoodTracker应用的场景。学习使用图像选择器向场景中添加照片。完成后,您的应用程序将如下所示:
学习目标
在课程结束时,您将能够:
了解View Controller生命周期
到目前为止,FoodTracker应用具有单个场景,其用户接口是由一个单一管理视图控制器。为您打造更复杂的应用程序,你会创造更多的场景,将需要管理装卸的意见,因为它们可以打开和关闭屏幕上移动。
该的对象UIViewController 类(及其子类)配备了一套管理其视图层次结构的方法。当视图控制器在状态之间转换时,iOS会在适当的时间自动调用这些方法。当你创建一个视图控制器子类(如ViewController 您一直使用类),它继承中定义的方法UIViewController ,让你添加自己的自定义行为每种方法。重要的是要了解系统何时调用这些方法,因此您可以设置或删除您在该过程中的适当步骤显示的视图 - 这是您以后在课程中需要做的。
的iOS调用UIViewController 方法如下:
-
viewDidLoad() 在视图控制器的内容视图(其视图层次结构的顶部)创建并从故事板加载时调用。视图控制器的插座在调用此方法时保证具有有效值。使用此方法执行视图控制器所需的任何其他设置。 通常情况下,iOS的要求viewDidLoad() 只有一次,第一次创建它的内容视图时; 然而,当控制器第一次被实例化时,内容视图不一定被创建。相反,它懒洋洋地创建了第一次系统或任何代码访问控制器的view 属性。 -
viewWillAppear() 刚刚在视图控制器的内容视图添加到应用程序的视图层次结构之前。使用此方法触发在内容视图显示在屏幕上之前需要发生的任何操作。尽管名称,只是因为系统调用此方法,它不保证内容视图将可见。视图可能被其他视图隐藏或隐藏。此方法简单地指示内容视图即将添加到应用程序的视图层次结构中。 -
viewDidAppear() 刚刚在视图控制器的内容视图添加到应用程序的视图层次结构之后。使用此方法触发任何需要在屏幕上显示视图时发生的操作,例如获取数据或显示动画。尽管名称,只是因为系统调用此方法,它不保证内容视图是可见的。视图可能被其他视图隐藏或隐藏。此方法仅表示内容视图已添加到应用程序的视图层次结构中。
存在一组互补的拆卸方法,如上面的状态转换图所示。
您将在FoodTracker应用程序中使用这些方法中的一些来加载和显示您的数据。事实上,如果你还记得,你已经写在代码的一些viewDidLoad() 方法ViewController :
-
overridefuncviewDidLoad() { -
super.viewDidLoad() -
-
// Handle the text field’s user input through delegate callbacks. -
nameTextField.delegate = self -
}
应用程式设计的这种风格在这里视图控制器作为您的观点和数据模型之间的通信管道被称为MVC(模型-视图-控制器)。在这种模式下,模型跟踪您的应用程序的数据,视图显示您的用户界面和组成应用程序的内容,控制器管理您的意见。通过响应用户操作和填充与内容的看法数据模型,控制器作为模型和视图之间进行通信的网关。MVC是任何iOS应用程序的一个好设计的核心,到目前为止,FoodTracker应用程序是按照MVC原则构建的。
当你保持MVC模式记住的应用程序的设计的其余部分,是时候把你的基本用户界面到一个新的水平,并添加一个图像到膳食的场景。
添加膳食照片
完成膳食场景的下一步骤是添加显示特定膳食的照片的方式。为此,你需要使用的成像图(UIImageView ),显示画面的用户界面元素。
向场景添加图像视图
-
打开你的故事板,Main.storyboard 。 -
打开对象库中的实用面积。(或者,选择“视图”>“实用程序”>“显示对象库”。)  -
在对象库中,类型image view 在过滤器字段来快速寻找图像视图对象。 -
将图像视图对象从对象库拖动到场景中,以使其位于按钮下方的堆栈视图中。  -
随着图像视图中选择,打开大小检查 在公用事业领域。 回想一下,尺寸检查,当你从检查选择栏左侧选择第五个按钮出现。它允许您编辑故事板中对象的大小和位置。  -
在“内部大小”字段中,选择占位符。(此字段位于“大小”检查器的底部,因此您需要向下滚动到该字段。) -
键入320 在宽度和高度字段。按返回。 视图的内在含量的大小是根据其内容视图的首选大小。空图像视图没有固有的内容大小。一旦向视图中添加图像,其内在内容大小就会设置为图像的大小。提供占位符大小使图像具有临时内在内容大小,您可以在设计用户界面时使用。此值仅在Interface Builder中设计接口时使用; 在运行时,布局引擎使用视图的实际内在内容大小。  -
在画布的右下角,打开固定菜单。  -
选中纵横比旁边的复选框。 Pin菜单应该看起来像这样:  -
在“固定”菜单中,单击“添加1约束”按钮。 您的图片视图现在的宽高比为1:1,因此它始终是正方形。 -
选择的图像视图中,打开属性检查器 。 回想一下,在属性检查器中,当你从检查选择栏中选择左边的第四个按钮会出现。它允许您编辑故事板中对象的属性。 -
在属性检查器中,找到交互字段,并选择启用用户交互复选框。 稍后您需要使用此功能,以便用户与图像视图进行交互。
您的场景应如下所示:
显示默认照片
添加一个占位符图片,让用户知道他们可以与图像视图交互以选择照片。从这张图片Images/ 的下载文件文件夹在这个课程结束时,或使用自己的图像。
向项目添加图像
-
在项目浏览器中,选择Assets.xcassets 查看资产目录。 该资产目录是存储和整理你的形象资产的应用程序的地方。 -
在左下角,点击加号(+ )按钮,然后选择新形象设置为从弹出菜单。  -
双击图片集名称并将其重命名为defaultPhoto 。 -
在计算机上,选择要添加的图片。 -
将影像拖放到2x 的图像集插槽。 2x 是为iPhone 7显示分辨率模拟器你使用这些教训,让图像看起来最好在这个分辨率。
探索进一步
有关图像分辨率的更多信息,请参阅图形>图像大小和分辨率在iOS的人机界面指南。
将默认占位符图像添加到项目中后,将图像视图设置为显示它。
在图像视图中显示默认图像
-
打开你的故事板。 -
在您的故事板中,选择图像视图。 -
选择的图像视图中,打开属性检查器 中的实用面积。 -
在属性检查器中,找到标有场图像与选择defaultPhoto 。
检查点:运行你的应用程序。默认图像显示在图像视图中。
将图像视图连接到代码
现在,您需要实现在运行时更改此图像视图中的图像的功能。首先,您需要将图像视图连接到代码中ViewController.swift 。
将图像视图连接到ViewController.swift代码
-
单击Xcode右上角附近Xcode工具栏中的“助理”按钮以打开助理编辑器。  -
如果您想了解更多的工作空间,折叠项目导航和生活区通过点击工具栏中的Xcode在导航和实用程序的按钮。 您还可以折叠大纲视图。 -
在您的故事板中,选择图像视图。 -
从画布到右边的编辑器中的代码显示屏上的图像视图控件拖拽,停在该行正下方在现有网点的阻力ViewController.swift 。  -
在出现的名称,类型对话框photoImageView 。 保留其余的选项。您的对话框应如下所示:  -
单击连接。 Xcode中添加必要的代码ViewController.swift 来存储到图像视图的引用,并配置故事板来设置该连接。
-
@IBOutletweakvar photoImageView: UIImageView!
您现在可以从代码访问图像视图以更改其图像,但是您如何知道何时更改图像?您需要给用户一个表示他们想要更改图像的方法 - 例如,通过点击图像视图。然后,您将定义一个操作方法,当自来水发生改变形象。
有之间的细微差别区分的观点和控制,这是专业的意见版本的以特定的方式来响应用户的操作。视图显示内容,而控件用于以某种方式修改内容。控制(UIControl )是的子类UIView 。事实上,您已经在界面中使用了两种视图(标签,图片视图)和控件(文本字段,按钮)。
创建手势识别器
图像视图不是控件,因此它的设计不是按照按钮或滑块可能的方式响应输入。例如,您不能简单地创建一个用户点击图像视图时触发的操作方法。(如果您尝试从图片视图中将控件拖动到代码,您会注意到您无法在“连接”字段中选择“操作”。)
幸运的是,通过添加一个手势识别器给一个视图与控件相同的功能很容易。手势识别是附加到一个视图,允许以响应用户控制的方式做对象。手势识别器解释触摸以确定它们是否对应于特定手势,诸如滑动,捏合或旋转。您可以编写一个动作方法,当手势识别器识别其分配的手势时,这是您需要为图像视图做的。
安装水龙头手势识别(UITapGestureRecognizer )将图像视图,当用户敲击着图像视图,表扬。你可以在你的故事板轻松做到这一点。
在图片视图中添加点击手势识别器
-
打开对象库(选择视图>实用程序>显示对象库)。 -
在对象库中,类型tap gesture 在过滤器领域迅速找到点击手势识别对象。 -
将“点击手势识别器”对象从对象库拖动到场景中,并将其放在图像视图的顶部。 轻按手势识别对象出现在吃饭的场景码头。 
将手势识别器连接到代码
现在,将该手势识别器连接到代码中的动作方法。
将手势识别器连接到ViewController.swift代码
-
按住Control键拖动从场景中的码头在右侧编辑器中的代码显示的手势识别,停在下面的一行拖动//MARK: Actions 注释中ViewController.swift 。  -
在出现的对话框中,对于连接,选择操作。 -
对于名称,键入selectImageFromPhotoLibrary 。 -
对于类型,请选择UITapGestureRecognizer 。 您的对话框应如下所示:  -
单击连接。 Xcode中添加必要的代码ViewController.swift 来设置的操作。
-
@IBActionfuncselectImageFromPhotoLibrary(_sender:UITapGestureRecognizer) { -
}
创建图像选择器以响应用户敲击
当用户点击图片视图时,他们应该能够从照片集合中选择照片,或者选择自己的照片。幸运的是,UIImagePickerController 类有这种行为内置到它。图像选择器控制器管理用于拍摄照片和选择要在您的应用程序中使用的已保存图像的用户界面。而且,正如你需要一个文本字段的委托,当你用文本字段工作,你需要一个图像采集器控制器委托具有图像采集器控制器进行工作。该名代表的姓名协议是UIImagePickerControllerDelegate ,那你会定义为图像选择器控制器的代表是对象ViewController.
首先,ViewController 需要采用的UIImagePickerControllerDelegate 协议。因为ViewController 将负责呈现影像选择器控制器,它也需要采取UINavigationControllerDelegate 协议,该协议只是让ViewController 承担一些基本的导航职责。
采用UIImagePickerControllerDelegate和UINavigationControllerDelegate协议
-
通过单击标准按钮返回到标准编辑器。 通过单击Xcode工具栏中的导航器和实用程序按钮,展开项目导航器和实用程序区域。 -
在项目导航栏中选择ViewController.swift 。 -
在ViewController.swift 中,找到class 行,应该是这样的:
-
classViewController:UIViewController, UITextFieldDelegate {
-
之后UITextFieldDelegate ,添加一个逗号(, ),并UIImagePickerControllerDelegate 通过该协议。
-
classViewController:UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate {
-
之后UIImagePickerControllerDelegate ,添加一个逗号(, ),并UINavigationControllerDelegate 通过该协议。
-
classViewController:UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
在这一点上,你可以回到你的定义,操作方法selectImageFromPhotoLibrary(_:) ,并完成实施。
实现selectImageFromPhotoLibrary(_ :)操作方法
-
在ViewController.swift 中,找到selectImageFromPhotoLibrary(_:) 您刚才添加的操作方法。 它应该看起来像这样:
-
@IBActionfuncselectImageFromPhotoLibrary(_sender:UITapGestureRecognizer) { -
}
-
在该方法的实现,大括号(间{} ),添加以下代码:
-
// Hide the keyboard. -
nameTextField.resignFirstResponder()
这段代码可以确保当用户点击,而在文本字段中键入图像视图,键盘正确关闭。 -
添加此代码以创建图像选择器控制器:
-
// UIImagePickerController is a view controller that lets a user pick media from their photo library. -
letimagePickerController = UIImagePickerController()
-
添加此代码:
-
// Only allow photos to be picked, not taken. -
imagePickerController.sourceType = .photoLibrary
这行代码设置图像拾取器控制器的源,或者它获取图像的地方。该.photoLibrary 选项使用模拟器的相机胶卷。 的类型imagePickerController.sourceType 是已知的UIImagePickerControllerSourceType ,这是一个枚举。这意味着你可以写它的值作为缩写形式.photoLibrary 代替UIImagePickerControllerSourceType.photoLibrary 。回想一下,您可以在枚举值的类型已知时随时使用缩写形式。 -
添加以下代码来设置图像选取器控制器的委托ViewController :
-
// Make sure ViewController is notified when the user picks an image. -
imagePickerController.delegate = self
-
在上一行下面,添加这行代码:
-
present(imagePickerController,animated:true, completion: nil)
present(_:animated:completion:) 是一种被称为上的方法ViewController 。虽然它没有明确地写,这种方法被隐式上执行self 对象。该方法要求ViewController 出示由定义的视图控制器imagePickerController 。传递true 的animated 参数动画影像选择器控制器的演示。该completion 参数是指完成处理程序,一段代码,执行该方法完成后。因为你不需要做别的事情,即表示您不需要通过传递来执行完成处理程序零。
您的selectImageFromPhotoLibrary(_:) 操作方法应该是这样的:
-
@IBActionfuncselectImageFromPhotoLibrary(_sender:UITapGestureRecognizer) { -
-
// Hide the keyboard. -
nameTextField.resignFirstResponder() -
-
// UIImagePickerController is a view controller that lets a user pick media from their photo library. -
letimagePickerController = UIImagePickerController() -
-
// Only allow photos to be picked, not taken. -
imagePickerController.sourceType = .photoLibrary -
-
// Make sure ViewController is notified when the user picks an image. -
imagePickerController.delegate = self -
present(imagePickerController,animated:true, completion: nil) -
}
提供图像选择器控制器后,您通过委托方法与它进行交互。为了让用户选择图像的能力,你需要实现两个定义的委托方法UIImagePickerControllerDelegate :
-
funcimagePickerControllerDidCancel(_picker:UIImagePickerController) -
funcimagePickerController(_picker:UIImagePickerController, didFinishPickingMediaWithInfo info: [String :Any])
其中第一项,imagePickerControllerDidCancel(_:) ,被当用户点击图像选择器的取消按钮调用。这种方法给你一个机会驳回UIImagePickerController (和可选,做任何必要的清理)。
实现imagePickerControllerDidCancel(_ :)方法
-
在ViewController.swift ,正上方的//MARK: Actions 部分,添加以下内容:
-
//MARK: UIImagePickerControllerDelegate
这是一个注释,以帮助您(和任何人读取您的代码)浏览您的代码,并确定本节适用于图像选择器实施。 -
在您刚添加的评论下方,添加以下方法:
-
funcimagePickerControllerDidCancel(_picker:UIImagePickerController) { -
}
-
在这种方法中,添加以下代码行:
-
// Dismiss the picker if the user canceled. -
dismiss(animated:true,completion: nil)
此代码动画图像选择器控制器的解除。
你的imagePickerControllerDidCancel(_:) 方法应该是这样的:
-
funcimagePickerControllerDidCancel(_picker:UIImagePickerController) { -
// Dismiss the picker if the user canceled. -
dismiss(animated:true,completion: nil) -
}
第二UIImagePickerControllerDelegate 是需要实现的方法,imagePickerController(_:didFinishPickingMediaWithInfo:) ,得到当用户选择照片调用。此方法让您有机会对用户从选择器中选择的图像执行某些操作。在您的情况下,您将获取所选图像并在图像视图中显示。
实现imagePickerController(_:didFinishPickingMediaWithInfo :)方法
-
下面的imagePickerControllerDidCancel(_:) 方法,添加下面的方法:
-
funcimagePickerController(_picker:UIImagePickerController, didFinishPickingMediaWithInfo info: [String :Any]) { -
}
-
在这种方法中,添加以下代码行:
-
// The info dictionary may contain multiple representations of the image. You want to use the original. -
guardletselectedImage = info[UIImagePickerControllerOriginalImage]as?UIImage else { -
fatalError("Expected a dictionary containing an image, but was provided the following: \(info)") -
}
的info 字典始终包含在该选择器所选择的原始图像。它还可以包含该图像的编辑版本(如果存在)。为了简单起见,您将使用原始,未编辑的图像餐照片。 此代码访问从原始的,未经编辑的图像info 字典。它安全地解开了字典返回可选的,蒙上它作为一个UIImage 对象。期望的是,解包和铸造操作永远不会失败。如果他们这样做,它代表您的应用程序中的一个错误,需要在设计时修复。该fatalError() 方法记录一个错误信息到控制台,包括的内容info 字典,然后使应用程序终止,防止它持续处于无效状态。 -
添加此行代码以在先前创建的图像视图出口中设置所选图像:
-
// Set photoImageView to display the selected image. -
photoImageView.image = selectedImage
-
添加以下代码行以关闭图像选择器:
-
// Dismiss the picker. -
dismiss(animated:true,completion: nil)
你的imagePickerController(_:didFinishPickingMediaWithInfo) 方法应该是这样的:
-
funcimagePickerController(_picker:UIImagePickerController, didFinishPickingMediaWithInfo info: [String :Any]) { -
-
// The info dictionary may contain multiple representations of the image. You want to use the original. -
guardletselectedImage = info[UIImagePickerControllerOriginalImage]as?UIImage else { -
fatalError("Expected a dictionary containing an image, but was provided the following: \(info)") -
} -
-
// Set photoImageView to display the selected image. -
photoImageView.image = selectedImage -
-
// Dismiss the picker. -
dismiss(animated:true,completion: nil) -
}
检查点:运行你的应用程序。当您点击图像视图时会发生什么?
该应用程序与终止SIGABRT 信号。这意味着发生了严重到导致应用程序中止的错误。在这种情况下,当您尝试显示图像选择器时会出现问题。系统必须在访问其照片库之前要求用户许可。在iOS 10和更高版本中,您必须提供照片库使用说明。此说明解释了您的应用想要访问照片库的原因。
添加照片库使用说明
-
在项目导航栏中选择Info.plist 。 Xcode在编辑器区域中显示属性列表编辑器。属性列表是一个结构化文本文件,其中包含有关应用程序的基本配置信息。属性列表的根是一个字典,它保存一组预定义的键及其值。
探索进一步
有关可能的更多信息info.plist 键,请参阅信息属性列表键参考。
-
如果属性列表中的最后一个项目是数组,请确保数组已折叠。如果将项目添加到展开的数组,它将添加一个子项目。如果添加一个项目一个折叠的阵列,它增加了一个兄弟到阵列。 -
要添加新项目,请将鼠标悬停在属性列表中的最后一个项目上,并在出现时单击添加按钮(或选择编辑器>添加项目)。  -
在弹出菜单中,向下滚动并选择隐私 - 照片库使用说明。  -
在新行中,确保类型设置为字符串。然后,双击值部分并输入Allows you to add photos to your meals.  -
完成编辑描述字符串后,按返回键。
检查点:再次运行你的应用程序。这次你应该能够点击图像视图来拉起图像选择器。您需要在要求授予FoodTracker应用访问照片权限的警报上单击“确定”。然后,您可以单击取消按钮关闭选取器,或打开相机胶卷并单击图像以选择它,并将其设置为图像视图中的图像。
如果你看看模拟器中可用的照片,你会注意到,它不包括任何食物的照片。您可以将自己的图像直接添加到模拟器中,以使用适当的示例内容测试FoodTracker应用程序。你可以找到内的样品图像Images/ 在这个课程结束的下载文件文件夹,或使用自己的图像。
将图像添加到iOS模拟器
-
如果需要,在模拟器中运行您的应用程序。 -
在计算机上,选择要添加的图像。 -
将图像拖放到模拟器中。 模拟器会打开照片应用并显示您添加的图片。 
检查点:运行你的应用程序。您应该能够点击图像视图来拉起图像选择器。打开相机胶卷,然后单击添加到模拟器中的一个图像以选择它,并将其设置为图像视图中的图像。
源码见github:03_使用视图控制器 |