WorldTrotter 看起來不錯,但到目前為止它并沒有做任何事情。 在本章中,您將向 WorldTrotter 添加一個 UITextField 實(shí)例。 文本字段將允許用戶鍵入華氏溫度,然后將其轉(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色,因?yàn)樗鼈兌贾苯踊蜷g接地錨定到該頂部標(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,然后點(diǎn)擊 Add 1 Constraint。
現(xiàn)在打開 Add New Constraints 菜單。 給文本字段提供 8 點(diǎn)的頂邊約束,8 點(diǎn)的底邊約束,250 的寬度(圖4.3)。 添加這三個約束。
圖4.3 文本字段 Add New Constraints 菜單

最后,選擇文本字段下面的標(biāo)簽。 打開 Align 菜單,設(shè)置 Horizontal Centers 為 0,點(diǎn)擊 Update Frames 菜單的 All Frames in Container ,最后點(diǎn)擊 Add 1 Constraint(圖4.4)。
圖4.4 對齊文本字段

接下來,自定義一些文本字段屬性。 打開文本字段的屬性檢查器,并進(jìn)行以下更改:
- 將文本顏色(從 Color 菜單)設(shè)置為燒橙色。
- 將字體大小設(shè)置為 System 70。
- 將 Alignment 設(shè)置為居中。
- 將占位符文本設(shè)置為 value。這是當(dāng)用戶沒有輸入任何文本時將顯示的文本。
- 將帶有虛線的分段控件的第一個元素的 Border Style 設(shè)置為 none。
文本字段的屬性檢查器應(yīng)如圖 4.5 所示。
圖4.5 文本字段屬性檢查器

因?yàn)槲谋咀侄蔚淖煮w改變了,畫布上的視圖現(xiàn)在錯位了。 選擇灰色背景視圖,打開 Resolve Auto Layout Issues 菜單,然后從 All Views in View Controller 部分中選擇 Update Frames。 文本字段和標(biāo)簽將重新定位以匹配其約束(圖4.6)。
圖4.6 更新邊框

構(gòu)建并運(yùn)行應(yīng)用程序。 點(diǎn)擊文本字段并輸入一些文本。 如果沒有看到鍵盤,請單擊模擬器的 Hardware 菜單,然后選擇 Keyboard → Toggle Software Keyboard 或使用鍵盤快捷鍵 Command-K。 默認(rèn)情況下,模擬器將計(jì)算機(jī)的鍵盤視為連接到模擬器的藍(lán)牙鍵盤。 這通常不是你想要的。 反而您希望模擬器使用的是不附帶任何附件的IOS設(shè)備屏幕鍵盤。
鍵盤屬性
點(diǎn)擊文本字段時,鍵盤自動向上滑到屏幕上。 (您將在本章后面看到這為什么會發(fā)生。)鍵盤的外觀由一組名為 UITextInputTraits 的 UITextField 屬性決定。 這些屬性之一是顯示的鍵盤類型。 對于這個應(yīng)用程序,你想使用數(shù)字鍵盤。
在文本字段的屬性檢查器中,找到名為 Keyboard Type 的屬性,然后選擇 Decimal Pad。 在同一部分,您可以看到您可以自定義鍵盤的其他一些文本輸入特征。 將 校正(Correction) 和 拼寫檢查(Spell Checking) 更改為 No(圖4.7)。
圖4.7 鍵盤文本輸入特征

構(gòu)建并運(yùn)行應(yīng)用程序。 點(diǎn)擊文本字段將輸入數(shù)字。
響應(yīng)文本字段更改
項(xiàng)目的下一步將是在文本字段中鍵入文本時更新攝氏度標(biāo)簽。 您將需要編寫一些代碼來執(zhí)行此操作。 具體來說,這個代碼將寫在與該界面關(guān)聯(lián)的視圖控制器子類中。
當(dāng)前界面對應(yīng)于 ViewController.swift 中定義的 ViewController 類。 但是,對于管理華氏和攝氏之間轉(zhuǎn)換的視圖控制器,ViewController 不是一個非常具有描述性的名稱。 具有描述性的類型名稱可以讓您在項(xiàng)目越來越大時更輕松地維護(hù)。
你將刪除這個文件,并用更具描述性的類替換它。
在項(xiàng)目導(dǎo)航器中,找到 ViewController.swift 并將其刪除。 然后通過選擇 File → New → File... (或按Command-N)創(chuàng)建一個新文件。 選擇頂部的 iOS 后,在 Source 標(biāo)簽下選擇 Swift File,然后單擊 Next。
在下一個窗格中,將此文件命名為 ConversionViewController。 將文件保存在 WorldTrotter 項(xiàng)目中的 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)檢查器,這是實(shí)用程序視圖(Command-Option-3)中的第三個選項(xiàng)卡。 在頂部,找到 Custom Class,并將類更改為 ConversionViewController(圖4.9)。 (您將在第5章中了解這些)
圖4.9 更改自定義類

您在第1章中看到,當(dāng)按鈕被點(diǎn)擊時,按鈕可以將事件發(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。
連接動作會有所不同,因?yàn)槟M诰庉嫺臅r觸發(fā)動作。
選擇畫布上的文本字段,然后從實(shí)用程序窗格(最右邊的選項(xiàng)卡或Command-Option-6)中打開其連接檢查器。 連接檢查器允許您進(jìn)行連接并查看已經(jīng)建立了哪些連接。
您將對文本字段進(jìn)行更改,觸發(fā)您在 ConversionViewController 中定義的操作。 在連接檢查器中,找到 Sent Events 部分和 Editing Changed。 單擊并拖動 Editing Changed 到 Conversion View Controller 的右側(cè)的圓圈,然后單擊彈出菜單中的 fahrenheitFieldEditingChanged:動作(圖4.10)。
圖4.10連接編輯更改的事件

構(gòu)建并運(yùn)行應(yīng)用程序。 點(diǎn)擊文本字段并鍵入一些數(shù)字。 攝氏標(biāo)簽將模擬輸入的文本?,F(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ùn)行應(yīng)用程序。 添加一些文本,刪除它,并在文本字段為空時確認(rèn)攝氏標(biāo)簽填充有“???”。
取消鍵盤
目前,沒有辦法解除鍵盤。 我們來補(bǔ)充一下這個功能。 這樣做的一個常見方法是檢測用戶何時點(diǎn)擊 Return 鍵并使用該動作來關(guān)閉鍵盤; 您將在第14章中使用此方法。由于數(shù)字小鍵盤沒有Return鍵,您將允許用戶點(diǎn)擊背景視圖以觸發(fā)解除。
當(dāng)文本字段被點(diǎn)擊時,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)在實(shí)現(xiàn)一個在調(diào)用時會關(guān)閉鍵盤的動作方法。
(在上面的代碼中,我們加上了現(xiàn)有的代碼,以便您可以正確定位新的代碼的位置。在下面的代碼中,我們不提供該上下文,因?yàn)樾麓a的位置不重要,只要它在大括號內(nèi) 在這種情況下,實(shí)現(xiàn)的類型是 ConversionViewController 類,當(dāng)一個代碼塊包含所有新的代碼時,我們建議你把它放在類型的實(shí)現(xiàn)的末尾,就在最后的大括號里面,在第15章你會看到當(dāng)您的文件變得更長,更復(fù)雜時,如何輕松地在實(shí)現(xiàn)文件中導(dǎo)航。)
@IBAction func dismissKeyboard(_ sender: UITapGestureRecognizer)
{
??textField.resignFirstResponder()
}
仍然需要完成以下:textField outlet 需要連接在故事板文件中,您需要一種觸發(fā)您添加的 dismissKeyboard(_ :) 方法的方法。
要處理第一項(xiàng),請打開 Main.storyboard 并選擇 Conversion View Controller。右鍵從 Conversion View Controller 拖動到畫布上的文本字段,并將其連接到 textField。
現(xiàn)在您需要一種方式來觸發(fā)您實(shí)現(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連接手勢識別器動作

實(shí)現(xiàn)溫度轉(zhuǎn)換
了解了界面的基本原理后,讓我們來實(shí)現(xiàn)從華氏溫度轉(zhuǎn)為攝氏溫度。 您將要存儲當(dāng)前的華氏值,并在文本字段更改時計(jì)算攝氏值。
在 ConversionViewController.swift 中,為華氏值添加一個屬性。 這將是溫度的可選測量 (Measurement<UnitTemperature>?)。
@IBOutlet var celsiusLabel: UILabel!
var fahrenheitValue: Measurement<UnitTemperature>?
該屬性是可選的原因是因?yàn)橛脩艨赡軟]有鍵入一個數(shù)字,類似于您之前修復(fù)的空字符串問題。
現(xiàn)在為 Celsius 值添加一個用于計(jì)算的屬性。 該值將根據(jù)華氏值計(jì)算。
var fahrenheitValue: Measurement<UnitTemperature>?
var celsiusValue: Measurement<UnitTemperature>? {
??if let fahrenheitValue = fahrenheitValue {
????return fahrenheitValue.converted(to: .celsius)
??} else {
????return nil
??}
}
首先檢查一下是否有華氏值。 如果有,您將該值轉(zhuǎn)換為等值的攝氏度。 如果沒有華氏值,那么您不能計(jì)算攝氏度值,因此您返回 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)換實(shí)現(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ùn)行應(yīng)用程序。 華氏和攝氏之間的轉(zhuǎn)換效果非常好——只要您輸入有效的數(shù)字。 (它顯示的數(shù)字有些你并不想要(小數(shù)點(diǎn)后的數(shù)字過多),接下來我們來解決它)
如果應(yīng)用程序首次啟動時就更新 celsiusLabel,而不是仍然顯示 “100”,那就更好了。
覆蓋 viewDidLoad() 以設(shè)置初始值,類似于第1章中的操作。
override func viewDidLoad() {
??super.viewDidLoad()
??updateCelsiusLabel()
}
在本章的剩余部分中,您將更新 WorldTrotter 以解決兩個問題:您將格式化 Celsius 值以顯示最多一個小數(shù)位的精度,并且不允許用戶鍵入多個小數(shù)分隔符。
您的應(yīng)用程序還有其他幾個問題,但現(xiàn)在您將重點(diǎ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í)例化數(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ùn)行應(yīng)用程序。 輸入多個華氏溫度觀察格式化方法是否有效。 您將不會在攝氏標(biāo)簽上看到多于一位的小數(shù)出現(xiàn)。
在下一節(jié)中,您將更新應(yīng)用程序,實(shí)現(xiàn)在文本字段中接受最多一個小數(shù)分隔符。 為此,您將使用一種常見的 iOS 設(shè)計(jì)模式,稱為 委托模式(delegation)。
委托模式
委托是一個面向?qū)ο蟮?回調(diào)(callback) 方法。 回調(diào)是在事件發(fā)生前提供的一個函數(shù),每次事件發(fā)生時都會調(diào)用它。 一些對象需要對多個事件進(jìn)行回調(diào)。 例如,當(dāng)用戶輸入文本以及用戶按 Return 鍵時,文本字段都需要“回調(diào)”。
然而,兩個(或多個)回調(diào)函數(shù)之間沒有內(nèi)置的方式來協(xié)調(diào)和共享信息。 這是委托所解決的問題——你提供一個委托來接收特定對象的所有與事件有關(guān)的回調(diào)。 然后,該委托對象可以進(jìn)行存儲,操作等,并在它認(rèn)為合適的時候從回調(diào)中傳遞信息。
當(dāng)用戶文本字段輸入時,該文本字段將詢問其委托是否接受用戶所做的更改。 對于 WorldTrotter,如果用戶嘗試輸入第二個小數(shù)分隔符,則要拒絕該更改。 文本字段的委托將是 ConversionViewController 的實(shí)例。
符合協(xié)議
第一步是通過聲明 ConversionViewController 符合 UITextFieldDelegate 協(xié)議,使 ConversionViewController 類的實(shí)例成為 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é)議實(shí)例; 它只是方法和屬性的列表。 相反,實(shí)現(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。
接下來,您將實(shí)現(xiàn)您感興趣的 UITextFieldDelegate 方法 —— textField(_:shouldChangeCharactersIn:replacementString :)。 因?yàn)槲谋咀侄卧谄湮兄姓{(diào)用此方法,所以必須在 ConversionViewController.swift 中實(shí)現(xiàn)它。
在 ConversionViewController.swift,實(shí)現(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能夠自動完成這個方法,因?yàn)?ConversionViewController 符合 UITextFieldDelegate。在實(shí)現(xiàn)協(xié)議的方法之前,先聲明一個協(xié)議是一個好主意,這樣 Xcode 才會提供這種支持。
構(gòu)建并運(yùn)行應(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ùn)行應(yīng)用程序。 嘗試輸入多個小數(shù)分隔符; 應(yīng)用程序?qū)⒕芙^您輸入的第二個小數(shù)分隔符。
協(xié)議其他
在 UITextFieldDelegate 協(xié)議中,有兩種方法:處理信息更新的方法和處理輸入請求的方法。 例如,如果文本字段的代理想知道用戶在文本字段上點(diǎn)擊的時候,文本字段的委托應(yīng)該實(shí)現(xiàn) textFieldDidBeginEditing(_ :) 方法。
另一方面,textField(_:shouldChangeCharactersIn:replacementString :) 是一個輸入請求。 文本字段在其委托中調(diào)用此方法來詢問替換字符串是否應(yīng)被接受或拒絕。 該方法返回一個 Bool,它是委托者的回復(fù)。
在協(xié)議中聲明的方法可以是必需的或可選的。 默認(rèn)情況下,協(xié)議方法是必要的,這意味著符合協(xié)議的類必須具有這些方法的實(shí)現(xiàn)。 如果一個協(xié)議有可選的方法,那么它們前面會有 optional。 回顧 UITextFieldDelegate 協(xié)議,您可以看到所有的方法都是可選的。 委托協(xié)議通常是這樣的。
青銅挑戰(zhàn):禁止字母字符
目前,用戶可以通過使用藍(lán)牙鍵盤或?qū)?fù)制的文本粘貼到文本字段中來輸入字母字符。 解決這個問題。 提示:您需要使用到 NSCharacterSet 類。