创建手势识别器
image view不是控件,它没有被设计成像button或者slider那样对输入作出响应。例如,你不能简单的创建一个方法,让它能够在image view 被用户点击的时候被触发。(如果你在刚才拖拽image view的时候稍加留意,你会发现弹出对话框的Connection字段不能选择Action。)
幸运的是,只要通过添加手势识别器(gesture recognizers)就能让视图轻松的获得和控件一样的能力。手势识别器是你附加在视图上的对象,它可以让视图以控件的方式响应用户。手势识别器解释触摸,判断它们是否符合特殊的手势,例如滑动(swipe)、捏合(pinch)、或者旋转(rotation)。你能够写一个方法,当手势识别器识别到它被分配的手势时这个方法会被调用。这正是你想为image view做的。
附加一个轻拍(tap)手势识别器(UITapGestureRecognizer)给image view,它将识别用户对image view 的轻拍手势。在storyboard中你很容易做到这点。
添加轻拍手势识别器给你的image view
- 打开Object library
- 在Object library中,在过滤字段中输入tap gesture快速找到Tap Gesutre Recognizer 对象。
-
从Object library拖拽Tap Gesture Recognizer对象到你的场景,放到image view 的上面。
Tap Gesture Recognizer对象出现在了菜品的场景dock中。
连接手势识别器到代码
现在连接手势识别器到代码中的action方法。
连接手势识别器到ViewController.swift代码
-
按住Control键,从场景dock的手势识别器处拖拽一条线到右侧的编辑器的代码处,停在如图所示的位置。
- 在弹出的对话框中,在Connection字段选择Action。
- Name字段,填入selectImageFromPhotoLibrary。
-
Type字段,选择UITapGestureRecognizer。
你的对话框看起来像这样:
- 点击连接。
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
}
创建一个Image Picker来响应用户的点击
当用户点击image view的时候,他们应该可以从相册中选择一张照片,或者自己拍摄一张。幸运的是,UIImagePickerController类已经有了这些功能。一个image picker controller(图片拾取控制器) 管理用于拍摄照片和选择图片的用户界面,以便在你的应用中使用。就像你使用text field的时候需要text field delegate一样,你在使用image picker的时候也需要image picker controller delegate。这个委托协议的名字是UIImagePickerControllerDelegate,你要声明为image picker controller的委托的对象是ViewController。
首先,ViewController需要采用UIImagePickerControllerDelegate协议。由于ViewController将要承担显示image picker controller的责任,所以它也需要采用UINavigationControllerDelegate协议,这样就可以让ViewController承担一些基本的导航功能。
采用UIImagePickerControllerDelegate和UINavigationControllerDelegate协议
-
回到标准编辑器。
- 在project navigator,选择ViewController.swift。
- 在ViewController.swift中,找到class这行。
class ViewController: UIViewController, UITextFieldDelegate {
- 在UITextFieldDelegate后面,添加逗号和UIImagePickerControllerDelegate来采用这个协议。
class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate {
- 在UIImagePickerControllerDelegate后面,添加逗号和UINavigationControllerDelegate来采用这个协议。
现在,回到你定义的action方法,selectImageFromPhotoLibrary(_:),来完成它的实现。
实现名为selectImageFromPhotoLibrary(_:)的 action 方法
- 在ViewController.swift中,找到你之前添加的selectImageFromPhotoLibrary(_:)方法。
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
}
- 在这个方法的两个花括号({ })之间,加入如下代码:
// Hide the keyboard.
nameTextField.resignFirstResponder()
这个代码确保在text filed是第一响应者的时候用户点击了image view,键盘会消失(也就是text field注销了第一响应者)。This code ensures that if the user taps the image view while typing in the text field, the keyboard is dismissed properly.
- 添加下面这些代码来创建一个image picker controller。
// UIImagePickerController is a view controller that lets a user pick media from their photo library.
let imagePickerController = UIImagePickerController()
- 添加代码。
// Only allow photos to be picked, not taken.
imagePickerController.sourceType = .photoLibrary
这行代码设置了image picker controller的源,或者说要从何处获取图片。这个.photoLibrary选项使用的是模拟器的相册。
imagePickerController.sourceType的类型是UIImagePickerControllerSourceType,它是一个枚举类型(enumeration)。这意味着你可以直接使用缩写形式.photoLibrary 来表示UIImagePickerControllerSourceType.photoLibrary。回想一下,只要知道是枚举值类型就可以采用这种缩写形式。
- 添加代码来设置image picker controller的委托为ViewController。
// Make sure ViewController is notified when the user picks an image.
imagePickerController.delegate = self
6紧接着,添加代码
present(imagePickerController, animated: true, completion: nil)
present(_:animated:completion:)是一个在ViewController上调用的方法。虽然没有明确的写,但是这个方法是有一个隐式的self对象执行的。这个方法请求ViewController呈现由imagePickerController定义的一个视图控制器。把animated参数值设为true会以动画的方式来呈现image picker controller。completion参数是指完成处理程序(completion handle),是在这个方法执行完毕之后执行的一段代码。因为你还不需要做这些,所以把它设置为nil就好。
现在你的selectImageFromPhotoLibrary(_:)方法看起来是这样的:
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
// Hide the keyboard.
nameTextField.resignFirstResponder()
// UIImagePickerController is a view controller that lets a user pick media from their photo library.
let imagePickerController = 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)
}
当image picker controller被呈现之后,你可以通过委托方法来和它互动。要想给用户选择照片的能力,你需要实现两个定义在UIImagePickerControllerDelegate:里的委托方法:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
第一个方法,imagePickerControllerDidCancel(_:),会在用户点击图像选择器(image picker)的取消(Cancel)按钮的时候调用。这个方法可以让你有机会关闭UIImagePickerController(并且可以选择任何有必要的清理)。
实现imagePickerControllerDidCancel(_:)方法
- 在ViewController.swift中,在//MARK: Actions部分上面,添加:
//MARK: UIImagePickerControllerDelegate
这个注释帮助你导航到代码位置。
- 紧跟着注释,添加下面这个方法:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
}
- 在这个方法中,输入下面代码:
// Dismiss the picker if the user canceled.
dismiss(animated: true, completion: nil)
这个代码会带动画的移除image picker controller。
你的imagePickerControllerDidCancel(_:)方法看上去是这样的:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// Dismiss the picker if the user canceled.
dismiss(animated: true, completion: nil)
}
第二个方法需要实现的UIImagePickerControllerDelegate,imagePickerController(_:didFinishPickingMediaWithInfo:),在你选择照片的时候调用。这个方法让你有机会对从选择器来的图片做一些事。本例中,你将选择图片,然后显示在image view上。
实现imagePickerController(_:didFinishPickingMediaWithInfo:) 方法
- 在imagePickerControllerDidCancel(_:)方法下面添加方法:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
}
- 在这个方法中添加如下代码:
// The info dictionary may contain multiple representations of the image. You want to use the original.
guard let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage else {
fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
}
这个info字典始终包含从拾取器获取的原图。它也可能包含一个原图的编辑版本,如果有的话。简单起见,你将使用没有修改的原图。
这段代码从info字典中访问原始未编辑的图片。它安全地解包由字典返回的可选对象,并把它转化为UIImage对象。期望是解包和转换都没有错误。如果有错,就相当于应用有bug,需要在设计的时候修补它。 fatalError()方法在控制台打印一个错误信息,包括info字典的内容,然后使应用终——防止应用继续处于无效状态。
- 添加下面这行代码,它把选中的图片设置到image view上。
// Set photoImageView to display the selected image.
photoImageView.image = selectedImage
- 添加下面代码来移除image picker。
// Dismiss the picker.
dismiss(animated: true, completion: nil)
你的imagePickerController(_:didFinishPickingMediaWithInfo)方法看上去是这样的:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// The info dictionary may contain multiple representations of the image. You want to use the original.
guard let selectedImage = 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)
}
检查点:运行应用。当你点击image view的时候发生了什么?
应用以SIGABRT信号终止。这意味着发生了一个足以导致应用终止的严重错误。在本例中,当你尝试显示image picker的时候这个问题会发生。系统在访问photo library之前必须询问用户是否允许。在iOS 10之后,你必须提供photo library使用描述。这个描述解释了你为什么想要访问photo library。
添加一个photo library使用描述
-
在project navigator,选择Info.plist。
Xcode在编辑器区域显示属性列表(property list)。属性列表是一个结构化的文本文件,它包含了关于应用的必要配置信息。属性列表的根是一个字典,它保存一组预定义的键和它们的值。
进一步探索
更多关于info.plist的信息,详见Information Property List Key Reference。
- 如果属性列表最后一个项目是数组,确保它是折叠的。如果你在一个展开的数组上添加项目,它会添加为一个子项目。如果你添加项目到折叠的数组,那么就会添加一个兄弟数组。
-
添加新项目,鼠标悬停在属性列表的最后一个项目上,当出现添加按钮(Add button)的时候,点击它(或者选择Editor > Add Item)。
- 在弹出的菜单中,滚动然后选择 Privacy - Photo Library Usage Description。
-
在新行中,确保Type (类型)设置的是String。然后,双击值区域,并键入Allows you to add photos to your meals.
- 当你输入完描述文件后,按下回车键。
检查点:再次运行应用。这次你应该能够点击image view来显示一个image picker。你需要在弹出的警告框上点击OK,这个警告框询问是否给FoodTracker应用访问Photos的许可。然后,你可以点击Cancel 按钮来移除picker,或者打开相册然后点击一张图片让它显示在image view上。
如果你看遍了模拟器中照片,你会发现它没有食品的照片。你可以直接添加自己的图片到模拟器,以便使用合适的内容来测试FoodTracker应用。你可以在下载文件的Images文件夹里找到图片,下载地址在本课最后,或者使用你自己的图片。
添加图片到iOS模拟器
- 如有必要,在模拟器中运行应用。
- 在你的电脑中,选择你想要添加的图片。
-
拖拽图片到模拟器。
模拟器打开Photos应用,并且显示你添加的图片。
检查点:运行应用。你应该能轻拍image view来显示一个image picker。打开相册,点击你添加到模拟器中的图片,选择它来设置iamge view的图片。
小结
在本课中,你学到了关于视图控制器生命周期方法,并且使用它们配置了你的视图控制器内容。你也学习了如何给视图添加手势识别器,并且知道如何从photo library中选择照片。场景开始看上去像一个真实的应用了。在下一课中,你将添加自定义的控件到场景。
注意
想看本课的完整代码,下载这个文件并在Xcode中打开。