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()
}
}