iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 11
1
自我挑戰組

Hey! UIKit, 做個朋友吧~系列 第 11

Day 11: UITextField三部曲-住海邊的UITextFieldDelegate

要聊到Keyboard-Related Properties,就不能不說說住海邊的UITextFieldDelegate。

textField使用他的delegate來管理內容。
當使用者與textField互動時,textField便會發送delegate,讓他有機會對接下來發生的事情做控制,真是個討厭的抓耙子。
你可以使用delegate裡的方法來管理使用者能否開始或結束編輯,或是在使用者輸入時驗證內容。例如輸入帳號密碼時同步通知格式不符,或是限制最大輸入字數等等都是用delegate實現的,非常的住海邊,感覺就是個會情緒勒索的父母。

Showing and Hiding Keyboard

先來說說讓鍵盤彈出和收回的原理-first responder。

當textField變成first responder的時候,系統會自動跳出鍵盤,並將鍵盤輸入的內容回饋到textField裡。
一般當你點擊textField的時候,textField就會變成first responder;但如果你要強迫使用者輸入訊息的話,也可以使用becomeFirstResponder()這個方法來讓textField變成first responder喔!
例如我們在viewDidLoad()裡對idTextField呼叫becomeFirstResponder():

我才剛打開app什麼也還沒做,就馬上跳鍵盤出來叫我編輯帳號,也太霸道了,都不管人家受不受得了!

如果需要隱藏鍵盤的話,可以呼叫resignFirstResponder()這個方法來使textField從first responder的身份中退出。你可以設定使用者某些特定的動作會使鍵盤收起,例如點擊return。

但我要怎麼知道使用者點擊了return?這就需要UITextFieldDelegate的幫忙了。
當class繼承了UITextFieldDelegate,只要使用者操作相關的互動,textField就會傳送一個delegate給UITextFieldDelegate,並呼叫相對應的function,你可以在function內定義響應的動作。

就本次案例來說,我們希望使用者點擊return後收回鍵盤,而使用者點擊return後,textField會發送delegate並呼叫textFieldShouldReturn(:)。
所以我們在textFieldShouldReturn(
:)裡定義resignFirstResponder()並回傳true。

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}

還沒結束呢!接下來我們要讓class繼承UITextFieldDelegate:

class ViewController: UIViewController, UITextFieldDelegate {}

並決定有哪些textField要啟用ViewController所繼承的Delegate。

idTextField.delegate = self

在這裡我們只讓idTextField啟用delegate來比較看看差異。

啟用了delegate的idTextField一點擊return就馬上收回了鍵盤,但沒有繼承的passwordTextField怎麼點94收不起來,ㄎ憐。

以下是textField發出delegate並呼叫function的程序:

  • 使用者點擊textField後,在轉為first responder之前會先發出delegate,呼叫textFieldShouldBeginEditing(_:)。
  • 轉為first responder並發出Keyboard-Related Notifications後,再發出一次delegate,呼叫textFieldDidBeginEditing(_:)並發出textDidBeginEditingNotification。
  • 在編輯期間的內文變動都會發出delegate,呼叫textField(_:shouldChangeCharactersIn:replacementString:)並發出textDidChangeNotification。限制輸入字數的功能就是用這個function實現的。
  • 如果有設置clean button的overlay,在使用者點擊overlay時會發出delegate並呼叫textFieldShouldClear(_:)。
  • 使用者點擊鍵盤的return時會發出delegate,並呼叫textFieldShouldReturn(_:)。
  • 當結束編輯後,在textField退出first responder的身份之前會先發出delegate,並呼叫textFieldShouldEndEditing(_:)。若要格式化使用者輸入的文字,可以利用此function生效textField裡的文字。
  • 退出first responder並發出Keyboard-Related Notifications後,再發出一次delegate並呼叫textFieldDidEndEditing(_:)。

來實作一波限制字數的功能,在textField(_:shouldChangeCharactersIn:replacementString:)裡加入響應的動作:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let countOfWords = textField.text!.count - range.length + string.count
    if countOfWords > 10 {
        return false
    }
    wordsCountingLabel.text = "\(countOfWords) / 10"
    return true
}

其中range是要被取代的字元,string是取代的新字串,總字數就是countOfWords。
當你的編輯會使countOfWords大於10的時候,function就會回傳false,禁止你此次的編輯生效。

Notifications

不知道大家有沒有發現,我鍵盤都已經彈出來了,textField還傻傻的待在原地不知道要跑。現在是還沒有被鍵盤遮到啦,如果有一天被遮到了呢!!(家長語氣)
所以這時候就必須更新介面來確保textField的編輯是可視的。
apple提供了一些關於鍵盤的通知,讓你可以在接收到這些通知後對視圖進行更新:

  • textField成為first responder之後
    • keyboardWillShowNotification
    • keyboardDidShowNotification
    • keyboardWillChangeFrameNotification
    • keyboardDidChangeFrameNotification
  • textField退出first responder之後
    • keyboardWillHideNotification
    • keyboardDidHideNotification

每個通知裡都有一個包含鍵盤大小的userInfo,可以利用這個參數重新調整你的畫面位置以避免畫面被鍵盤遮擋。
一般的狀況下可以重新定義view的大小,將view的大小減去鍵盤的高度;而視圖是嵌入scrollView裡的話,則可以將畫面向上捲動鍵盤大小的距離,看起來就像視圖跟著鍵盤移動了~

(圖片來源:UITextField)

因為scrollView在第15天左右才會講到,大家應該還不是很熟悉,所以就先示範第一種狀況就好(迴避眼神)。

首先先建立2個當中心收到通知後會呼叫的function:

@objc func keyboardShow(_ notification: Notification) {
    //從userInfo中取得鍵盤的frame -> size
    let userInfo = notification.userInfo!
    let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let intersection = keyboardSize.intersection(view.frame)
    
    view.frame.size = CGSize(width: view.frame.width, height: view.frame.height - intersection.height)
    newTextField.center = CGPoint(x: view.frame.width / 2, y: view.frame.height / 2)
}
    
@objc func keyboardHide(_ notification: Notification) {
    view.frame.size = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    newTextField.center = CGPoint(x: view.frame.width / 2, y: view.frame.height / 2)
}

剛剛提到每個通知裡都有一個包含鍵盤大小的userInfo,所以當keyboard彈出的的時候,先從userInfo裡取出鍵盤的size,再用他resize畫面大小及reposition textField的位置。
而當鍵盤退出的時候,讓畫面恢復至螢幕的大小,再回復textField的位置。

接下來加入2個通知,讓viewController成為觀察者,如果觀察到指定的notification,就會執行相對應的selector function。
這裡我們設定觀察到keyboardWillShowNotification時執行keyboardShow(:),觀察到keyboardWillHideNotification時執行keyboardHide(:)。而object決定了觀察者只接受誰發出來的通知,如果設定為nil他就不會啟動篩選,也就是只要收到通知,不管是誰發出來的都OK。

NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)

Attributes

textField實作了一個叫做UITextInputTraits的protocol,裡面的屬性可以客製化textField的鍵盤。除了使用者當前語言的標準鍵盤以外,還提供了數字、URL、e-mail等特定訊息的專用鍵盤,你可以利用這個protocol裡的屬性去調整鍵盤。

Capitalization

這個function決定了你自動大寫的時機,有allCharacters(全大寫)、none(全小寫)、sentences(句子首字大寫)和words(單字首字大寫)四種。使用autocapitalizationType這個變數。
預設是sentences,這裡demo單字首字大寫:

newTextField.autocapitalizationType = .words

Correction / Spell Checking

有時候只是想打siu恥,系統就自動幫你變成sounds恥,是在sounds幾點的啦!火星文錯了嗎?
這個就是correction幹的好事!autoCorrection會偵測你所輸入的文字,列出相依性高的單字們。於是在你打出一個單字庫裡沒有的字時,他就會自動幫你切換成相依性最高的那一個,根本是火星文殺手!

(怨念很深)

這個時候只要把autocorrectionType設為no,系統就不會再雞婆了!!哈哈!!

而有時打出單字庫裡沒有的字時下標會出現紅色毛毛蟲,這就是拼字檢查。

newTextField.autocorrectionType = .no
newTextField.spellCheckingType = .yes


範例中我把自動校正關掉了,所以就算打siu他也不會給我改掉,但我拼字檢查還開著,所以會出現紅色毛毛蟲。

Correction和Spell Checking裡除了yes與no之外還有default,Correction的default會由系統決定要不要開啟自動校正,怎麼聽起來毛毛的...
而Spell Checking的default則是由Correction決定,Correction是yes他就yes,是no他就no,愛哭愛跟路。

Keyboard Type

apple除了使用者所在地區的預設鍵盤外,還提供了很多有趣的鍵盤。像是數字only鍵盤、e-mail鍵盤(@在第一頁)、URL鍵盤(一鍵 .com)等等。因為實在是太多了,就不一一demo了,大家可以每個選來玩玩看,用keyboardType這個變數指定。

Appearance

appearance決定了鍵盤的外觀,預設是light,也有dark可以選擇,超炫砲的。

newTextField.keyboardAppearance = .dark

Return Key

apple也為return這個鍵提供了很多樣式,全部的鍵只有他有,就他最特別。
使用returnKeyType這個變數指定,其實也沒什麼大不了的,就你選哪個type,按鍵上就寫什麼字。除了continue、return和next是灰色以外,其他的形式都是藍色的。不過不管你改成怎樣的形式,都還是會響應textFieldShouldReturn(_:)這個function。

呼~終於講完Keyboard-Related Properties了,花了很多時間理解delegate,但覺得很有收穫。
下一回是UITextField的精采大結局,會講overlay和其他額外的特性,大家不要錯過喔~


上一篇
Day 10: UITextField三部曲-龍紋身的UITextField
下一篇
Day 12: UITextField三部曲-直搗工作室的UITextField
系列文
Hey! UIKit, 做個朋友吧~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言