SwiftUI 輸入文字的 TextField,SecureField,TextEditor & 猜數字 App

TextField,SecureField & TextEditor 專門讓使用者輸入文字,接下來讓我們好好認識它們跟利用它們實現猜數字 App。

綁定型別 String 變數的單行 TextField

TextField 可以輸入單行的文字,它的 init 接受型別 Binding<String> 的參數,所以我們必須傳入型別 String 的變數讓它綁定。當使用者輸入文字時,TextField 綁定的資料會更新。

顯示 Text Field

struct ContentView: View {
@State private var name = ""

var body: some View {
TextField("你的名字", text: $name)
.padding()
}
}

TextField 的 init 如下。

init(_ titleKey: LocalizedStringKey, text: Binding<String>)

參數說明。

  • titleKey

說明 text filed 的目的。當 TextField 尚未輸入文字時,titleKey 將成為顯示的提示文字(placeholder)。

  • text

型別 Binding<String> 的參數 text 將綁定變數 name,方便我們取得使用者輸入的文字。

測試 text field 時請儘量從模擬器測試,因為 preview 雖然可以輸入文字,但是無法顯示鍵盤。

自動避開鍵盤的 TextField & TextEditor

當鍵盤出現時,App 畫面將自動上移,我們再也不怕鍵盤檔到輸入的文字。

相關說明可參考以下連結。

圓角邊框和鍵盤樣式

textFieldStyle 設定邊框樣式,keyboardType 設定鍵盤樣式,在此我們設定圓角邊框和數字鍵盤。

struct ContentView: View {
@State private var age = ""

var body: some View {
TextField("你的年紀", text: $age)
.textFieldStyle(.roundedBorder)
.keyboardType(.numberPad)
.padding()
}
}

客製邊框

利用 overlay 或 background 客製邊框。

  • 寫法 1。
struct ContentView: View {
@State private var name = ""

var body: some View {
TextField("你的名字", text: $name)
.padding()
.overlay(.blue, in: .rect(cornerRadius: 20).stroke(lineWidth: 5))
.padding()
}
}
  • 寫法 2。
struct ContentView: View {
@State private var name = ""

var body: some View {
TextField("你的名字", text: $name)
.padding()
.overlay {
textFieldBorder
}
.padding()
}

var textFieldBorder: some View {
RoundedRectangle(cornerRadius: 20)
.stroke(.blue, lineWidth: 5)
}
}

多行捲動文字的 TextEditor

若想輸入多行文字,則可搭配容納多行捲動文字的 TextEditor。

struct ContentView: View {
@State private var message = ""

var body: some View {
TextEditor(text: $message)
.frame(height: 300)
.padding()
}
}

不過 TextEditor 預設沒有邊框,我們可透過 border 或前面介紹的 overlay / background 加上邊框。

struct ContentView: View {
@State private var message = ""

var body: some View {
TextEditor(text: $message)
.frame(height: 300)
.padding()
.border(.black, width: 5)
.padding()
}
}

讀取 text filed / text editor 的文字

由於 text filed / text editor 利用 Binding 跟字串型別的變數綁在一起,因此我們可從它綁定的變數讀取 text filed / text editor 的文字。

範例: 按下 button 時讀取得 text filed 的文字。

struct ContentView: View {
@State private var name = ""

var body: some View {
VStack {
TextField("你的名字", text: $name)
.textFieldStyle(.roundedBorder)
.padding()
Button("印出名字") {
print(name)
}
}
}
}

輸入 Peter 後,點選印出名字將印出 Peter。

將字串變數字

利用 Int( ) 或 Double( ),在 ( ) 裡傳入字串,可以將字串變成數字。比方 Int("18") 可得到 18。不過傳入的字串不見得是數字,所以可能生不出數字,比方 Int("peter")將得到 nil,因此得到的數字型別將為 optional。

以下程式利用 optional binding 的 if let 語法讀取字串生成的數字,相關說明可參考下連結。

struct ContentView: View {
@State private var age = ""

var body: some View {
VStack {
TextField("你的年紀", text: $age)
.textFieldStyle(.roundedBorder)
.keyboardType(.numberPad)
.padding()
Button("印出年紀") {
if let age = Int(age) {
print(age)
} else {
print("not number")
}
}
}
}
}

指定文字格式和將文字變數字

除了手動將字串變數字,我們也可以透過 init 的參數 format 指定文字格式,比方傳入 .number 表示我們想要數字時,參數 value 將可傳入數字型別的 state property age 的 binding,SwiftUI 會貼心地幫我們將使用者輸入的字串變成數字存入 age。

struct ContentView: View {
@State private var age: Int?

var body: some View {
VStack {
TextField("你的年紀", value: $age, format: .number)
.textFieldStyle(.roundedBorder)
.keyboardType(.numberPad)
.padding()
Button("印出年紀") {
print(age ?? 0)
}
}
}
}

當使用者輸入 18 時,字串 "18" 將自動被轉成數字 18,存入變數 age。若使用者輸入的不是數字,它將無法順利轉成數字,此時 age 的內容將不會更新。

當 TextField 綁定的資料型別和 format 設定的格式不合時,將產生紅色錯誤,比方搭配 .number,但綁定的資料型別是字串。

輸入密碼的 SecureField

讓輸入的文字變成小圓點。

struct ContentView: View {
@State private var password = ""

var body: some View {
SecureField("password", text: $password)
.textFieldStyle(.roundedBorder)
.padding()
}
}

按下 return 鍵時執行程式

呼叫 modifier onSubmit,傳入 onSubmit 的 closure 程式將在使用者按下 return 鍵時執行。

struct ContentView: View {
@State private var name = ""

var body: some View {
TextField("你的名字", text: $name)
.textFieldStyle(.roundedBorder)
.padding()
.onSubmit {
print(name)
}
}
}

猜數字 App 範例

以下是簡單的猜數字範例。

struct ContentView: View {
@State private var guessNumber: Int?
@State private var showResult = false
@State private var result = ""
var answer = Int.random(in: 0...100)
@FocusState private var isFocused: Bool

var body: some View {
VStack {
TextField("猜一猜七夕狐狸會送你幾朵玫瑰", value: $guessNumber, format: .number)
.textFieldStyle(.roundedBorder)
.keyboardType(.numberPad)
.focused($isFocused)
.padding()
Button("Guess") {
isFocused = false
if let guessNumber {
if guessNumber == answer {
result = "答對了"
} else if answer > guessNumber {
result = "狐狸很愛你,請猜多一點"
} else {
result = "狐狸不夠愛你,請猜少一點"
}
}
showResult = true
}
}
.alert(result, isPresented: $showResult, actions: {
Button("OK") {}
})
}
}

說明。

.focused($isFocused)

focused 控制鍵盤的顯示和關閉,isFocused 則以 @FocusState 宣告,當 isFocused 為 true 時鍵盤將顯示,游標將跑到 text field 上。當 isFocused 為 false 時鍵盤將關閉,游標將離開 text field。

Button("Guess") {
isFocused = false
if let guessNumber {
if guessNumber == answer {
result = "答對了"
} else if answer > guessNumber {
result = "狐狸很愛你,請猜多一點"
} else {
result = "狐狸不夠愛你,請猜少一點"
}
}
showResult = true
}

button 按下時比對 guessNumber & answer。

.alert(result, isPresented: $showResult, actions: {
Button("OK") {}
})

利用 alert 顯示結果,alert 的相關說明可參考以下連結。

設定 return 鍵的文字

利用 submitLabel 設定 return 鍵的文字。

struct ContentView: View {
@State private var name = ""

var body: some View {
TextField("你的名字", text: $name)
.textFieldStyle(.roundedBorder)
.submitLabel(.next)
.padding()
}
}

設定 SwiftUI Text、TextField、TextEditor 的文字對齊

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com