創(chuàng)建手勢(shì)識(shí)別器
image view不是控件液茎,它沒(méi)有被設(shè)計(jì)成像button或者slider那樣對(duì)輸入作出響應(yīng)。例如免钻,你不能簡(jiǎn)單的創(chuàng)建一個(gè)方法莫秆,讓它能夠在image view 被用戶點(diǎn)擊的時(shí)候被觸發(fā)。(如果你在剛才拖拽image view的時(shí)候稍加留意催什,你會(huì)發(fā)現(xiàn)彈出對(duì)話框的Connection字段不能選擇Action涵亏。)
幸運(yùn)的是,只要通過(guò)添加手勢(shì)識(shí)別器(gesture recognizers)就能讓視圖輕松的獲得和控件一樣的能力。手勢(shì)識(shí)別器是你附加在視圖上的對(duì)象气筋,它可以讓視圖以控件的方式響應(yīng)用戶拆内。手勢(shì)識(shí)別器解釋觸摸,判斷它們是否符合特殊的手勢(shì)宠默,例如滑動(dòng)(swipe)矛纹、捏合(pinch)、或者旋轉(zhuǎn)(rotation)光稼。你能夠?qū)懸粋€(gè)方法,當(dāng)手勢(shì)識(shí)別器識(shí)別到它被分配的手勢(shì)時(shí)這個(gè)方法會(huì)被調(diào)用孩等。這正是你想為image view做的艾君。
附加一個(gè)輕拍(tap)手勢(shì)識(shí)別器(UITapGestureRecognizer)給image view,它將識(shí)別用戶對(duì)image view 的輕拍手勢(shì)肄方。在storyboard中你很容易做到這點(diǎn)冰垄。
添加輕拍手勢(shì)識(shí)別器給你的image view
- 打開(kāi)Object library
- 在Object library中,在過(guò)濾字段中輸入tap gesture快速找到Tap Gesutre Recognizer 對(duì)象权她。
-
從Object library拖拽Tap Gesture Recognizer對(duì)象到你的場(chǎng)景虹茶,放到image view 的上面。
image: ../Art/WWVC_gesturerecognizer_drag_2x.png
Tap Gesture Recognizer對(duì)象出現(xiàn)在了菜品的場(chǎng)景dock中隅要。
image: ../Art/WWVC_scenedock_2x.png
連接手勢(shì)識(shí)別器到代碼
現(xiàn)在連接手勢(shì)識(shí)別器到代碼中的action方法蝴罪。
連接手勢(shì)識(shí)別器到ViewController.swift代碼
-
按住Control鍵,從場(chǎng)景dock的手勢(shì)識(shí)別器處拖拽一條線到右側(cè)的編輯器的代碼處步清,停在如圖所示的位置要门。
image: ../Art/WWVC_gesturerecognizer_dragaction_2x.png - 在彈出的對(duì)話框中,在Connection字段選擇Action廓啊。
- Name字段欢搜,填入selectImageFromPhotoLibrary。
-
Type字段谴轮,選擇UITapGestureRecognizer炒瘟。
你的對(duì)話框看起來(lái)像這樣:
image: ../Art/WWVC_gesturerecognizer_addaction_2x.png - 點(diǎn)擊連接。
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
}
創(chuàng)建一個(gè)Image Picker來(lái)響應(yīng)用戶的點(diǎn)擊
當(dāng)用戶點(diǎn)擊image view的時(shí)候第步,他們應(yīng)該可以從相冊(cè)中選擇一張照片疮装,或者自己拍攝一張。幸運(yùn)的是雌续,UIImagePickerController類已經(jīng)有了這些功能斩个。一個(gè)image picker controller(圖片拾取控制器) 管理用于拍攝照片和選擇圖片的用戶界面,以便在你的應(yīng)用中使用驯杜。就像你使用text field的時(shí)候需要text field delegate一樣受啥,你在使用image picker的時(shí)候也需要image picker controller delegate。這個(gè)委托協(xié)議的名字是UIImagePickerControllerDelegate,你要聲明為image picker controller的委托的對(duì)象是ViewController滚局。
首先居暖,ViewController需要采用UIImagePickerControllerDelegate協(xié)議。由于ViewController將要承擔(dān)顯示image picker controller的責(zé)任藤肢,所以它也需要采用UINavigationControllerDelegate協(xié)議太闺,這樣就可以讓ViewController承擔(dān)一些基本的導(dǎo)航功能。
采用UIImagePickerControllerDelegate和UINavigationControllerDelegate協(xié)議
-
回到標(biāo)準(zhǔn)編輯器嘁圈。
image: ../Art/standard_toggle_2x.png - 在project navigator省骂,選擇ViewController.swift。
- 在ViewController.swift中最住,找到class這行钞澳。
class ViewController: UIViewController, UITextFieldDelegate {
- 在UITextFieldDelegate后面,添加逗號(hào)和UIImagePickerControllerDelegate來(lái)采用這個(gè)協(xié)議涨缚。
class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate {
- 在UIImagePickerControllerDelegate后面轧粟,添加逗號(hào)和UINavigationControllerDelegate來(lái)采用這個(gè)協(xié)議。
現(xiàn)在脓魏,回到你定義的action方法兰吟,selectImageFromPhotoLibrary(_:),來(lái)完成它的實(shí)現(xiàn)茂翔。
實(shí)現(xiàn)名為selectImageFromPhotoLibrary(_:)的 action 方法
- 在ViewController.swift中混蔼,找到你之前添加的selectImageFromPhotoLibrary(_:)方法。
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
}
- 在這個(gè)方法的兩個(gè)花括號(hào)({ })之間檩电,加入如下代碼:
// Hide the keyboard.
nameTextField.resignFirstResponder()
這個(gè)代碼確保在text filed是第一響應(yīng)者的時(shí)候用戶點(diǎn)擊了image view拄丰,鍵盤會(huì)消失(也就是text field注銷了第一響應(yīng)者)。This code ensures that if the user taps the image view while typing in the text field, the keyboard is dismissed properly.
- 添加下面這些代碼來(lái)創(chuàng)建一個(gè)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
這行代碼設(shè)置了image picker controller的源,或者說(shuō)要從何處獲取圖片卓箫。這個(gè).photoLibrary選項(xiàng)使用的是模擬器的相冊(cè)载矿。
imagePickerController.sourceType的類型是UIImagePickerControllerSourceType,它是一個(gè)枚舉類型(enumeration)烹卒。這意味著你可以直接使用縮寫(xiě)形式.photoLibrary 來(lái)表示UIImagePickerControllerSourceType.photoLibrary闷盔。回想一下旅急,只要知道是枚舉值類型就可以采用這種縮寫(xiě)形式逢勾。
- 添加代碼來(lái)設(shè)置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:)是一個(gè)在ViewController上調(diào)用的方法溺拱。雖然沒(méi)有明確的寫(xiě)逃贝,但是這個(gè)方法是有一個(gè)隱式的self對(duì)象執(zhí)行的。這個(gè)方法請(qǐng)求ViewController呈現(xiàn)由imagePickerController定義的一個(gè)視圖控制器迫摔。把a(bǔ)nimated參數(shù)值設(shè)為true會(huì)以動(dòng)畫(huà)的方式來(lái)呈現(xiàn)image picker controller沐扳。completion參數(shù)是指完成處理程序(completion handle),是在這個(gè)方法執(zhí)行完畢之后執(zhí)行的一段代碼句占。因?yàn)槟氵€不需要做這些沪摄,所以把它設(shè)置為nil就好。
現(xiàn)在你的selectImageFromPhotoLibrary(_:)方法看起來(lái)是這樣的:
@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)
}
當(dāng)image picker controller被呈現(xiàn)之后纱烘,你可以通過(guò)委托方法來(lái)和它互動(dòng)杨拐。要想給用戶選擇照片的能力,你需要實(shí)現(xiàn)兩個(gè)定義在UIImagePickerControllerDelegate:里的委托方法:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
第一個(gè)方法擂啥,imagePickerControllerDidCancel(_:)戏阅,會(huì)在用戶點(diǎn)擊圖像選擇器(image picker)的取消(Cancel)按鈕的時(shí)候調(diào)用。這個(gè)方法可以讓你有機(jī)會(huì)關(guān)閉UIImagePickerController(并且可以選擇任何有必要的清理)啤它。
實(shí)現(xiàn)imagePickerControllerDidCancel(_:)方法
- 在ViewController.swift中,在//MARK: Actions部分上面舱痘,添加:
//MARK: UIImagePickerControllerDelegate
這個(gè)注釋幫助你導(dǎo)航到代碼位置变骡。
- 緊跟著注釋,添加下面這個(gè)方法:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
}
- 在這個(gè)方法中芭逝,輸入下面代碼:
// Dismiss the picker if the user canceled.
dismiss(animated: true, completion: nil)
這個(gè)代碼會(huì)帶動(dòng)畫(huà)的移除image picker controller塌碌。
你的imagePickerControllerDidCancel(_:)方法看上去是這樣的:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// Dismiss the picker if the user canceled.
dismiss(animated: true, completion: nil)
}
第二個(gè)方法需要實(shí)現(xiàn)的UIImagePickerControllerDelegate,imagePickerController(_:didFinishPickingMediaWithInfo:)旬盯,在你選擇照片的時(shí)候調(diào)用台妆。這個(gè)方法讓你有機(jī)會(huì)對(duì)從選擇器來(lái)的圖片做一些事。本例中胖翰,你將選擇圖片接剩,然后顯示在image view上。
實(shí)現(xiàn)imagePickerController(_:didFinishPickingMediaWithInfo:) 方法
- 在imagePickerControllerDidCancel(_:)方法下面添加方法:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
}
- 在這個(gè)方法中添加如下代碼:
// 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)")
}
這個(gè)info字典始終包含從拾取器獲取的原圖萨咳。它也可能包含一個(gè)原圖的編輯版本懊缺,如果有的話。簡(jiǎn)單起見(jiàn)培他,你將使用沒(méi)有修改的原圖鹃两。
這段代碼從info字典中訪問(wèn)原始未編輯的圖片。它安全地解包由字典返回的可選對(duì)象舀凛,并把它轉(zhuǎn)化為UIImage對(duì)象俊扳。期望是解包和轉(zhuǎn)換都沒(méi)有錯(cuò)誤。如果有錯(cuò)猛遍,就相當(dāng)于應(yīng)用有bug馋记,需要在設(shè)計(jì)的時(shí)候修補(bǔ)它号坡。 fatalError()方法在控制臺(tái)打印一個(gè)錯(cuò)誤信息,包括info字典的內(nèi)容抗果,然后使應(yīng)用終——防止應(yīng)用繼續(xù)處于無(wú)效狀態(tài)筋帖。
- 添加下面這行代碼,它把選中的圖片設(shè)置到image view上冤馏。
// Set photoImageView to display the selected image.
photoImageView.image = selectedImage
- 添加下面代碼來(lái)移除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)
}
檢查點(diǎn):運(yùn)行應(yīng)用。當(dāng)你點(diǎn)擊image view的時(shí)候發(fā)生了什么逮光?
應(yīng)用以SIGABRT信號(hào)終止代箭。這意味著發(fā)生了一個(gè)足以導(dǎo)致應(yīng)用終止的嚴(yán)重錯(cuò)誤。在本例中涕刚,當(dāng)你嘗試顯示image picker的時(shí)候這個(gè)問(wèn)題會(huì)發(fā)生嗡综。系統(tǒng)在訪問(wèn)photo library之前必須詢問(wèn)用戶是否允許。在iOS 10之后杜漠,你必須提供photo library使用描述极景。這個(gè)描述解釋了你為什么想要訪問(wèn)photo library。
添加一個(gè)photo library使用描述
-
在project navigator驾茴,選擇Info.plist盼樟。
Xcode在編輯器區(qū)域顯示屬性列表(property list)。屬性列表是一個(gè)結(jié)構(gòu)化的文本文件锈至,它包含了關(guān)于應(yīng)用的必要配置信息晨缴。屬性列表的根是一個(gè)字典,它保存一組預(yù)定義的鍵和它們的值峡捡。
image: ../Art/WWVC_Property_List_Editor_2x.png
進(jìn)一步探索
更多關(guān)于info.plist的信息击碗,詳見(jiàn)Information Property List Key Reference。
- 如果屬性列表最后一個(gè)項(xiàng)目是數(shù)組们拙,確保它是折疊的稍途。如果你在一個(gè)展開(kāi)的數(shù)組上添加項(xiàng)目,它會(huì)添加為一個(gè)子項(xiàng)目砚婆。如果你添加項(xiàng)目到折疊的數(shù)組晰房,那么就會(huì)添加一個(gè)兄弟數(shù)組。
-
添加新項(xiàng)目射沟,鼠標(biāo)懸停在屬性列表的最后一個(gè)項(xiàng)目上殊者,當(dāng)出現(xiàn)添加按鈕(Add button)的時(shí)候,點(diǎn)擊它(或者選擇Editor > Add Item)验夯。
image: ../Art/WWVC_addInfoPlistItem_2x.png - 在彈出的菜單中猖吴,滾動(dòng)然后選擇 Privacy - Photo Library Usage Description。
image: ../Art/WWVC_addphotolibrarydescription_2x.png -
在新行中挥转,確保Type (類型)設(shè)置的是String海蔽。然后共屈,雙擊值區(qū)域,并鍵入Allows you to add photos to your meals.
image: ../Art/WWVC_addingDescriptionString_2x.png - 當(dāng)你輸入完描述文件后党窜,按下回車鍵拗引。
檢查點(diǎn):再次運(yùn)行應(yīng)用。這次你應(yīng)該能夠點(diǎn)擊image view來(lái)顯示一個(gè)image picker幌衣。你需要在彈出的警告框上點(diǎn)擊OK矾削,這個(gè)警告框詢問(wèn)是否給FoodTracker應(yīng)用訪問(wèn)Photos的許可。然后豁护,你可以點(diǎn)擊Cancel 按鈕來(lái)移除picker哼凯,或者打開(kāi)相冊(cè)然后點(diǎn)擊一張圖片讓它顯示在image view上。
如果你看遍了模擬器中照片楚里,你會(huì)發(fā)現(xiàn)它沒(méi)有食品的照片断部。你可以直接添加自己的圖片到模擬器,以便使用合適的內(nèi)容來(lái)測(cè)試FoodTracker應(yīng)用班缎。你可以在下載文件的Images文件夾里找到圖片蝴光,下載地址在本課最后,或者使用你自己的圖片达址。
添加圖片到iOS模擬器
- 如有必要虱疏,在模擬器中運(yùn)行應(yīng)用。
- 在你的電腦中苏携,選擇你想要添加的圖片。
-
拖拽圖片到模擬器对粪。
image: ../Art/WWVC_sim_dragphoto_2x.png
模擬器打開(kāi)Photos應(yīng)用右冻,并且顯示你添加的圖片。
檢查點(diǎn):運(yùn)行應(yīng)用著拭。你應(yīng)該能輕拍image view來(lái)顯示一個(gè)image picker纱扭。打開(kāi)相冊(cè),點(diǎn)擊你添加到模擬器中的圖片儡遮,選擇它來(lái)設(shè)置iamge view的圖片乳蛾。
小結(jié)
在本課中,你學(xué)到了關(guān)于視圖控制器生命周期方法鄙币,并且使用它們配置了你的視圖控制器內(nèi)容肃叶。你也學(xué)習(xí)了如何給視圖添加手勢(shì)識(shí)別器,并且知道如何從photo library中選擇照片十嘿。場(chǎng)景開(kāi)始看上去像一個(gè)真實(shí)的應(yīng)用了因惭。在下一課中,你將添加自定義的控件到場(chǎng)景绩衷。
注意
想看本課的完整代碼蹦魔,下載這個(gè)文件并在Xcode中打開(kāi)激率。