Swift UI数値のみを受け入れるTextFieldの作成方法


10

私はswiftUIとiOを初めて使用し、数字のみを受け入れる入力フィールドを作成しようとしています

 TextField("Total number of people", text: $numOfPeople)

TextFieldではアルファベット文字も使用できますが、ユーザーが数字のみを入力するように制限するにはどうすればよいですか?


TextFieldには、フォーマッタを引数として取るinit methidがあることに注意してください
Joakim Danielson

@JoakimDanielsonフォーマッタの使用方法を示すのを手伝ってくれる?
Lupyana Mbembati

回答:


11

キーボードのタイプを設定して、TextFieldユーザーが入力できるものを制限することができます。

TextField("Total number of people", text: $numOfPeople)
    .keyboardType(.numberPad)

Appleのドキュメントを見つけることができ、ここで、あなたはすべてのサポートされているキーボードの種類のリストを参照することができ、ここを

注:iPadにはテンキーがないため、これはiPadでは機能しません。より良いソリューションのチェックアウトについては、以下のJohn Mのソリューションをご覧くださいhttps://stackoverflow.com/a/58736068/5508175


1
これは私が👍探していたまさにです
Lupyana Mbembati

マイナーな問題が発生しています。入力を完了してもキーボードが消えません。なぜでしょうか。
Lupyana Mbembati

1
@LupyanaMbembatiこのSOの質問/回答には、キーボードを使い終わったらキーボードを非表示にする方法に関するいくつかの提案があります。 stackoverflow.com/questions/56491386/...
アンドリュー・

1
これはiPadでは機能しないことに注意してください
LuLuGaGa

3
これは実際には非数値入力を妨げません。私の答えを見てください。
John M.

23

テンキーを表示することは良い最初のステップですが、実際には不正なデータの入力を防ぐことはできません。

  1. ユーザーは数値以外のテキストをテキストフィールドに貼り付けることができます
  2. iPadユーザーはまだフルキーボードを手に入れます
  3. Bluetoothキーボードが接続されている人なら誰でも何でも入力できます

あなたが本当にやりたいことは、次のように入力を無害化することです:

import SwiftUI
import Combine

struct StackOverflowTests: View {
    @State private var numOfPeople = "0"

    var body: some View {
        TextField("Total number of people", text: $numOfPeople)
            .keyboardType(.numberPad)
            .onReceive(Just(numOfPeople)) { newValue in
                let filtered = newValue.filter { "0123456789".contains($0) }
                if filtered != newValue {
                    self.numOfPeople = filtered
                }
        }
    }
}

numOfPeople変更されるたびに、非数値がフィルターで除外され、フィルターされた値が比較numOfPeopleされて、もう一度更新する必要があるかどうかが確認され、無効な入力がフィルターされた入力で上書きされます。

Justパブリッシャーはあなたにそれを要求することに注意してくださいimport Combine

編集:

Justパブリッシャーを説明するために、の値を変更したときに発生する次の概念的な概要を検討してくださいTextField

  1. TextFieldはa BindingをとるためString、フィールドの内容が変更されると、その変更が@State変数に書き戻されます。
  2. 変数に@State変更のマークが付けられると、SwiftUI bodyはビューのプロパティを再計算します。
  3. body計算中に、Just発行者が作成されます。Combineには、時間の経過とともに値を出力するさまざまなパブリッシャーがたくさんありますが、Justパブリッシャーは単一の値(の新しい値numberOfPeople)を「ちょうど」受け取り、要求されたときにそれを出力します。
  4. このonReceiveメソッドは、Viewパブリッシャー(この場合Justは先ほど作成したパブリッシャー)のサブスクライバーになります。サブスクライブすると、すぐにパブリッシャーから使用可能な値を要求されますnumberOfPeople。その中の1つだけの新しい値があります。
  5. ときにonReceive、加入者が値を受信すると、指定されたクロージャを実行します。私たちの閉鎖は2つの方法のうちの1つを終わらせることができます。テキストがすでに数値のみの場合は、何もしません。フィルターされたテキストが異なる場合、@State変数に書き込まれ、ループが再び開始されますが、今回は、プロパティを変更せずにクロージャーが実行されます。

チェックアウト組み合わせる使い方詳細は。


私はSwift / SwiftUI(Windows C#とWeb Typescriptの世界からのもの)を調べ始めたばかりで、最初に遭遇した問題は、ユーザー入力をフィルタリングして数値のみを許可する方法でした。正規表現に基づいて入力をフィルタリングすることは非常に一般的です。あなたの解決策はまさに私が探していたもののようです-本当にありがとう 私はドキュメントを見て、せいぜい少し不足しているようです。'Just'パブリッシャーの理由を説明していただけませんか
Jesper Kristiansen

この回答の+1。ただし、そのためにCombineをインポートすることは理解できません。ただ何をしているのか。ありがとう
davidev

@JesperKristiansen @davidev Just出版社の説明を追加しました。
ジョンM.

素敵な説明ありがとうございます!あなたの解決策は機能しますが、少しの間、フィルタリングされる前に古い値を見ることができます。これを防ぐ方法はありますか?
Lupurus

1
ちなみに、.onReceiveは、sthのときに毎回呼び出されます。ビューの他の部分は変更されます。これはちょっと重すぎませんか?
Lupurus

1

Combineand を使用する必要はありません。onReceive次のコードも使用できます。

class Model: ObservableObject {
    @Published var text : String = ""
}

struct ContentView: View {

    @EnvironmentObject var model: Model

    var body: some View {
        TextField("enter a number ...", text: Binding(get: { self.model.text },
                                                      set: { self.model.text = $0.filter { "0123456789".contains($0) } }))
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(Model())
    }
}

残念ながら小さなちらつきもあるので、許可されていない文字を非常に短い時間で見ることもできます(私の目では、 Combine


0

別のアプローチとしては、TextFieldビューをラップし、2つの値を保持するビューを作成する方法があります。入力された文字列を保持するプライベート変数と、同等のDoubleを保持するバインド可能な値です。ユーザーが文字を入力するたびに、Doubleを更新しようとします。

基本的な実装は次のとおりです。

struct NumberEntryField : View {
    @State private var enteredValue : String = ""
    @Binding var value : Double

    var body: some View {        
        return TextField("", text: $enteredValue)
            .onReceive(Just(enteredValue)) { typedValue in
                if let newValue = Double(typedValue) {
                    self.value = newValue
                }
        }.onAppear(perform:{self.enteredValue = "\(self.value)"})
    }
}

次のように使用できます。

struct MyView : View {
    @State var doubleValue : Double = 1.56

    var body: some View {        
        return HStack {
             Text("Numeric field:")
             NumberEntryField(value: self.$doubleValue)   
            }
      }
}

これは必要最低限​​の例です-不十分な入力や境界チェックなどの警告を表示する機能を追加したい場合があります...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.