iOS Apprentice中文版-從0開(kāi)始學(xué)iOS開(kāi)發(fā)-第十五課

來(lái)個(gè)深加工

在你寫(xiě)代碼將文本作為一條新的紀(jì)錄插入列表之前,讓我們先來(lái)改進(jìn)一下新增待辦事項(xiàng)頁(yè)面的工作方式和設(shè)計(jì)。

例如:如果可以自動(dòng)為用戶(hù)彈出鍵盤(pán),而不是當(dāng)用戶(hù)點(diǎn)擊后才彈出鍵盤(pán),這樣體驗(yàn)會(huì)好一些。

為了達(dá)到這個(gè)目的,需要在AddItemViewController.swift中添加一個(gè)方法viewWillAppear():

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        textField.becomeFirstResponder()
    }

視圖控制器在即將可視化之前接收viewWillAppear()的消息,這正是激活文本框的完美時(shí)機(jī)。我們?cè)谶@里發(fā)送becomeFirstResponder()消息。

如果你是在其他平臺(tái)上做這個(gè)事,通常被稱(chēng)為“控制聚焦”。在iOS術(shù)語(yǔ)中,這種控制方法稱(chēng)為“第一響應(yīng)(first responder)”

運(yùn)行app然后點(diǎn)擊?號(hào)按鈕,你可以看到鍵盤(pán)會(huì)自動(dòng)滑出來(lái)了。

(再說(shuō)一次,如果模擬器上鍵盤(pán)沒(méi)有自動(dòng)滑出的話(huà),可以通過(guò)command+K組合鍵喚醒鍵盤(pán))

像這樣的小細(xì)節(jié)通常會(huì)帶來(lái)很好的用戶(hù)體驗(yàn)。比起必須點(diǎn)擊文本框才能打字,現(xiàn)在的做法明顯快了不少。在這個(gè)日新月異的年代中,人們使用產(chǎn)品時(shí)不會(huì)有太多的耐心。比如一點(diǎn)點(diǎn)的瑕疵都會(huì)讓用戶(hù)轉(zhuǎn)移到你競(jìng)爭(zhēng)對(duì)手的產(chǎn)品上去。我總是花大量的精力來(lái)使自己的app盡可能的完美。

所以,我們來(lái)繼續(xù)改進(jìn)一下輸入框。

打開(kāi)故事模版并且選定文本框,進(jìn)入到屬性檢查器并且按照下面的列表進(jìn)行設(shè)置:

1、Placeholder:Name of the Item

2、Font:System 17

3、Adjust to Fit:Uncheck this

4、Capitalization:Sentence

5、Return Key:Done

設(shè)置文本框的屬性

這里有一些選項(xiàng)可以使你配置文本框被激活時(shí)的鍵盤(pán)的屬性。

例如,如果這個(gè)文本框僅可以輸入數(shù)值的話(huà),你可以設(shè)置Keyboard Type為Number Pad。如果這是一個(gè)用于輸入Email地址的文本框,你就可以將Keyboard Type為E-mail Address。對(duì)我們這個(gè)app而言,默認(rèn)的類(lèi)型就正好符號(hào)要求,因?yàn)橛脩?hù)可以隨意輸入自己將要做的事情。

你也可以改變鍵盤(pán)上回車(chē)鍵的名稱(chēng)。默認(rèn)的是“return”,這里我們改為了“Done”。這只是按鈕上的標(biāo)題文本,點(diǎn)擊這個(gè)“Done”并不會(huì)自動(dòng)關(guān)閉當(dāng)前界面,你還需要將鍵盤(pán)上的“Done”按鈕指向到相同的動(dòng)作上,這樣鍵盤(pán)上的Done按鈕才能和導(dǎo)航欄上的Done按鈕起到相同的作用。

選定text field然后打開(kāi)鏈接檢查器,拖拽Did End on Exit事件到視圖控制器,并且選擇done動(dòng)作。

如果你的輔助編輯器還沒(méi)關(guān)的話(huà),也可以直接拖拽到源代碼的done()方法上。

鏈接text field到done()動(dòng)作方法

查看done動(dòng)作的鏈接可以點(diǎn)擊done()方法左邊,屏幕邊緣上的那個(gè)小圓圈,點(diǎn)擊后會(huì)彈出一個(gè)窗口顯示done()方法的鏈接內(nèi)容。

運(yùn)行app,點(diǎn)擊鍵盤(pán)上的Done按鈕,這時(shí)界面會(huì)自動(dòng)關(guān)閉,并且在調(diào)試區(qū)域打印出我們預(yù)置的文本信息。

現(xiàn)在鍵盤(pán)上的Done按鈕和導(dǎo)航欄上的Done按鈕作用一致了

驗(yàn)證用戶(hù)的輸入并且確保它們被接收到了,是必不可缺的一個(gè)步驟。例如,用戶(hù)瞬間點(diǎn)了一下Done按鈕但是什么都沒(méi)輸入會(huì)發(fā)生什么。

在主界面的列表中,新增一個(gè)空白的什么都沒(méi)有的行,可一點(diǎn)也不好,為了避免這件事的發(fā)生你需要在用戶(hù)什么都沒(méi)有輸入前,禁止done按鈕的作用。

當(dāng)然,你需要同時(shí)處理兩個(gè)done按鈕,鍵盤(pán)上的一個(gè),導(dǎo)航欄上的一個(gè)。讓我們先著手處理鍵盤(pán)上的done按鈕,這個(gè)稍微簡(jiǎn)單一些。

選中文本框,打開(kāi)屬性檢查器,選中Auto-enable Return Key。

就這樣,現(xiàn)在你運(yùn)行app,什么也不輸入,點(diǎn)擊鍵盤(pán)上的Done按鈕是不會(huì)起作用的,試試看!

選中Auto-enable Return Key

對(duì)于導(dǎo)航欄上的Done按鈕,工作就復(fù)雜一些了。你需要監(jiān)聽(tīng)每次一次用戶(hù)敲擊鍵盤(pán)后,文本框中的內(nèi)容是否為空。如果為空,則禁用Done按鈕。

如果用戶(hù)的本身是點(diǎn)錯(cuò)了,可以通過(guò)Cancel按鈕退出界面,但是用戶(hù)點(diǎn)擊Done按鈕時(shí),必須保證文本框中有輸入的內(nèi)容。

對(duì)于文本框而言,有兩種輸入方法,敲擊鍵盤(pán)輸入和復(fù)制黏貼,你需要給視圖控制器一個(gè)委托用于文本框。

當(dāng)文本框發(fā)送事件到這個(gè)委托時(shí),你就可以知道文本框的情況了。這個(gè)委托存在于AdditemViewController內(nèi),可以對(duì)文本框的事件進(jìn)行響應(yīng),并且做出合適的動(dòng)作。

一個(gè)視圖控制器允許委托多個(gè)對(duì)象。AdditemViewController已經(jīng)有了一個(gè)委托和一個(gè)數(shù)據(jù)源用于UITableView,因?yàn)樗緛?lái)就是一個(gè)UITableViewController?,F(xiàn)在它要增加一個(gè)委托用于text field的對(duì)象,UITextfiled。

這是兩種不同類(lèi)型的委托并且你要使視圖控制同時(shí)執(zhí)行它們的規(guī)則。關(guān)于委托的更多內(nèi)容我們會(huì)在下一個(gè)課程中講解。

如何定義一個(gè)委托
在iOS系統(tǒng)的SDK里,委托無(wú)處不在,所以我們最好記住它總是由三個(gè)步驟完成。
1、你申明自己有能力成為一個(gè)委托——成為UITextField的委托你需要將UITextFieldDelegate寫(xiě)到視圖控制器的類(lèi)的聲明中去。這樣就告訴了編譯器這個(gè)視圖控制器可以處理來(lái)自文本框的消息。
2、你讓那個(gè)有問(wèn)題的對(duì)象知道視圖控制器愿意成為它的委托——在我們這個(gè)例子里,這個(gè)對(duì)象是UITextField。如果你忘記了告訴文本框它有一個(gè)委托,則這個(gè)文本框永遠(yuǎn)不會(huì)發(fā)送任何消息。
3、執(zhí)行委托方法——如果你沒(méi)有對(duì)你發(fā)出的消息進(jìn)行響應(yīng),那么委托根本不會(huì)成立。
通常,委托方法是可選的,所以你不需要執(zhí)行全部的委托方法。例如:UITextFieldDelegate實(shí)際上聲明了七個(gè)方法但是你僅僅關(guān)心textField(shouldChangeCharactersIn, replacementString)這一個(gè)。

打開(kāi)AddItemViewController.swift,添加UITextFieldDelegate到類(lèi)的聲明中。

class AddItemViewController: UITableViewController,UITextFieldDelegate

現(xiàn)在視圖控制器好比在說(shuō):“我可以做為text filed對(duì)象的委托了”
你同時(shí)也需要讓text field知道這個(gè)委托的存在。

打開(kāi)故事模版并且選定text field。

這里有好幾種方法鏈接text field的委托outlet到視圖控制器。我比較喜歡從鏈接檢查器拖拽委托到視圖控制器(黃色圖標(biāo)那個(gè),見(jiàn)下圖):

從鏈接指示器拖拽文本框的委托到視圖控制器

你同時(shí)還需要添加一個(gè)outlet到導(dǎo)航欄的Done按鈕上,如此你就可以從視圖控制器內(nèi)部向它發(fā)送消息,以達(dá)到禁用它的目的。

打開(kāi)輔助編輯器,并且確保輔助編輯界面展示的是AddItemViewController.swift。

選定導(dǎo)航欄的Done按鈕并且按住ctrl拖拽它到輔助編輯窗口的swift文件中去,給這個(gè)新的outlet命名為doneBarButton。

這是swift文件中應(yīng)該增加了這樣一行:

@IBOutlet weak var doneBarButton: UIBarButtonItem!

在AddItemViewController.swift的底部添加如下方法:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String)-> Bool {
        let oldText = textField.text! as NSString
        let newText = oldText.replacingCharacters(in: range, with: string) as NSString
        
        if newText.length > 0 {
            doneBarButton.isEnabled = true
        } else {
            doneBarButton.isEnabled = false
        }
        return true
    }

這是UITextField的委托方法之一。每次用戶(hù)修改文本后它都會(huì)被調(diào)用,無(wú)論是輸入還是黏貼復(fù)制。

首先,你判斷了新的文本內(nèi)容:

let oldText = textField.text! as NSString
        let newText = oldText.replacingCharacters(in: range, with: string) as NSString

委托方法textField(shouldChangeCharactersIn, replacementString)不能直接給出關(guān)于新文本的內(nèi)容,只有文本發(fā)生變更的時(shí)候才可以。

你需要通過(guò)讀取文本框的內(nèi)容來(lái)計(jì)算新的文本是什么,并且用它自己替換自己。這樣你才能得到一個(gè)新的字符串對(duì)象,并且將它存儲(chǔ)在newText常量中。

NSString與String
文本字符串在swift中的數(shù)據(jù)類(lèi)型為String。但是在上面的方法中你使用了某種叫做NSString的東西。這兩者有什么區(qū)別呢?
NSString是Object-C語(yǔ)言中用于存儲(chǔ)文本的對(duì)象。說(shuō)實(shí)在的,它比Swift中的String要強(qiáng)大而且簡(jiǎn)便。
然而,Swift有一個(gè)絕招:String和NSString是“橋接”的,這就是說(shuō)你可以用NSString代替String。這里,你想要使用屬于NSString的方法replacingCharacters(in:with:),所以你必須使swift知道這個(gè)文本是NSString類(lèi)型的,而不是String。
關(guān)鍵字as NSString的作用就是通知swift這個(gè)oldText是一個(gè)NSString類(lèi)型的常量。如果你沒(méi)有這樣做的話(huà),swift會(huì)根據(jù)類(lèi)型推定斷定oldText是一個(gè)String對(duì)象,這樣就不能使用replacingCharacters(in:with:)方法了。
順便說(shuō)一下,String并不是唯一一個(gè)和Object-C有橋接的類(lèi)型,另一個(gè)例子是數(shù)組(Array)可以橋接到Object-C的NSArray上去。因?yàn)閕OS的架構(gòu)并不完全是由swift寫(xiě)成的,所以O(shè)bject-C的退休時(shí)間延后了。

當(dāng)你讀取到了新的文本之后,你通過(guò)計(jì)算它的長(zhǎng)度檢查它是否為空,并且來(lái)禁用或者啟用Done按鈕:

if newText.length > 0 {
            doneBarButton.isEnabled = true
        } else {
            doneBarButton.isEnabled = false
        }

運(yùn)行app并且在文本框中敲一些內(nèi)容,然后刪除這些內(nèi)容,Done按鈕就被禁用了。

唯一的問(wèn)題是:在最初你還什么都沒(méi)輸入的時(shí)候Done按鈕是可用的。這樣相當(dāng)于我們之前的工作都白做了,這是不允許的,所以我們來(lái)一起修復(fù)這個(gè)問(wèn)題。

打開(kāi)故事模版,選定Done按鈕并且打開(kāi)屬性檢查器,取消選定Enabled選框。

現(xiàn)在一開(kāi)始的時(shí)候Done按鈕就是禁用狀態(tài)了。

什么都沒(méi)輸入的時(shí)候,無(wú)法點(diǎn)擊Done按鈕

最后我們來(lái)優(yōu)化一下剛才的代碼:

用下面這一行替換掉if語(yǔ)句

doneBarButton.isEnabled = (newText.length > 0)

之前的if語(yǔ)句是這樣的:

if newText.length > 0 {
   // 長(zhǎng)度大于0的情況
} else {
   // 長(zhǎng)度小于等于0的情況
}

你通過(guò)檢查條件newText.length > 0來(lái)判斷是否禁用Done按鈕,如果大于0的話(huà)啟用,否則則禁用。

注意一下這個(gè)if語(yǔ)句翻譯過(guò)來(lái)其實(shí)是:如果條件為真則isEnable為真,如果條件為假則isEnable為假。換而言之,isEnable不是為真就是為假。這樣其實(shí)就可以簡(jiǎn)化為:

doneBarButton. isEnable = the result of the condition

就是我們剛才看到的:

doneBarButton.isEnabled = (newText.length > 0)

其實(shí)這里的括號(hào)都沒(méi)有存在的必要,因?yàn)橹挥幸粋€(gè)運(yùn)算,所以不存在優(yōu)先級(jí)。我們可以更加簡(jiǎn)化為:

doneBarButton.isEnabled = newText.length > 0

然而,這樣做會(huì)使可讀性變差,所以我總是習(xí)慣加上括號(hào)。

我們順便來(lái)簡(jiǎn)單介紹一下Swift中的比較運(yùn)算符:

大于 >
< 小于
= 大于等于
<= 小于等于
== 相等
!= 不相等

記住這個(gè)小把戲,無(wú)論何時(shí)你看到這個(gè)結(jié)構(gòu):

if some condition {
  something = true
} else {
  something = false
}

都可以替換為:

something = (some condition)

在實(shí)際的操作中你使用那個(gè)版本都行。我比較喜歡簡(jiǎn)短的這種,專(zhuān)業(yè)的人都喜歡這種方式。你需要記住的就是當(dāng)比較運(yùn)算符總是返回true和false的時(shí)候,你都可以用簡(jiǎn)短的方式表達(dá)它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容