Start Developing iOS Apps (Swift)->使用視圖控制器(二)

創(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

  1. 打開Object library
  2. 在Object library中,在過(guò)濾字段中輸入tap gesture快速找到Tap Gesutre Recognizer 對(duì)象。
  3. 從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代碼

  1. 按住Control鍵,從場(chǎng)景dock的手勢(shì)識(shí)別器處拖拽一條線到右側(cè)的編輯器的代碼處,停在如圖所示的位置。


    image: ../Art/WWVC_gesturerecognizer_dragaction_2x.png
  2. 在彈出的對(duì)話框中,在Connection字段選擇Action。
  3. Name字段,填入selectImageFromPhotoLibrary。
  4. Type字段,選擇UITapGestureRecognizer。
    你的對(duì)話框看起來(lái)像這樣:


    image: ../Art/WWVC_gesturerecognizer_addaction_2x.png
    image: ../Art/WWVC_gesturerecognizer_addaction_2x.png
  5. 點(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é)議

  1. 回到標(biāo)準(zhǔn)編輯器。


    image: ../Art/standard_toggle_2x.png
  2. 在project navigator,選擇ViewController.swift。
  3. 在ViewController.swift中,找到class這行。
class ViewController: UIViewController, UITextFieldDelegate {
  1. 在UITextFieldDelegate后面,添加逗號(hào)和UIImagePickerControllerDelegate來(lái)采用這個(gè)協(xié)議。
class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate {
  1. 在UIImagePickerControllerDelegate后面,添加逗號(hào)和UINavigationControllerDelegate來(lái)采用這個(gè)協(xié)議。

現(xiàn)在,回到你定義的action方法,selectImageFromPhotoLibrary(_:),來(lái)完成它的實(shí)現(xiàn)。

實(shí)現(xiàn)名為selectImageFromPhotoLibrary(_:)的 action 方法

  1. 在ViewController.swift中,找到你之前添加的selectImageFromPhotoLibrary(_:)方法。
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
}   
  1. 在這個(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.

  1. 添加下面這些代碼來(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()   
  1. 添加代碼。
// 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)。這意味著你可以直接使用縮寫形式.photoLibrary 來(lái)表示UIImagePickerControllerSourceType.photoLibrary。回想一下,只要知道是枚舉值類型就可以采用這種縮寫形式。

  1. 添加代碼來(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)有明確的寫,但是這個(gè)方法是有一個(gè)隱式的self對(duì)象執(zhí)行的。這個(gè)方法請(qǐng)求ViewController呈現(xiàn)由imagePickerController定義的一個(gè)視圖控制器。把a(bǔ)nimated參數(shù)值設(shè)為true會(huì)以動(dòng)畫的方式來(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(_:)方法

  1. 在ViewController.swift中,在//MARK: Actions部分上面,添加:
//MARK: UIImagePickerControllerDelegate

這個(gè)注釋幫助你導(dǎo)航到代碼位置。

  1. 緊跟著注釋,添加下面這個(gè)方法:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
}
  1. 在這個(gè)方法中,輸入下面代碼:
// Dismiss the picker if the user canceled.
dismiss(animated: true, completion: nil)    

這個(gè)代碼會(huì)帶動(dòng)畫的移除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:) 方法

  1. 在imagePickerControllerDidCancel(_:)方法下面添加方法:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
}       
  1. 在這個(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)。

  1. 添加下面這行代碼,它把選中的圖片設(shè)置到image view上。
// Set photoImageView to display the selected image.
photoImageView.image = selectedImage    
  1. 添加下面代碼來(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ā)生了什么?

image: ../Art/WWVC_app_abort_2x.png

應(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使用描述

  1. 在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。

  1. 如果屬性列表最后一個(gè)項(xiàng)目是數(shù)組,確保它是折疊的。如果你在一個(gè)展開的數(shù)組上添加項(xiàng)目,它會(huì)添加為一個(gè)子項(xiàng)目。如果你添加項(xiàng)目到折疊的數(shù)組,那么就會(huì)添加一個(gè)兄弟數(shù)組。
  2. 添加新項(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
  3. 在彈出的菜單中,滾動(dòng)然后選擇 Privacy - Photo Library Usage Description。
    image: ../Art/WWVC_addphotolibrarydescription_2x.png
  4. 在新行中,確保Type (類型)設(shè)置的是String。然后,雙擊值區(qū)域,并鍵入Allows you to add photos to your meals.


    image: ../Art/WWVC_addingDescriptionString_2x.png
  5. 當(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,或者打開相冊(cè)然后點(diǎn)擊一張圖片讓它顯示在image view上。

image: ../Art/WWVC_sim_imagepicker_2x.png

如果你看遍了模擬器中照片,你會(huì)發(fā)現(xiàn)它沒(méi)有食品的照片。你可以直接添加自己的圖片到模擬器,以便使用合適的內(nèi)容來(lái)測(cè)試FoodTracker應(yīng)用。你可以在下載文件的Images文件夾里找到圖片,下載地址在本課最后,或者使用你自己的圖片。

添加圖片到iOS模擬器

  1. 如有必要,在模擬器中運(yùn)行應(yīng)用。
  2. 在你的電腦中,選擇你想要添加的圖片。
  3. 拖拽圖片到模擬器。


    image: ../Art/WWVC_sim_dragphoto_2x.png

模擬器打開Photos應(yīng)用,并且顯示你添加的圖片。

image: ../Art/WWVC_sim_choosephoto_2x.png

檢查點(diǎn):運(yùn)行應(yīng)用。你應(yīng)該能輕拍image view來(lái)顯示一個(gè)image picker。打開相冊(cè),點(diǎn)擊你添加到模擬器中的圖片,選擇它來(lái)設(shè)置iamge view的圖片。

image: ../Art/WWVC_sim_selectedphoto_2x.png

小結(jié)

在本課中,你學(xué)到了關(guān)于視圖控制器生命周期方法,并且使用它們配置了你的視圖控制器內(nèi)容。你也學(xué)習(xí)了如何給視圖添加手勢(shì)識(shí)別器,并且知道如何從photo library中選擇照片。場(chǎng)景開始看上去像一個(gè)真實(shí)的應(yīng)用了。在下一課中,你將添加自定義的控件到場(chǎng)景。

注意
想看本課的完整代碼,下載這個(gè)文件并在Xcode中打開。

下載文件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,417評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,267評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,471評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,698評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,204評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,436評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容