質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Q&A

解決済

3回答

712閲覧

Textfieldを選択した時に元から入っている数値を0に置き換えたい

mnkd

総合スコア37

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

0グッド

0クリップ

投稿2022/04/06 12:26

前提

SwiftUIのテキストフィールドについてです。
入力されたデータをmodelDataに入れています。
Textfieldの引数のvalueに$modelData.costのように入れて使っています。

実現したいこと

  • テキストフィールドに入力するときに元から入っていた数値を0に置き換えたい。

発生している問題・エラーメッセージ

.onChangeでテキストフィールドにフォーカスインしたタイミングでvalue = 0 としているのですが反映されません。

該当のソースコード

Swift

1import SwiftUI 2import Combine 3 4struct FormInfo<T:Numeric> : View { 5 @ObservedObject var modelData: ModelData 6 @Binding var value: T 7 @FocusState var textfieleFoucused: Bool 8 9 var title: String 10 var unit: String 11 12 private let numberFormatter: NumberFormatter = { 13 let f = NumberFormatter() 14 f.numberStyle = .decimal 15 f.zeroSymbol = "" 16 f.isLenient = true 17 return f 18 }() 19 20 var body: some View { 21 HStack { 22 Text(title) 23 TextField("0", value: $value, formatter: numberFormatter) 24 .focused($textfieleFoucused) 25 .keyboardType(.numbersAndPunctuation) 26 .multilineTextAlignment(.trailing) 27 .onSubmit { 28 if ((modelData.profitRate + modelData.commission) >= 100) { 29 modelData.profitRate = 0.0 30 modelData.commission = 0.0 31 } 32 } 33 Text(unit) 34 } 35 .onChange(of: textfieleFoucused) { newValue in 36 if newValue { 37 value = 0 38 print("フォーカスイン") 39 } else { 40 print("フォーカスアウト") 41 } 42 } 43 } 44} 45 46import Foundation 47 48final class ModelData: ObservableObject { 49 @Published var cost: Int = 0 50 @Published var postage: Int = 0 51 @Published var commission: Double = 0.0 52 @Published var profit: Int = 0 53 @Published var profitRate: Double = 0.0 54 @Published var sellPrice: Int = 0 55 @Published var memo: String = "" 56 @Published var commisionToggle: Bool = false 57} 58

試したこと

$valueにしてみましたがエラーになりました。

補足情報(FW/ツールのバージョンなど)

Xcode13.3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

自己解決

xg63ex2b様、tyagishi様コメントどうもありがとうございます。
先日xg63ex2b様が試していただいた結果、挙動がおかしいとのことだったのでその後、AppleのTechnical Support Incident (TSI)に問い合わせをしていて本日返事が届きました。
結論を言いますと、「The reported behavior appears to be the result of a system bug.」ということです。
バグレポートを出してくれとのことだったので先ほど提出しておきました。
勉強を始めたばかりで何がおかしいのかもよくわかっていないので、お二人のご意見はとても参考になりました。
今後ともよろしくお願い致します。

投稿2022/04/12 11:53

mnkd

総合スコア37

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/04/12 13:30

コメントがなかったので気になっていましたが、Appleに問い合わせしていたのですね。 すごいですね、その行動力を見習おうと思いました。 Swiftはこういった不具合や、バージョンアップによる非互換など、開発者にとっては大変そうなのですね・・ tyagishiさん フォローありがとうございます。
mnkd

2022/04/12 13:38

Appleから返事が来てからコメント返そうと思っていたのですが思ったより時間がかかって失礼いたしました。 また色々お聞きするかもしれませんがその時はよろしくお願いいたします。
guest

0

xg63ex2b さんも言われていますが、Formatterを使った、TextField の動作は怪しいと思います。
個人的には、Local Binding を使った方法がお手軽で気に入っています。

@FocusState を使って、制御してみたところ、フィールド選択状態で 0設定することができるようです。

struct ContentView: View { @State private var value: Double = 10.0 @FocusState private var field: Bool var body: some View { VStack { Text("Current Value: \(value)") TextField("field", text: Binding<String>(get: { return String("\(value)") }, set: { newValue in if let value = Double(newValue) { self.value = value } })) .focused($field) .onChange(of: field) { newValue in if newValue { value = 0 } } } } }

もう、解決されているかもしれませんが、ご参考まで。

投稿2022/04/12 00:22

tyagishi

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

TextFieldの扱いで大変そうですね・・

ちょっと試していたのですが、TextField.init(_:value:formatter:)だと制御がちゃんとできないっぽいですよね。
フォーカスが当たったタイミングでNumberFormatter.zeroSymbolを"0"とか""に変更しても適用されません・・
フォーカスが外れるタイミングで適用されます・・

TextField.init(_:text:)だとちょっとだけマシな制御ができる印象でした。
試したコードを貼り付けておきます。
(このコードだとGeneric Typesが逆に面倒で、Int型のFormInfo、Double型のFormInfoをそれぞれ実装した方が良さそうにも思ってしまいます・・あるいは私の知識不足なだけかもしれません・・)

swift

1import SwiftUI 2 3struct ContentView: View { 4 @ObservedObject var modelData = ModelData() 5 var body: some View { 6 FormInfo(modelData: modelData, value: $modelData.cost, title: "Title1", unit: "Unit1") 7 FormInfo(modelData: modelData, value: $modelData.postage, title: "Title2", unit: "Unit2") 8 FormInfo(modelData: modelData, value: $modelData.commission, title: "Title3", unit: "Unit3") 9 Text(String(modelData.cost)) 10 Text(String(modelData.postage)) 11 Text(String(modelData.commission)) 12 } 13} 14 15struct ContentView_Previews: PreviewProvider { 16 static var previews: some View { 17 ContentView() 18 } 19} 20 21import SwiftUI 22import Combine 23 24struct FormInfo<T:Numeric> : View { 25 @ObservedObject var modelData: ModelData 26 @Binding var value: T 27 @FocusState var textfieleFoucused: Bool 28 // TextField用のString型のプロパティ 29 @State private var valueString: String = "" 30 31 var title: String 32 var unit: String 33 34 private let numberFormatter: NumberFormatter = { 35 let f = NumberFormatter() 36 f.numberStyle = .decimal 37 f.zeroSymbol = "" 38 f.isLenient = true 39 return f 40 }() 41 42 var body: some View { 43 HStack { 44 Text(title) 45 TextField("0", text: $valueString) 46 .focused($textfieleFoucused) 47 .keyboardType(.numbersAndPunctuation) 48 .multilineTextAlignment(.trailing) 49 .onSubmit { 50 if ((modelData.profitRate + modelData.commission) >= 100) { 51 modelData.profitRate = 0.0 52 modelData.commission = 0.0 53 } 54 } 55 Text(unit) 56 } 57 .onChange(of: textfieleFoucused) { newValue in 58 if newValue { 59 print("フォーカスイン") 60 if value == 0 { 61 valueString = "" 62 } 63 } else { 64 print("フォーカスアウト") 65 // フォーカスが外れたらNumberFormatterを適用 66 if value is Int, 67 let v = value as? NSNumber { 68 numberFormatter.zeroSymbol = "0" 69 valueString = numberFormatter.string(from: v) ?? "0" 70 numberFormatter.zeroSymbol = "" 71 } 72 if value is Double, 73 let d = value as? Double { 74 let v = NSNumber(value: d) 75 numberFormatter.zeroSymbol = "0.0" 76 valueString = numberFormatter.string(from: v) ?? "0.0" 77 numberFormatter.zeroSymbol = "" 78 } 79 } 80 } 81 .onAppear { 82 // String型のプロパティに値を設定 83 valueString = "\(value)" 84 } 85 .onChange(of: valueString) { newValue in 86 // TextFieldから変更があったらvalueに反映 87 if value is Int, 88 let v = Int(newValue) { 89 value = v as! T 90 } 91 if value is Double, 92 let v = Double(newValue) { 93 value = v as! T 94 } 95 } 96 } 97} 98 99import Foundation 100 101final class ModelData: ObservableObject { 102 @Published var cost: Int = 0 103 @Published var postage: Int = 0 104 @Published var commission: Double = 0.0 105 @Published var profit: Int = 0 106 @Published var profitRate: Double = 0.0 107 @Published var sellPrice: Int = 0 108 @Published var memo: String = "" 109 @Published var commisionToggle: Bool = false 110}

投稿2022/04/06 15:11

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mnkd

2022/04/06 22:09

いつも教えていただきありがとうございます。 勉強を始めたばかりなので自分が間違っているのか仕様なのか分からないので教えていただいてとても助かります。 いただいたコードを試してみたのですが入力後にリターンで確定すると数値が0になってしまいます。 やはりIntの場合とDoubleの場合と分けた方がいいのでしょうか。
mnkd

2022/04/06 22:40

modelDataを全部StringにしてTextField.init(_:text:)の方で管理することも考えたのですが、数値の計算をする時に全ての値にキャストするのはごちゃごちゃになって嫌だなあと思ってしてないのですがそちらのやり方についてはどう思われますか?
退会済みユーザー

退会済みユーザー

2022/04/06 23:17

コメントありがとうございます。 > いただいたコードを試してみたのですが入力後にリターンで確定すると数値が0になってしまいます。 ごめんなさい、コメントをいただいて試してみたのですが、確かに、そうなってしまいますね・・ リターンキーは確認していませんでした。 タブキーでフォーカスアウトするとうまくいっているように見えたのですけどね・・ リターンキーを押すと`.onChange(of: valueString)`のタイミングでなぜがvalueが0になってしまっているようで、 すぐには解決方法が思いつきませんでした・・ > modelDataを全部StringにしてTextField.init(_:text:)の方で管理することも考えたのですが、数値の計算をする時に全ての値にキャストするのはごちゃごちゃになって嫌だなあと思ってしてないのですがそちらのやり方についてはどう思われますか? 私も嫌かなと思ってしまいます。 でも、モデルのクラスと画面に表示するクラスと2つ存在するのは、ありなのかもしれないとも思いました。 モデルクラスはIntやDoubleの型で定義して、画面表示用のクラスはStringの型で定義して、 値を同期するのをどう実装するのか、Swiftにおける具体的な実装イメージはまだないのですが・・ ただ、計算処理はモデルのクラスにあった方が良い気がします。 「Computed Properties」「Property Observers」などで計算すると良さそうかなと思いました。 https://docs.swift.org/swift-book/LanguageGuide/Properties.html
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問