WorldTrotter 看起來不錯横腿,但到目前為止它并沒有做任何事情锭亏。 在本章中诉瓦,您將向 WorldTrotter 添加一個 UITextField 實例姐呐。 文本字段將允許用戶鍵入華氏溫度豁跑,然后將其轉(zhuǎn)換為攝氏溫度并顯示在界面上(圖4.1)廉涕。
圖4.1 具有 UITextField 的 WorldTrotter
您要做的第一件事是向界面添加一個 UITextField,并為該文本字段設(shè)置約束艇拍。 此文本字段將替換當(dāng)前界面中文本為 “212” 的頂部標(biāo)簽狐蜕。
打開 Main.storyboard。 選擇頂部標(biāo)簽卸夕,然后按 Delete 鍵刪除此子視圖层释。 所有其他標(biāo)簽的限制將變?yōu)榧t色,因為它們都直接或間接地錨定到該頂部標(biāo)簽(圖4.2)快集。 這無所謂贡羔, 你會很快修復(fù)它。
圖4.2 標(biāo)簽的不明確邊框
打開對象庫个初,然后將 Text Field 拖動到畫布頂部之前放置標(biāo)簽的位置乖寒。
現(xiàn)在為此文本字段設(shè)置約束。 選中文本字段后勃黍,打開 Align 菜單宵统,并將 *Horizontally in Container * 設(shè)置為 0.確保 Update Frames 設(shè)置為 None,然后點擊 Add 1 Constraint覆获。
現(xiàn)在打開 Add New Constraints 菜單马澈。 給文本字段提供 8 點的頂邊約束,8 點的底邊約束弄息,250 的寬度(圖4.3)痊班。 添加這三個約束。
圖4.3 文本字段 Add New Constraints 菜單
最后摹量,選擇文本字段下面的標(biāo)簽涤伐。 打開 Align 菜單,設(shè)置 Horizontal Centers 為 0缨称,點擊 Update Frames 菜單的 All Frames in Container 凝果,最后點擊 Add 1 Constraint(圖4.4)。
圖4.4 對齊文本字段
接下來睦尽,自定義一些文本字段屬性器净。 打開文本字段的屬性檢查器,并進行以下更改:
- 將文本顏色(從 Color 菜單)設(shè)置為燒橙色当凡。
- 將字體大小設(shè)置為 System 70山害。
- 將 Alignment 設(shè)置為居中纠俭。
- 將占位符文本設(shè)置為 value。這是當(dāng)用戶沒有輸入任何文本時將顯示的文本浪慌。
- 將帶有虛線的分段控件的第一個元素的 Border Style 設(shè)置為 none冤荆。
文本字段的屬性檢查器應(yīng)如圖 4.5 所示。
圖4.5 文本字段屬性檢查器
因為文本字段的字體改變了权纤,畫布上的視圖現(xiàn)在錯位了钓简。 選擇灰色背景視圖,打開 Resolve Auto Layout Issues 菜單妖碉,然后從 All Views in View Controller 部分中選擇 Update Frames涌庭。 文本字段和標(biāo)簽將重新定位以匹配其約束(圖4.6)。
圖4.6 更新邊框
構(gòu)建并運行應(yīng)用程序欧宜。 點擊文本字段并輸入一些文本坐榆。 如果沒有看到鍵盤,請單擊模擬器的 Hardware 菜單冗茸,然后選擇 Keyboard → Toggle Software Keyboard 或使用鍵盤快捷鍵 Command-K席镀。 默認(rèn)情況下,模擬器將計算機的鍵盤視為連接到模擬器的藍牙鍵盤夏漱。 這通常不是你想要的豪诲。 反而您希望模擬器使用的是不附帶任何附件的IOS設(shè)備屏幕鍵盤。
鍵盤屬性
點擊文本字段時挂绰,鍵盤自動向上滑到屏幕上屎篱。 (您將在本章后面看到這為什么會發(fā)生。)鍵盤的外觀由一組名為 UITextInputTraits 的 UITextField 屬性決定葵蒂。 這些屬性之一是顯示的鍵盤類型交播。 對于這個應(yīng)用程序,你想使用數(shù)字鍵盤践付。
在文本字段的屬性檢查器中秦士,找到名為 Keyboard Type 的屬性,然后選擇 Decimal Pad永高。 在同一部分隧土,您可以看到您可以自定義鍵盤的其他一些文本輸入特征。 將 校正(Correction) 和 拼寫檢查(Spell Checking) 更改為 No(圖4.7)命爬。
圖4.7 鍵盤文本輸入特征
構(gòu)建并運行應(yīng)用程序曹傀。 點擊文本字段將輸入數(shù)字。
響應(yīng)文本字段更改
項目的下一步將是在文本字段中鍵入文本時更新攝氏度標(biāo)簽饲宛。 您將需要編寫一些代碼來執(zhí)行此操作皆愉。 具體來說,這個代碼將寫在與該界面關(guān)聯(lián)的視圖控制器子類中。
當(dāng)前界面對應(yīng)于 ViewController.swift 中定義的 ViewController 類亥啦。 但是,對于管理華氏和攝氏之間轉(zhuǎn)換的視圖控制器练链,ViewController 不是一個非常具有描述性的名稱翔脱。 具有描述性的類型名稱可以讓您在項目越來越大時更輕松地維護。
你將刪除這個文件媒鼓,并用更具描述性的類替換它届吁。
在項目導(dǎo)航器中,找到 ViewController.swift 并將其刪除绿鸣。 然后通過選擇 File → New → File... (或按Command-N)創(chuàng)建一個新文件疚沐。 選擇頂部的 iOS 后,在 Source 標(biāo)簽下選擇 Swift File潮模,然后單擊 Next亮蛔。
在下一個窗格中,將此文件命名為 ConversionViewController擎厢。 將文件保存在 WorldTrotter 項目中的 WorldTrotter 組中究流,并確保選中了 WorldTrotter 目標(biāo),如圖4.8所示动遭。 單擊 Create芬探,Xcode 將在編輯器中打開 ConversionViewController.swift。
圖4.8保存Swift文件
在 ConversionViewController.swift 中厘惦,導(dǎo)入 UIKit 并定義一個名為 ConversionViewController 的新視圖控制器偷仿。
import Foundation
import UIKit
class ConversionViewController: UIViewController {
}
現(xiàn)在,您需要將您在 Main.storyboard 中創(chuàng)建的界面與此新的視圖控制器相關(guān)聯(lián)宵蕉。
打開 Main.storyboard酝静,然后在文檔大綱中或通過單擊界面上方的黃色圓圈選擇 View Controller。
打開 身份(identity)檢查器国裳,這是實用程序視圖(Command-Option-3)中的第三個選項卡形入。 在頂部,找到 Custom Class缝左,并將類更改為 ConversionViewController(圖4.9)亿遂。 (您將在第5章中了解這些)
圖4.9 更改自定義類
您在第1章中看到,當(dāng)按鈕被點擊時渺杉,按鈕可以將事件發(fā)送到控制器蛇数。 文本字段是另一個控件(UIButton 和 UITextField 都是 UIControl 的子類),并且可以在文本更改時發(fā)送事件是越。
要使這一切正常工作耳舅,您將需要創(chuàng)建一個 outlet 到攝氏文本標(biāo)簽,并為文本字段創(chuàng)建一個動作,以便在文本更改時調(diào)用浦徊。
打開 ConversionViewController.swift 并定義此 outlet 和 action馏予。 現(xiàn)在,標(biāo)簽將隨用戶輸入文本字段的任何文本而更新盔性。
class ConversionViewController: UIViewController {
??@IBOutlet var celsiusLabel: UILabel!
??@IBAction func fahrenheitFieldEditingChanged(_ textField: UITextField){
????celsiusLabel.text = textField.text
??}
}
打開 Main.storyboard 將它們連接起來霞丧。 outlet 將像第1章一樣連接。右鍵從 Conversion View Controller 拖動到攝氏標(biāo)簽(當(dāng)前顯示為 “100”)冕香,并將其連接到 celsiusLabel蛹尝。
連接動作會有所不同,因為您希望在編輯更改時觸發(fā)動作悉尾。
選擇畫布上的文本字段突那,然后從實用程序窗格(最右邊的選項卡或Command-Option-6)中打開其連接檢查器。 連接檢查器允許您進行連接并查看已經(jīng)建立了哪些連接构眯。
您將對文本字段進行更改愕难,觸發(fā)您在 ConversionViewController 中定義的操作。 在連接檢查器中鸵赖,找到 Sent Events 部分和 Editing Changed务漩。 單擊并拖動 Editing Changed 到 Conversion View Controller 的右側(cè)的圓圈,然后單擊彈出菜單中的 fahrenheitFieldEditingChanged:動作(圖4.10)它褪。
圖4.10連接編輯更改的事件
構(gòu)建并運行應(yīng)用程序饵骨。 點擊文本字段并鍵入一些數(shù)字。 攝氏標(biāo)簽將模擬輸入的文本∶4颍現(xiàn)在刪除文本字段中的文本居触,并注意標(biāo)簽是如何消失的。 沒有文字的標(biāo)簽的內(nèi)容內(nèi)容寬度和高度為 0老赤,因此下方的標(biāo)簽會向上移動轮洋。 我們來解決這個問題。
在 ConversionViewController.swift 中抬旺,如果文本字段為空弊予,更新 fahrenheitFieldEditingChanged(_ :) 顯示為 “???”。
@IBAction func fahrenheitFieldEditingChanged(_ textField: UITextField) {
??//celsiusLabel.text = textField.text
??if let text = textField.text, !text.isEmpty {
????celsiusLabel.text = text
??} else {
????celsiusLabel.text = "???"
??}
}
如果文本字段有文本开财,文本不為空汉柒,它將在 celsiusLabel 上顯示。 如果這些條件中的任何一個都不為真责鳍,那么 celsiusLabel 將被賦予字符串“???”碾褂。
構(gòu)建并運行應(yīng)用程序。 添加一些文本历葛,刪除它正塌,并在文本字段為空時確認(rèn)攝氏標(biāo)簽填充有“???”。
取消鍵盤
目前,沒有辦法解除鍵盤乓诽。 我們來補充一下這個功能帜羊。 這樣做的一個常見方法是檢測用戶何時點擊 Return 鍵并使用該動作來關(guān)閉鍵盤; 您將在第14章中使用此方法。由于數(shù)字小鍵盤沒有Return鍵鸠天,您將允許用戶點擊背景視圖以觸發(fā)解除逮壁。
當(dāng)文本字段被點擊時,FirstFirstResponder() 將被調(diào)用 粮宛。 這是除了別的以外,使鍵盤出現(xiàn)的方法卖宠。 要關(guān)閉鍵盤巍杈,可以在文本字段中調(diào)用 resignFirstResponder() 方法。 您將在第14章中更多地了解這些方法扛伍。
對于 WorldTrotter筷畦,您將需要一個文本字段的 outlet 和當(dāng)后臺視圖被輕觸時觸發(fā)的方法。 此方法將在文本字段插槽上調(diào)用 resignFirstResponder()刺洒。 我們先來看看代碼鳖宾。
打開 ConversionViewController.swift 并在文本字段引用附近聲明一個 outlet 。
@IBOutlet var celsiusLabel: UILabel!
@IBOutlet var textField: UITextField!
現(xiàn)在實現(xiàn)一個在調(diào)用時會關(guān)閉鍵盤的動作方法逆航。
(在上面的代碼中鼎文,我們加上了現(xiàn)有的代碼,以便您可以正確定位新的代碼的位置因俐。在下面的代碼中,我們不提供該上下文,因為新代碼的位置不重要捡多,只要它在大括號內(nèi) 在這種情況下绪励,實現(xiàn)的類型是 ConversionViewController 類,當(dāng)一個代碼塊包含所有新的代碼時澳眷,我們建議你把它放在類型的實現(xiàn)的末尾胡嘿,就在最后的大括號里面,在第15章你會看到當(dāng)您的文件變得更長钳踊,更復(fù)雜時衷敌,如何輕松地在實現(xiàn)文件中導(dǎo)航。)
@IBAction func dismissKeyboard(_ sender: UITapGestureRecognizer)
{
??textField.resignFirstResponder()
}
仍然需要完成以下:textField outlet 需要連接在故事板文件中箍土,您需要一種觸發(fā)您添加的 dismissKeyboard(_ :) 方法的方法逢享。
要處理第一項,請打開 Main.storyboard 并選擇 Conversion View Controller吴藻。右鍵從 Conversion View Controller 拖動到畫布上的文本字段瞒爬,并將其連接到 textField。
現(xiàn)在您需要一種方式來觸發(fā)您實現(xiàn)的方法。 您將使用手勢識別器來完成此操作侧但。
手勢識別器是 UIGestureRecognizer 的子類矢空,它檢測特定的觸摸序列,并在檢測到該序列時對其目標(biāo)調(diào)用動作禀横。 有手勢識別器檢測觸擊屁药,滑動,長按等等柏锄。 在本章中酿箭,您將使用 UITapGestureRecognizer 來檢測用戶何時觸擊背景視圖。 您將在第19章中更多地了解手勢識別器趾娃。
在 Main.storyboard 中缭嫡,在對象庫中找到 Tap Gesture Recognizer。 將此對象拖動到 Conversion View Controller 的背景視圖中抬闷。 您將在 scene 底座妇蛀,也就是畫布上方的圖標(biāo)行中的看到對該手勢識別器的引用。
右鍵從 Tap Gesture Recognizer 拖動到 Conversion View Controller笤成,并將其連接到 dismissKeyboard: 方法(圖4.11)评架。
圖4.11連接手勢識別器動作
實現(xiàn)溫度轉(zhuǎn)換
了解了界面的基本原理后,讓我們來實現(xiàn)從華氏溫度轉(zhuǎn)為攝氏溫度炕泳。 您將要存儲當(dāng)前的華氏值纵诞,并在文本字段更改時計算攝氏值。
在 ConversionViewController.swift 中培遵,為華氏值添加一個屬性挣磨。 這將是溫度的可選測量 (Measurement<UnitTemperature>?)。
@IBOutlet var celsiusLabel: UILabel!
var fahrenheitValue: Measurement<UnitTemperature>?
該屬性是可選的原因是因為用戶可能沒有鍵入一個數(shù)字荤懂,類似于您之前修復(fù)的空字符串問題茁裙。
現(xiàn)在為 Celsius 值添加一個用于計算的屬性。 該值將根據(jù)華氏值計算节仿。
var fahrenheitValue: Measurement<UnitTemperature>?
var celsiusValue: Measurement<UnitTemperature>? {
??if let fahrenheitValue = fahrenheitValue {
????return fahrenheitValue.converted(to: .celsius)
??} else {
????return nil
??}
}
首先檢查一下是否有華氏值晤锥。 如果有,您將該值轉(zhuǎn)換為等值的攝氏度廊宪。 如果沒有華氏值矾瘾,那么您不能計算攝氏度值,因此您返回 nil箭启。
接下來完成:每當(dāng)華氏值變化時壕翩,更新攝氏標(biāo)簽。
向 ConversionViewController 添加一個方法來更新 celsiusLabel傅寡。
func updateCelsiusLabel() {
??if let celsiusValue = celsiusValue {
????celsiusLabel.text = "\(celsiusValue.value)"
??} else {
????celsiusLabel.text = "???"
??}
}
您想要在華氏值變化時調(diào)用此方法放妈。 為此北救,您將使用 屬性觀察者(property observer),它是一個代碼塊芜抒,當(dāng)屬性的值更改時珍策,該代碼將被調(diào)用。
在屬性聲明之后立即使用花括號來聲明屬性觀察者宅倒。 在大括號內(nèi)攘宙,您可以使用 willSet 或 didSet 來聲明您的觀察者,具體取決于是否要在屬性值更改之前或之后立即通知屬性值更改拐迁。
添加一個屬性觀察器蹭劈,使得當(dāng)屬性值更改后調(diào)用 fahrenheitValue 。
var fahrenheitValue: Measurement<UnitTemperature>?
{
??didSet {
????updateCelsiusLabel()
??}
}
(一個小筆記:當(dāng)屬性值從構(gòu)造方法中更改時线召,不會觸發(fā)屬性觀察者链方。)
有了這個邏輯,您現(xiàn)在可以在文本字段更改時更新華氏值(這又會觸發(fā)更新攝氏標(biāo)簽)灶搜。
在 fahrenheitFieldEditingChanged(_ :) 中,刪除以前的非轉(zhuǎn)換實現(xiàn)工窍,而沒有正確更新華氏值割卖。
@IBAction func fahrenheitFieldEditingChanged(_ textField: UITextField) {
??if let text = textField.text, !text.isEmpty {
????celsiusLabel.text = text
??} else {
????celsiusLabel.text = "???"
??}
??if let text = textField.text, let value = Double(text) {
????fahrenheitValue = Measurement(value: value, unit: .fahrenheit)
??} else {
????fahrenheitValue = nil
??}
}
首先,您檢查文本字段是否有文本患雏。 如果有鹏溯,檢查該文本是否可以由 Double 表示。 例如淹仑,“3.14” 可以由 Double 表示丙挽,但 “three” 和 “1.2.3” 不能。 如果這兩個檢查都通過了匀借,那么華氏溫度值被設(shè)置為用該 Double 值初始化的 Measurement颜阐。 如果這些檢查中的任何一個失敗,則華氏值設(shè)置為 nil吓肋。
構(gòu)建并運行應(yīng)用程序凳怨。 華氏和攝氏之間的轉(zhuǎn)換效果非常好——只要您輸入有效的數(shù)字。 (它顯示的數(shù)字有些你并不想要(小數(shù)點后的數(shù)字過多)是鬼,接下來我們來解決它)
如果應(yīng)用程序首次啟動時就更新 celsiusLabel肤舞,而不是仍然顯示 “100”,那就更好了均蜜。
覆蓋 viewDidLoad() 以設(shè)置初始值李剖,類似于第1章中的操作。
override func viewDidLoad() {
??super.viewDidLoad()
??updateCelsiusLabel()
}
在本章的剩余部分中囤耳,您將更新 WorldTrotter 以解決兩個問題:您將格式化 Celsius 值以顯示最多一個小數(shù)位的精度篙顺,并且不允許用戶鍵入多個小數(shù)分隔符偶芍。
您的應(yīng)用程序還有其他幾個問題,但現(xiàn)在您將重點關(guān)注這兩個問題慰安。在本章結(jié)尾處腋寨,其他問題將作為挑戰(zhàn)呈現(xiàn)。 我們開始更新攝氏度值的精度化焕。
數(shù)字格式化
您使用 數(shù)字格式化器(number formatter) 自定義數(shù)字的顯示萄窜。 還有其他格式化器用于格式化日期,能量撒桨,質(zhì)量查刻,長度,測量等凤类。
在 ConversionViewController.swift 中創(chuàng)建一個 number formatter穗泵。
let numberFormatter: NumberFormatter = {
??let nf = NumberFormatter()
??nf.numberStyle = .decimal
??nf.minimumFractionDigits = 0
??nf.maximumFractionDigits = 1
??return nf
}()
在這里,您使用閉包來實例化數(shù)字格式化程序谜疤。 您正在創(chuàng)建具有 .decimal 樣式的 NumberFormatter佃延,并將其配置為顯示不超過一個小數(shù)位數(shù)。 您將在第16章中了解更多關(guān)于聲明屬性的新語法夷磕。
現(xiàn)在修改 updateCelsiusLabel() 來使用這個格式化程序履肃。
func updateCelsiusLabel() {
??if let celsiusValue = celsiusValue {
????celsiusLabel.text = "\(celsiusValue.value)"
????celsiusLabel.text =numberFormatter.string(from: NSNumber(value: celsiusValue.value))
??} else {
????celsiusLabel.text = "???"
??}
}
構(gòu)建并運行應(yīng)用程序。 輸入多個華氏溫度觀察格式化方法是否有效坐桩。 您將不會在攝氏標(biāo)簽上看到多于一位的小數(shù)出現(xiàn)尺棋。
在下一節(jié)中,您將更新應(yīng)用程序绵跷,實現(xiàn)在文本字段中接受最多一個小數(shù)分隔符膘螟。 為此,您將使用一種常見的 iOS 設(shè)計模式碾局,稱為 委托模式(delegation)荆残。
委托模式
委托是一個面向?qū)ο蟮?回調(diào)(callback) 方法。 回調(diào)是在事件發(fā)生前提供的一個函數(shù)净当,每次事件發(fā)生時都會調(diào)用它脊阴。 一些對象需要對多個事件進行回調(diào)。 例如蚯瞧,當(dāng)用戶輸入文本以及用戶按 Return 鍵時嘿期,文本字段都需要“回調(diào)”。
然而埋合,兩個(或多個)回調(diào)函數(shù)之間沒有內(nèi)置的方式來協(xié)調(diào)和共享信息备徐。 這是委托所解決的問題——你提供一個委托來接收特定對象的所有與事件有關(guān)的回調(diào)。 然后甚颂,該委托對象可以進行存儲蜜猾,操作等秀菱,并在它認(rèn)為合適的時候從回調(diào)中傳遞信息。
當(dāng)用戶文本字段輸入時蹭睡,該文本字段將詢問其委托是否接受用戶所做的更改衍菱。 對于 WorldTrotter,如果用戶嘗試輸入第二個小數(shù)分隔符肩豁,則要拒絕該更改脊串。 文本字段的委托將是 ConversionViewController 的實例。
符合協(xié)議
第一步是通過聲明 ConversionViewController 符合 UITextFieldDelegate 協(xié)議清钥,使 ConversionViewController 類的實例成為 UITextField 的委托角色琼锋。 對于每個委托角色,都有一個相應(yīng)的協(xié)議祟昭,聲明對象可以調(diào)用它的委托的方法缕坎。
UITextFieldDelegate 協(xié)議如下所示:
protocol UITextFieldDelegate: NSObjectProtocol {
??optional func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool
??optional func textFieldDidBeginEditing(_ textField: UITextField)
??optional func textFieldShouldEndEditing(_ textField: UITextField) -> Bool
??optional func textFieldDidEndEditing(_ textField: UITextField)
??optional func textField(_ textField: UITextField,
????????????shouldChangeCharactersIn range: NSRange,
????????????replacementString string: String) -> Bool
??optional func textFieldShouldClear(_ textField: UITextField) -> Bool
??optional func textFieldShouldReturn(_ textField: UITextField) -> Bool
}
UITextFieldDelegate ,與其它協(xié)議一樣篡悟,使用關(guān)鍵字 protocol 來聲明谜叹,其后面是它的名字 。 冒號之后的 NSObjectProtocol 是指 NSObject 協(xié)議搬葬,并告訴您 UITextFieldDelegate 繼承 NSObject 協(xié)議中的所有方法荷腊。 接下來聲明特定于 UITextFieldDelegate 的方法。
您無法創(chuàng)建協(xié)議實例; 它只是方法和屬性的列表踩萎。 相反,實現(xiàn)方法被保留到符合協(xié)議的每一種類型很钓。
在類的聲明中香府,類遵循的協(xié)議列表跟在父類(如果有的話)之后并以逗號分隔的。 在 ConversionViewController.swift 中码倦,聲明 ConversionViewController 符合 UITextFieldDelegate 協(xié)議企孩。
class ConversionViewController: UIViewController, UITextFieldDelegate {
用于委托的協(xié)議稱為 委托協(xié)議(delegate protocol),委托協(xié)議的命名約定是委派類的名稱加上 Delegate 一詞袁稽。 然而并不是所有的協(xié)議都是委托協(xié)議勿璃,第16章中將會看到一種不同類型協(xié)議的例子。目前為止我們提到的協(xié)議是 iOS SDK 的一部分推汽,您也可以編寫自己的協(xié)議补疑。
使用委托
現(xiàn)在您已將 ConversionViewController 聲明為符合 UITextFieldDelegate 協(xié)議,您可以去設(shè)置文本字段的 delegate 屬性了歹撒。
打開 Main.storyboard莲组,然后從文本框拖動到 Control View Controller。 從彈出菜單中選擇 delegate 并將文本字段的 delegate 屬性連接到 ConversionViewController暖夭。
接下來锹杈,您將實現(xiàn)您感興趣的 UITextFieldDelegate 方法 —— textField(_:shouldChangeCharactersIn:replacementString :)撵孤。 因為文本字段在其委托中調(diào)用此方法,所以必須在 ConversionViewController.swift 中實現(xiàn)它竭望。
在 ConversionViewController.swift,實現(xiàn) textField(_:shouldChangeCharactersIn:replacementString:) 打印文本字段的當(dāng)前文本以及替換字符串⌒奥耄現(xiàn)在,只要這個方法返回 true即可咬清。
func textField(_ textField: UITextField,
??????shouldChangeCharactersIn range: NSRange,
??????replacementString string: String) -> Bool {
??print("Current text: \(textField.text)")
??print("Replacement text: \(string)")
??return true
}
注意闭专,Xcode能夠自動完成這個方法,因為 ConversionViewController 符合 UITextFieldDelegate枫振。在實現(xiàn)協(xié)議的方法之前喻圃,先聲明一個協(xié)議是一個好主意,這樣 Xcode 才會提供這種支持粪滤。
構(gòu)建并運行應(yīng)用程序斧拍。 在文本字段中輸入幾位數(shù)字,并觀看 Xcode 控制臺(圖4.12)杖小。 它打印文本字段的當(dāng)前文本以及替換字符串肆汹。
圖4.12打印到控制臺
您的目標(biāo)是防止多個小數(shù)分隔符,考慮這個 當(dāng)前文本(current text) 和 替換文本(replacement text) 予权。 邏輯上昂勉,如果現(xiàn)有的字符串有一個小數(shù)分隔符,替換字符串有一個小數(shù)分隔符扫腺,那么更改應(yīng)該被拒絕岗照。
在 ConversionViewController.swift 中,更新 textField(_:shouldChangeCharactersIn:replacementString :) 以使用此邏輯笆环。
func textField(_ textField: UITextField,
??????shouldChangeCharactersIn range: NSRange,
??????replacementString string: String) -> Bool {
??print("Current text: \(textField.text)")
??print("Replacement text: \(string)")
??return true
??let existingTextHasDecimalSeparator = textField.text?.range(of: ".")
??let replacementTextHasDecimalSeparator = string.range(of: ".")
??if existingTextHasDecimalSeparator != nil,
????replacementTextHasDecimalSeparator != nil {
????return false
??} else {
????return true
??}
}
構(gòu)建并運行應(yīng)用程序攒至。 嘗試輸入多個小數(shù)分隔符; 應(yīng)用程序?qū)⒕芙^您輸入的第二個小數(shù)分隔符。
協(xié)議其他
在 UITextFieldDelegate 協(xié)議中躁劣,有兩種方法:處理信息更新的方法和處理輸入請求的方法迫吐。 例如,如果文本字段的代理想知道用戶在文本字段上點擊的時候账忘,文本字段的委托應(yīng)該實現(xiàn) textFieldDidBeginEditing(_ :) 方法志膀。
另一方面,textField(_:shouldChangeCharactersIn:replacementString :) 是一個輸入請求鳖擒。 文本字段在其委托中調(diào)用此方法來詢問替換字符串是否應(yīng)被接受或拒絕溉浙。 該方法返回一個 Bool,它是委托者的回復(fù)蒋荚。
在協(xié)議中聲明的方法可以是必需的或可選的放航。 默認(rèn)情況下,協(xié)議方法是必要的圆裕,這意味著符合協(xié)議的類必須具有這些方法的實現(xiàn)广鳍。 如果一個協(xié)議有可選的方法荆几,那么它們前面會有 optional。 回顧 UITextFieldDelegate 協(xié)議赊时,您可以看到所有的方法都是可選的吨铸。 委托協(xié)議通常是這樣的。
青銅挑戰(zhàn):禁止字母字符
目前祖秒,用戶可以通過使用藍牙鍵盤或?qū)?fù)制的文本粘貼到文本字段中來輸入字母字符诞吱。 解決這個問題。 提示:您需要使用到 NSCharacterSet 類竭缝。