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

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

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

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2149閲覧

UITableViewCell内に配置したTextFieldの値を取得したい

pftyuk

総合スコア52

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2019/05/22 15:50

編集2019/05/26 15:00

前提・実現したいこと

textFieldを配置したカスタムセルを作成しました。
カスタムセルと同画面のボタンを押下時に、各カスタムセルのtextFieldに
入力されたそれぞれの値を取得したいのですが、調べても実装方法が分からず
手詰まってしまったので、ご教示頂きたいです。

具体的には、以下画面の+ボタンを押下時に
各セルの1000,2000,3000の値を取得したいです。

宜しくお願い致します。
イメージ説明

該当のソースコード

Swift

1class InputCostsViewController: UIViewController { 2 3 var items:[UtilityCosts] = [ 4 UtilityCosts.Electricity, 5 UtilityCosts.Gas, 6 UtilityCosts.Water 7 ] 8 9 var result:[UtilityCosts:String] = [:] 10 11 @IBOutlet weak var inputCostsTableView: UITableView!{ 12 didSet{ 13 let nib = UINib(nibName: InputCostsTableViewCell.identifire, bundle: nil) 14 inputCostsTableView.register(nib, forCellReuseIdentifier: InputCostsTableViewCell.identifire) 15 16 inputCostsTableView.dataSource = self 17 18 inputCostsTableView.allowsSelection = false 19 inputCostsTableView.tableFooterView = UIView(frame: .zero) 20 } 21 } 22 23 @IBAction func RegisterCosts(_ sender: UIButton) { 24 print(result) 25 26 //navigationController?.popViewController(animated: true) 27 } 28 29 override func viewDidLoad() { 30 super.viewDidLoad() 31 } 32} 33 34extension InputCostsViewController:UITableViewDataSource{ 35 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 36 return items.count 37 } 38 39 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 40 let cell = tableView.dequeueReusableCell(withIdentifier: InputCostsTableViewCell.identifire, for: indexPath) as! InputCostsTableViewCell 41 cell.configure(kind: items[indexPath.row]) 42 cell.delegate = self 43 44 return cell 45 } 46 47 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 48 return 50 49 } 50} 51 52extension InputCostsViewController:TextFieldEditCellDelegate{ 53 func textFieldEditingChanged(cell: InputCostsTableViewCell, value: String) { 54 let key = cell.kind 55 result.updateValue(value, forKey: key) 56 } 57}

Swift

1protocol TextFieldEditCellDelegate:class { 2 func textFieldEditingChanged(cell:InputCostsTableViewCell,value:String) 3} 4 5class InputCostsTableViewCell: UITableViewCell { 6 7 @IBOutlet weak var icon: UIImageView! 8 @IBOutlet weak var textField: UITextField!{ 9 didSet{ 10 textField.borderStyle = .none 11 textField.keyboardType = .numberPad 12 } 13 } 14 15 var kind = UtilityCosts.None 16 weak var delegate:TextFieldEditCellDelegate? = nil 17 static let identifire = "InputCostsTableViewCell" 18 19 override func awakeFromNib() { 20 super.awakeFromNib() 21 textField.addTarget(self, action: #selector(editingChanged), for: .editingChanged) 22 } 23 24 override func setSelected(_ selected: Bool, animated: Bool) { 25 super.setSelected(selected, animated: animated) 26 } 27 28 func configure(kind:UtilityCosts){ 29 self.kind = kind 30 self.icon.image = kind.icon 31 self.textField.placeholder = kind.name 32 } 33 34 @objc func editingChanged(textField: UITextField) { 35 self.delegate?.textFieldEditingChanged(cell: self, value: textField.text!) 36 } 37}

Swift

1enum UtilityCosts{ 2 case None 3 case Electricity 4 case Gas 5 case Water 6 7 var name:String{ 8 switch self { 9 case .None: 10 return "" 11 case .Electricity: 12 return "電気代" 13 case . Gas: 14 return "ガス代" 15 case .Water: 16 return "水道代" 17 } 18 } 19 20 var icon:UIImage?{ 21 switch self { 22 case .None: 23 return nil 24 case .Electricity: 25 return nil 26 case . Gas: 27 return nil 28 case .Water: 29 return nil 30 } 31 } 32}

試したこと

調べたことを記載します。

textFieldが入力されたタイミングでセルが1つであれば
こちらの方法で実装することはできましたが、セルが複数になった時の各値を
取得するところの解答にまでは辿り着けませんでした。

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

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

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

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

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

guest

回答1

0

ベストアンサー

UITableViewの特性として、

  • Cellは再利用される可能性がある≒スクロールして画面外に行くとセルにアクセスできなくなる可能性がある

という点に注意してください。
つまり全Cellがメモリ上に存在するとは限らないということです。

テキストフィールドの文字列と行番号(row)が取れているのでしたら、その値をメンバ変数として配列等に格納すれば良いと思います。
(こうすればCellが画面外に行って再利用されたとしても、入力した値をとっておくことができます。)

すべての値が必要になった場合はその配列の値を参照すればOKです。
また、セルの構築の際(func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath))、その配列に入っている値をUITextFiled.textに再度格納するという手順も必要です。

投稿2019/05/23 00:28

takabosoft

総合スコア8356

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

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

pftyuk

2019/05/23 14:25

ご回答頂きありがとうございます。 UITableViewの特性については理解しました。 飲み込みが悪く、いまいちピンときてないのでご教示下さい。 テキストフィールドの文字列と行番号(row)が取れているのでしたら、その値をメンバ変数として配列等に格納すれば良いと思います。 →こちらについては、どのタイミングで保存用の配列等に格納すれば宜しいのでしょうか・・・。  追記で書かせて頂いたのでご覧頂けると幸いですが、生成したセルのインスタンス自体を  保持するのとは違いますよね・・・? また、セルの構築の際(func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath))、その配列に入っている値をUITextFiled.textに再度格納するという手順も必要です。 →こちらは"再度格納するという手順も必要"理由が全くわからなかったので  もう少し詳しくお聞かせ願いたいです。 恐縮ですが、宜しくお願いします。
takabosoft

2019/05/24 00:52 編集

格納するタイミングは参考にしたサイトと同じ「UITextFieldDelegate.textFieldDidEndEditing」か、一文字でも変わったら即座に格納(addTarget&.editingChanged)でもどちらもでたぶん大丈夫ですので検証してみてください。 保持するのはセルやUITextFieldのインスタンスではなく、UITextField.text、つまり文字列(もしくはそれを変換した数値)だけです。 > こちらは"再度格納するという手順も必要"理由が全くわからなかった 極端な話ですが、例えば理論上のCellの数が1000個で画面に収まるセルが10個しかないと仮定します。 (※UITableViewはこの時画面に表示される最低限のCell(つまり10個)しかセルを要求しません。) あるCellのインスタンスAはItem0番目の表示のために構築されたとします。 ここでユーザーがCellのインスタンスAのテキストフィールドに1000を入力し、その値(1000)をメンバ変数に保持できたとします。 さて、回答欄でも説明したとおり、UITableViewのCellのインスタンスはメモリ節約のためにリサイクルされるという特性があります。 実際とは異なりますが、例えばテーブルを少し上にスクロールすると、 Item0番目として用意したCellのインスタンスAは画面外に出ます。 すると、UITableViewは、このCellのインスタンスAを不要なものとしてテーブルビューから外し、プールします(適当な場所に置いておきます)。 次にスクロール後出てくるItem10番目のためにUITableViewはユーザーへcellの提供をdelegate経由で指示します。 このときfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)関数内で > tableView.dequeueReusableCell(withIdentifier: "InputCostsTableViewCell", for: indexPath) というAPIを呼んでセルのインスタンスを構築していると思いますが、この関数名には よく見ると「Reusable」という単語が含まれているのがわかると思います。 つまり再利用できるセルがあるならそれをデキュー(≒プールしてあるセルがあるならもってこい)、無いなら新規にインスタンスを作成せよ、というAPIになります。 この例え話ではさきほどItem0用に作成したCellのインスタンスAがプールされていますので、 Item10番用にcellをdequeueするとCellのインスタンスAがもらえます。 するとどうでしょう、cellのインスタンスAのUITextFiledには前に入力した1000という文字列が残っていますから、Item10番目が本来表示するべき値とは異なってしまうことになります。 そのため、UITextFiled.textに再度適切な値を格納(新しいItemであれば空だし、過去に入力済みの値であればその値を再度格納)しないと、再利用したときに前の値が出てしまい、おかしなことになるというわけです(再利用でなく、新規作成されたCellのインスタンスに対しても同様に再格納は必要ですが)。 --- 長々と書きましたが、今回のように3つしかItemが無いと決まっているのでしたら、セルのリサイクルについて意識する必要は実はありませんし、「リサイクルの機構をあえて使わない」という手もあります。ただUITableViewとの付き合いはおそらく長くなると思うので、仕組みをしっかりと理解しておいてほしいと思います。あとこのCellリサイクルの仕組みはUICollectionViewにもあります。
pftyuk

2019/05/26 15:09

ご回答頂きありがとうございます。 返信が遅くなり大変恐縮です。 UITableViewのセルの再利用について 非常に細かくご説明頂き、本当にありがとうございます。 具体的な例を挙げてご説明頂き、頭が上がりません。。。 正直UITableViewについては知識としてかなり怪しい部分が多いので 少しずつ仕組みについてきちんと理解を深めていきたいと思います。 格納については実装ができたので、質問欄のコードを 解決済みのものに修正させて頂きました。 UITextFieldDelegate.textFieldDidEndEditingで実装してみたところ カーソルが離れたタイミングでの呼び出しになり都合が悪かっため ご教示頂いたaddTarget&.editingChangedで試してみました。 正直、addTarget辺りの書き方がきちんと飲み込めてないので調べて理解を深めておきます。 書き方等、怪しい所があればご指摘頂けると幸いです。 質問事項については解決しましたので ベストアンサーとし閉じさせて頂きました。この度は誠にありがとうございました。
takabosoft

2019/05/27 01:04

addTarget&.editingChangedは動いていればそれで問題ありません。おそらくストーリーボードからでもeditingChangedの@IBActionをドラッグ&ドロップ作れますので、そちらからのほうが楽かもしれません(コードからでもなんら問題はありません)。 本題とは逸れますが、enumの各要素の頭文字は小文字が一応公式な書き方です。 https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html (Swift初期のバージョンでは頭文字は大文字でしたが、途中のバージョンから小文字が正式になりました)
pftyuk

2019/05/27 13:17

ストーリーボード から作れるのは知りませんでした。 enumの各要素は今は小文字が公式的な書き方なのですね。 こちらも知らなかったので修正してみます。ご丁寧にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問