來個深加工
在你寫代碼將文本作為一條新的紀錄插入列表之前禽捆,讓我們先來改進一下新增待辦事項頁面的工作方式和設計琐凭。
例如:如果可以自動為用戶彈出鍵盤统屈,而不是當用戶點擊后才彈出鍵盤,這樣體驗會好一些吨掌。
為了達到這個目的窿侈,需要在AddItemViewController.swift中添加一個方法viewWillAppear():
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textField.becomeFirstResponder()
}
視圖控制器在即將可視化之前接收viewWillAppear()的消息,這正是激活文本框的完美時機。我們在這里發(fā)送becomeFirstResponder()消息衙傀。
如果你是在其他平臺上做這個事火本,通常被稱為“控制聚焦”茫陆。在iOS術(shù)語中簿盅,這種控制方法稱為“第一響應(first responder)”
運行app然后點擊?號按鈕现斋,你可以看到鍵盤會自動滑出來了瞬内。
(再說一次倦西,如果模擬器上鍵盤沒有自動滑出的話,可以通過command+K組合鍵喚醒鍵盤)
像這樣的小細節(jié)通常會帶來很好的用戶體驗。比起必須點擊文本框才能打字,現(xiàn)在的做法明顯快了不少。在這個日新月異的年代中,人們使用產(chǎn)品時不會有太多的耐心。比如一點點的瑕疵都會讓用戶轉(zhuǎn)移到你競爭對手的產(chǎn)品上去。我總是花大量的精力來使自己的app盡可能的完美。
所以,我們來繼續(xù)改進一下輸入框。
打開故事模版并且選定文本框俱病,進入到屬性檢查器并且按照下面的列表進行設置:
1、Placeholder:Name of the Item
2果元、Font:System 17
3倡怎、Adjust to Fit:Uncheck this
4贱枣、Capitalization:Sentence
5监署、Return Key:Done
這里有一些選項可以使你配置文本框被激活時的鍵盤的屬性。
例如纽哥,如果這個文本框僅可以輸入數(shù)值的話钠乏,你可以設置Keyboard Type為Number Pad。如果這是一個用于輸入Email地址的文本框春塌,你就可以將Keyboard Type為E-mail Address晓避。對我們這個app而言,默認的類型就正好符號要求只壳,因為用戶可以隨意輸入自己將要做的事情够滑。
你也可以改變鍵盤上回車鍵的名稱。默認的是“return”吕世,這里我們改為了“Done”。這只是按鈕上的標題文本梯投,點擊這個“Done”并不會自動關(guān)閉當前界面命辖,你還需要將鍵盤上的“Done”按鈕指向到相同的動作上,這樣鍵盤上的Done按鈕才能和導航欄上的Done按鈕起到相同的作用分蓖。
選定text field然后打開鏈接檢查器尔艇,拖拽Did End on Exit事件到視圖控制器,并且選擇done動作么鹤。
如果你的輔助編輯器還沒關(guān)的話终娃,也可以直接拖拽到源代碼的done()方法上。
查看done動作的鏈接可以點擊done()方法左邊蒸甜,屏幕邊緣上的那個小圓圈棠耕,點擊后會彈出一個窗口顯示done()方法的鏈接內(nèi)容。
運行app柠新,點擊鍵盤上的Done按鈕窍荧,這時界面會自動關(guān)閉,并且在調(diào)試區(qū)域打印出我們預置的文本信息恨憎。
驗證用戶的輸入并且確保它們被接收到了蕊退,是必不可缺的一個步驟。例如,用戶瞬間點了一下Done按鈕但是什么都沒輸入會發(fā)生什么瓤荔。
在主界面的列表中净蚤,新增一個空白的什么都沒有的行,可一點也不好输硝,為了避免這件事的發(fā)生你需要在用戶什么都沒有輸入前今瀑,禁止done按鈕的作用。
當然腔丧,你需要同時處理兩個done按鈕放椰,鍵盤上的一個,導航欄上的一個愉粤。讓我們先著手處理鍵盤上的done按鈕砾医,這個稍微簡單一些。
選中文本框衣厘,打開屬性檢查器如蚜,選中Auto-enable Return Key。
就這樣影暴,現(xiàn)在你運行app错邦,什么也不輸入,點擊鍵盤上的Done按鈕是不會起作用的型宙,試試看撬呢!
對于導航欄上的Done按鈕,工作就復雜一些了妆兑。你需要監(jiān)聽每次一次用戶敲擊鍵盤后魂拦,文本框中的內(nèi)容是否為空。如果為空搁嗓,則禁用Done按鈕芯勘。
如果用戶的本身是點錯了,可以通過Cancel按鈕退出界面腺逛,但是用戶點擊Done按鈕時荷愕,必須保證文本框中有輸入的內(nèi)容。
對于文本框而言棍矛,有兩種輸入方法安疗,敲擊鍵盤輸入和復制黏貼,你需要給視圖控制器一個委托用于文本框茄靠。
當文本框發(fā)送事件到這個委托時茂契,你就可以知道文本框的情況了。這個委托存在于AdditemViewController內(nèi)慨绳,可以對文本框的事件進行響應掉冶,并且做出合適的動作真竖。
一個視圖控制器允許委托多個對象。AdditemViewController已經(jīng)有了一個委托和一個數(shù)據(jù)源用于UITableView厌小,因為它本來就是一個UITableViewController∩退郑現(xiàn)在它要增加一個委托用于text field的對象御雕,UITextfiled。
這是兩種不同類型的委托并且你要使視圖控制同時執(zhí)行它們的規(guī)則。關(guān)于委托的更多內(nèi)容我們會在下一個課程中講解勺三。
如何定義一個委托
在iOS系統(tǒng)的SDK里唇兑,委托無處不在辫狼,所以我們最好記住它總是由三個步驟完成聪蘸。
1、你申明自己有能力成為一個委托——成為UITextField的委托你需要將UITextFieldDelegate寫到視圖控制器的類的聲明中去疯搅。這樣就告訴了編譯器這個視圖控制器可以處理來自文本框的消息濒生。
2、你讓那個有問題的對象知道視圖控制器愿意成為它的委托——在我們這個例子里幔欧,這個對象是UITextField罪治。如果你忘記了告訴文本框它有一個委托,則這個文本框永遠不會發(fā)送任何消息礁蔗。
3觉义、執(zhí)行委托方法——如果你沒有對你發(fā)出的消息進行響應,那么委托根本不會成立浴井。
通常晒骇,委托方法是可選的,所以你不需要執(zhí)行全部的委托方法磺浙。例如:UITextFieldDelegate實際上聲明了七個方法但是你僅僅關(guān)心textField(shouldChangeCharactersIn, replacementString)這一個厉碟。
打開AddItemViewController.swift,添加UITextFieldDelegate到類的聲明中屠缭。
class AddItemViewController: UITableViewController,UITextFieldDelegate
現(xiàn)在視圖控制器好比在說:“我可以做為text filed對象的委托了”
你同時也需要讓text field知道這個委托的存在。
打開故事模版并且選定text field崭参。
這里有好幾種方法鏈接text field的委托outlet到視圖控制器呵曹。我比較喜歡從鏈接檢查器拖拽委托到視圖控制器(黃色圖標那個,見下圖):
你同時還需要添加一個outlet到導航欄的Done按鈕上何暮,如此你就可以從視圖控制器內(nèi)部向它發(fā)送消息奄喂,以達到禁用它的目的。
打開輔助編輯器海洼,并且確保輔助編輯界面展示的是AddItemViewController.swift跨新。
選定導航欄的Done按鈕并且按住ctrl拖拽它到輔助編輯窗口的swift文件中去,給這個新的outlet命名為doneBarButton坏逢。
這是swift文件中應該增加了這樣一行:
@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的委托方法之一域帐。每次用戶修改文本后它都會被調(diào)用赘被,無論是輸入還是黏貼復制。
首先肖揣,你判斷了新的文本內(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ā)生變更的時候才可以。
你需要通過讀取文本框的內(nèi)容來計算新的文本是什么龙优,并且用它自己替換自己羊异。這樣你才能得到一個新的字符串對象,并且將它存儲在newText常量中彤断。
NSString與String
文本字符串在swift中的數(shù)據(jù)類型為String野舶。但是在上面的方法中你使用了某種叫做NSString的東西。這兩者有什么區(qū)別呢宰衙?
NSString是Object-C語言中用于存儲文本的對象平道。說實在的,它比Swift中的String要強大而且簡便菩浙。
然而巢掺,Swift有一個絕招:String和NSString是“橋接”的,這就是說你可以用NSString代替String劲蜻。這里陆淀,你想要使用屬于NSString的方法replacingCharacters(in:with:),所以你必須使swift知道這個文本是NSString類型的先嬉,而不是String轧苫。
關(guān)鍵字as NSString的作用就是通知swift這個oldText是一個NSString類型的常量。如果你沒有這樣做的話疫蔓,swift會根據(jù)類型推定斷定oldText是一個String對象含懊,這樣就不能使用replacingCharacters(in:with:)方法了。
順便說一下衅胀,String并不是唯一一個和Object-C有橋接的類型岔乔,另一個例子是數(shù)組(Array)可以橋接到Object-C的NSArray上去。因為iOS的架構(gòu)并不完全是由swift寫成的滚躯,所以Object-C的退休時間延后了雏门。
當你讀取到了新的文本之后,你通過計算它的長度檢查它是否為空掸掏,并且來禁用或者啟用Done按鈕:
if newText.length > 0 {
doneBarButton.isEnabled = true
} else {
doneBarButton.isEnabled = false
}
運行app并且在文本框中敲一些內(nèi)容茁影,然后刪除這些內(nèi)容,Done按鈕就被禁用了丧凤。
唯一的問題是:在最初你還什么都沒輸入的時候Done按鈕是可用的募闲。這樣相當于我們之前的工作都白做了,這是不允許的愿待,所以我們來一起修復這個問題浩螺。
打開故事模版靴患,選定Done按鈕并且打開屬性檢查器,取消選定Enabled選框年扩。
現(xiàn)在一開始的時候Done按鈕就是禁用狀態(tài)了蚁廓。
最后我們來優(yōu)化一下剛才的代碼:
用下面這一行替換掉if語句
doneBarButton.isEnabled = (newText.length > 0)
之前的if語句是這樣的:
if newText.length > 0 {
// 長度大于0的情況
} else {
// 長度小于等于0的情況
}
你通過檢查條件newText.length > 0來判斷是否禁用Done按鈕厨幻,如果大于0的話啟用相嵌,否則則禁用。
注意一下這個if語句翻譯過來其實是:如果條件為真則isEnable為真况脆,如果條件為假則isEnable為假饭宾。換而言之,isEnable不是為真就是為假格了。這樣其實就可以簡化為:
doneBarButton. isEnable = the result of the condition
就是我們剛才看到的:
doneBarButton.isEnabled = (newText.length > 0)
其實這里的括號都沒有存在的必要看铆,因為只有一個運算,所以不存在優(yōu)先級盛末。我們可以更加簡化為:
doneBarButton.isEnabled = newText.length > 0
然而弹惦,這樣做會使可讀性變差,所以我總是習慣加上括號悄但。
我們順便來簡單介紹一下Swift中的比較運算符:
大于 >
< 小于
= 大于等于
<= 小于等于
== 相等
!= 不相等
記住這個小把戲棠隐,無論何時你看到這個結(jié)構(gòu):
if some condition {
something = true
} else {
something = false
}
都可以替換為:
something = (some condition)
在實際的操作中你使用那個版本都行。我比較喜歡簡短的這種檐嚣,專業(yè)的人都喜歡這種方式助泽。你需要記住的就是當比較運算符總是返回true和false的時候,你都可以用簡短的方式表達它嚎京。