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

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

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

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

ラジオボタン

ラジオボタンはフォームに使われる要素のひとつであり、ユーザに限られた選択肢からひとつの答えを選んでもらうというものです。

Swift

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

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

1回答

1901閲覧

【Swift】TableViewCell内に配置した複数ボタンの選択状況を値として出力する方法

tasmicsy

総合スコア13

TableView

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

ラジオボタン

ラジオボタンはフォームに使われる要素のひとつであり、ユーザに限られた選択肢からひとつの答えを選んでもらうというものです。

Swift

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

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

0グッド

0クリップ

投稿2021/10/16 11:05

前提・実現したいこと

Swiftでアンケート画面を作成しています。
下の写真のように、TableCell上部に質問文、下部に複数の円ボタンを配置します。
複数の質問文について5段階で評価をします。
QuizTableViewController
イメージ説明

回答完了ボタンを押したら、各質問で選択されている円ボタンに従って、値を返したいです。

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

回答完了ボタンがTableViewController内、円ボタンがTableViewCell内で設定しており、回答完了ボタンの@IBAction内で円ボタンを認識する方法がわかっていません。

該当のソースコード

TableCell毎にボタンにtagをつけて、どの問題文のボタンかを認識できるようにしました。
また、回答完了ボタンは画面遷移のみとし、円ボタン選択時に値が受け取れないか試しました。

Swift

1func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 2 guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuizCellIdentifier") as? Quiz2Cell else {fatalError("Could not dequeue a cell") 3 4 } 5 6 let series1 = series_questions[indexPath.row] 7//series_questionsは、Question(text: "質問文", number: Int)です。 8// numberに、各質問文の値を入れられないかと考えました。 9 10 cell.QuizLabel.text = series1.text 11 cell.QuizButton0.tag = indexPath.row 12cell.QuizButton0.addTarget(self, action: #selector(isQuizButton0Tapped(_:)), for: UIControl.Event.touchUpInside) 13 return cell 14 } 15//QuizButton0は、左端のボタンです。上手く回ればこれを端〜端のボタン分までコピペしようとしています。 16 17 @objc func isQuizButton0Tapped(_ sender: UIButton){ 18 print("(sender.tag)" + "は(sender.isSelected)になった") 19 series1.number = 0 //エラーCannot find 'series1' in scope 20 }

最後の文でエラー分が出てしまうことはわかるのですが、どう解決すればいいかわかりません。。

試したこと

上記にまとめました。

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

初学者で独学でやっており、初歩的な知識が抜けているかもしれません。また、こちらで質問するのも初めてでして、十分な情報を載せられているか不安なところがあります。不足している情報・知識などございましたらご教授いただけますと大変助かります。どうかご寛容なご助言をいただけますと幸いです。
よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

まず最初にプロパティ名は小文字で開始して下さい。この回答ではそのつもりで記載します。


Quiz2Cell クラスにはおそらく、

swift

1class Quiz2Cell: UITableViewCell { 2 @IBOutlet weak var quizLabel: UILabel! 3 @IBOutlet var quizButton0: UIButton! 4 @IBOutlet var quizButton1: UIButton! 5 ...

のようにボタン 5 個分のアウトレット接続があるのだと思いますが、まずこれをやめましょう。

次のように配列に 5 個のボタンをまとめておく アウトレットコレクション を使ってください。

swift

1class Quiz2Cell: UITableViewCell { 2 @IBOutlet weak var quizLabel: UILabel! 3 @IBOutlet var quizButtons: [UIButton]!

ストーリーボードで左側のボタンから順番にこのプロパティへアウトレット接続すればその順番のまま格納されます。


また、どのボタンが選択状態にあるのかを Quiz2Cell 自身も Int 型のプロパティで持ちましょう。

swift

1var currentQuizButtonIndex: Int = 0

次に、ボタンアクションを作ります。ひとつのアクションには複数のボタンを紐付けられます。 アウトレット接続と同じ要領です。5 個のボタン全てをこのアクションに紐づけて下さい。どのボタンを押しても押されたボタンが sender に渡されつつこのアクションが実行されます。

ここで、アウトレットコレクションにしたボタンの配列から押したボタンを検索すれば、そのインデックス=ボタンの位置になります。currentQuizButtonIndex にセットしましょう。

swift

1class Quiz2Cell: UITableViewCell { 2 ... 3 4 @IBAction func didTapQuizButton(_ sender: UIButton) { 5 if let index = self.quizButtons.firstIndex(of: sender) { 6 self.currentQuizButtonIndex = index 7 } 8 }

currentQuizButtonIndex の更新タイミングに合わせて、選択したボタンを黒丸へ、それ以外を白丸に戻す処理が必要です。

インデックスを渡したら、ボタンの配列の列挙を行い該当するボタンだけ黒丸になるよう関数を用意します。以下の関数はボタンタイトルで黒丸、白丸を表現していると仮定しています。

swift

1private func updateCurrentQuizButton(_ currentIndex: Int) { 2 for (index, quizButton) in self.quizButtons.enumerated() { 3 if index == currentIndex { 4 quizButton.setTitle("●", for: .normal) 5 } else { 6 quizButton.setTitle("○", for: .normal) 7 } 8 } 9}

プロパティには値のセット前後に処理を行わせる プロパティオブサーバ を記述できます。値をセットしたら処理を実行するよう didSet ブロックを currentQuizButtonIndex に追記し、上記の関数を実行させましょう。

swift

1var currentQuizButtonIndex: Int = 0 { 2 didSet { 3 let value = self.currentQuizButtonIndex 4 self.updateCurrentQuizButton(value) 5 } 6}

これで、ボタンを押したら、その位置をプロパティにセットし、そのセット後にボタンのタイトルを更新する処理の流れが組めました。


次は TableViewController が値の変更を知ることができるようにします。それには プロトコルデリゲート が適しています。

Quiz2Cell のファイルに次のようなプロトコルを追記します。そしてそのプロトコル型のプロパティ delegate を追加します。

swift

1protocol Quiz2CellDelegate { 2 func quiz2CellDidChangeCurrentQuizButtonIndex(_ cell: Quiz2Cell, index: Int) 3} 4 5class Quiz2Cell: UITableViewCell { 6 @IBOutlet weak var quizLabel: UILabel! 7 @IBOutlet var quizButtons: [UIButton]! 8 var delegate: Quiz2CellDelegate? 9 ... 10}

このプロトコルに定義した関数は名前の通り、選択されたクイズボタンのインデックスが変更されたという通知です。インデックスの変更時、このメソッドを delegate にセットされたものが実行するよう、currentQuizButtonIndexdidSet ブロックに追記しましょう。

swift

1var currentQuizButtonIndex: Int = 0 { 2 didSet { 3 let value = self.currentQuizButtonIndex 4 self.updateCurrentQuizButton(value) 5 if let delegate = self.delegate { 6 delegate.quiz2CellDidChangeCurrentQuizButtonIndex(self, index: value) 7 } 8 } 9}

これで、インデックスをセットしたタイミングで delegate にセル自身とボタンの位置を示すインデックスが渡るようになりました。


TableViewController でこのプロトコルを採用し、通知を受け取るメソッドを実装します。

swift

1class TableViewController: UITableViewController, Quiz2CellDelegate { 2 ... 3 4 func quiz2CellDidChangeCurrentQuizButtonIndex(_ cell: Quiz2Cell, index: Int) { 5 } 6}

セルの生成時に delegateTableViewController 自身を入れましょう。これまでの変更に沿い、セルの生成内容は次のようになります。

swift

1override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 2 guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuizCellIdentifier") as? Quiz2Cell else { 3 fatalError("Could not dequeue a cell") 4 } 5 6 let series1 = series_questions[indexPath.row] 7 cell.quizLabel.text = series1.text 8 cell.currentQuizButtonIndex = series1.number // 選択番号をセットするだけ 9 cell.delegate = self // デリゲートを設定 10 11 return cell 12}

最後です。実装したデリゲートメソッドではセルが渡されているので、テーブルビューにセルを渡すだけでセルのインデックスパスが取得できます。これは series_questions と対応しているわけだから、次のように対応する Question オブジェクトを取得して、number プロパティに渡された index をセットすれば series_questions に現在の選択状態が反映されることになります。

swift

1// varで宣言していないとその要素のプロパティも変更できないので注意 2var series_questions: [Question] = [ 3 Question(text: "質問 1"), 4 Question(text: "質問 2"), 5 ... 6] 7 8... 9 10func quiz2CellDidChangeCurrentQuizButtonIndex(_ cell: Quiz2Cell, index: Int) { 11 if let indexPath = self.tableView.indexPath(for: cell) { 12 self.series_questions[indexPath.row].number = index 13 } 14}

これで、回答完了ボタンの処理でも series_questions を参照するだけでそれぞれのアンケートのボタン位置が調べられます。

投稿2021/10/16 17:53

編集2021/10/17 07:05
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

tasmicsy

2021/10/16 20:57

とてもご丁寧で分かりやすく、大変勉強になる説明をしていただき本当にありがとうございます。 プロパティの頭文字を小文字にするという慣習からわかっておらず(どうして頭文字が小文字だったり大文字だったりするのだろうと思っていました。。やっと区別化してる意味が分かりました。。)、そのようなところからご丁寧にご説明いただきありがたいです。 回答者様のおっしゃる通りに修正しまして、無事に動くことができました。お恥ずかしながら一人で2日ほど悩んでいたのでこんな綺麗に解決するものなのか、、と感動しています。 ボタンを一つに配列でまとめてIndexに手区別できること、for文を用いたボタンのタイトル変更(修正前は、各ボタンに対してisSelectedを5ボタン分変更してました。)、didsetやdelegateの活用法などどれも大変参考になるご説明ばかりでした。func文も使いこなせていないなと反省しています。 改めまして、この度は親切ご丁寧なご回答をいただき、本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問