[SwiftUI] テキストフィールドの入力

テキストフィールドに入力できるのはStringデータだけなので、型変換をする必要がある
オプショナルバリュー、オプショナルバインディングを学ぶ

TextField, textFieldStyle, isEmpty, keyboardType, Group, @FocusState, fucused, toolbar, ToolbarItemGroup, guard let-else, if let-else, !, ??, nil

### テキストフィールドを作る
入力した名前を表示する
 L TextFieldで作成する

    @State var name: String = ""
        
    var body: some View {
        TextField("お名前は?", text: $name)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .frame(width: 250)
        
        if (!name.isEmpty){
            Text("\(name)さん、こんにちは!")
        }
    }

キーボードの種類を指定
L keyboardType(.numberPad)のように指定する
L 複数のビューを一纏めにする場合は Groupで囲む
  L guard let-elseで整数に変換できたら変換後の値をnumに代入、変換できなかったらfalse

struct ContentView: View {
    @State var kosu:String = ""
    let tanka:Double = 250
    let tax:Double = 1.1
        
    var body: some View {
        VStack(alignment: .leading){
            
            HStack {
                Text("個数:").padding(.horizontal, 0)
                TextField("0", text: $kosu)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .keyboardType(.numberPad)
                    .frame(width: 100)
            }
            .font(.title)
            .frame(width: 200)
            
            Group {
                if kosuCheck(min: 1, max: 10){
                    Text("\(price())円です。")
                        .font(.title)
                } else {
                    Text("個数は1〜10個を入れてください。")
                        .foregroundColor(.red)
                        .font(.headline)
                }
            }.frame(width: 300, height: 30)
        }
    }
    
    func kosuCheck(min:Int, max:Int) -> Bool {
        guard let num = Int(kosu) else {
            return false
        }
        return (num>=min && num <= max)
    }
    
    func price() -> Int {
        if let num = Double(kosu){
            let result = Int(tanka * num * tax)
            return result
        } else {
            return -1
        }
    }
}

Doneボタンの追加
L focusが終わるとキーボードが下がる

@FocusState var isInputActive: Bool
    @State var kosu:String = ""
    let tanka:Double = 250
    let tax:Double = 1.1
        
    var body: some View {
        VStack(alignment: .leading){
            
            HStack {
                Text("個数:").padding(.horizontal, 0)
                TextField("0", text: $kosu)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .keyboardType(.numberPad)
                    .frame(width: 100)
                    .focused($isInputActive)
            }
            .font(.title)
            .frame(width: 200)
            
            Group {
                if kosuCheck(min: 1, max: 10){
                    Text("\(price())円です。")
                        .font(.title)
                } else {
                    Text("個数は1〜10個を入れてください。")
                        .foregroundColor(.red)
                        .font(.headline)
                }
            }.frame(width: 300, height: 30)
        }
        .toolbar {
            ToolbarItemGroup(placement: .keyboard){
                Spacer()
                Button("Done"){
                    isInputActive = false
                }
            }
        }
    }

### オプショナルバインディング
nilかもしれないオプショナルバリュー

let nums:[Int] = []
let lastNum = nums.last
let ans = lastNum * 2

var num:Int
num = 5
num = nil

代入できる変数を作る

var num:Int?
num = 5
num = nil

??演算子を使う

let nums:[Int] = [1, 2, 3]
let lastNum = nums.last ?? 0
let ans = lastNum * 2
print(ans)

### guard let-elseを使ったオプショナルバインディング
関数を実行する前にオプショナルバリューをチェックするオプショナルバインディング構文
L Int(kosu)がnilならfalseを戻す
  L if let elseでも同様にオプショナルバインディングを行う

    func kosuCheck(min:Int, max:Int) -> Bool {
        guard let num = Int(kosu) else {
            return false
        }
        return (num>=min && num <= max)
    }

なるほど、中々深いな
バリデーションでチェックしてたから意識したことなかったけど、Swiftの場合はnilをチェックする場合はこう書くのね。