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

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

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

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

Q&A

解決済

1回答

690閲覧

swift3で、tableviewのセルのindex番号で複数の画面に遷移先を振り分け、値を渡したい。

torkia

総合スコア24

Swift

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

0グッド

1クリップ

投稿2017/10/22 09:12

編集2017/10/23 01:30

swift3で、tableviewのセルのindex番号で複数の画面に遷移先を振り分け、値を渡したい。

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

画面遷移時に値が渡せずエラーがでてしまいます。
エラーメッセージ
fatal error: unexpectedly found nil while unwrapping an Optional value

###該当のソースコード

swift3

1import UIKit 2 3//セルに表示するデータ 4let sectionTitle = ["4月"] 5let section0 = [("1week","1day"),("","2day"),("","3day"),("","4day"),("","5day"), 6 ("2week","6day"),("","7day"),("","8day"),("","9day"),("","10day"), 7 ("3week","11day"),("","12day"),("","33day"),("","14day"),("","15day"), 8 ("4week","16day"),("","17day"),("","18day"),("","19day"),("","20day"),] 9 10class TableViewController: UITableViewController { 11 12 var result:Int? 13 14 // MARK: - Table view data source 15 16 //セクションの個数 17 override func numberOfSections(in tableView: UITableView) -> Int { 18 return sectionTitle.count 19 } 20 21 //セクション内の行数 22 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 23 return section0.count 24 } 25 26 //セクションのタイトルを決める 27 override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 28 return sectionTitle[section] 29 } 30 31 //セルを作る 32 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 33 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 34 let cellData = section0[(indexPath as NSIndexPath).row] 35 cell.textLabel?.text = cellData.0 36 cell.detailTextLabel?.text = cellData.1 37 cell.tag = indexPath.row //※rowを保存 38 return cell 39 } 40 41 42 //セルがタップされた時の処理 43 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ 44 tableView.deselectRow(at: indexPath, animated: false) 45 print("タップされたセルのindex番号: (indexPath.row)") 46 result = indexPath.row 47 if result == 0 { 48 performSegue(withIdentifier: "segue1",sender: self) 49 }else if result == 1 { 50 performSegue(withIdentifier: "segue2",sender: self) 51 }else if result == 2 { 52 performSegue(withIdentifier: "segue3",sender: self) 53 } 54 } 55 56 57 //セグエで移動する前にデータを受け渡す 58 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 59 //セグエがsegue1のときの処理 60 if segue.identifier == "segue1" { 61 //セグエsegue1のとき実行する 62 if let indexPath = self.tableView.indexPathForSelectedRow { 63 //行のデータを取り出す 64 let cellData = section0[indexPath.row] 65 result = Int(indexPath.row) 66 //移動先のビューコントローラのdataプロパティに値を設定する 67 if let navi = segue.destination as? UINavigationController, 68 let fVC = navi.viewControllers.first as? ViewController { 69 fVC.data = cellData.1 70 fVC.received = result 71 } 72 } 73 print("prepare(result)") 74 } 75 //セグエがsegue2のときの処理 76 if segue.identifier == "segue2" { 77 //セグエsegue2のとき実行する 78 if let indexPath = self.tableView.indexPathForSelectedRow { 79 //行のデータを取り出す 80 let cellData = section0[indexPath.row] 81 result = Int(indexPath.row) 82 //移動先のビューコントローラのdataプロパティに値を設定する 83 if let navi = segue.destination as? UINavigationController, 84 let sVC = navi.viewControllers.first as? SecondViewController { 85 sVC.data = cellData.1 86 sVC.received = result 87 } 88 } 89 print("prepare(result)") 90 } 91 //セグエがsegue3のときの処理 92 if segue.identifier == "segue3" { 93 //セグエsegue3のとき実行する 94 if let indexPath = self.tableView.indexPathForSelectedRow { 95 //行のデータを取り出す 96 let cellData = section0[indexPath.row] 97 result = Int(indexPath.row) 98 //移動先のビューコントローラのdataプロパティに値を設定する 99 if let navi = segue.destination as? UINavigationController, 100 let tVC = navi.viewControllers.first as? ThirdViewController { 101 tVC.data = cellData.1 102 tVC.received = result 103 } 104 } 105 print("prepare(result)") 106 } 107 } 108 109 110 override func viewDidLoad() { 111 super.viewDidLoad() 112 113 } 114 115 override func didReceiveMemoryWarning() { 116 super.didReceiveMemoryWarning() 117 } 118} 119

###該当のソースコード

swift3

1import UIKit 2 3class ViewController: UIViewController { 4 5 @IBOutlet weak var myLabel: UILabel! 6 @IBOutlet weak var indexLabel: UILabel! 7 8 9 //シーン移動の際に設定されるデータ 10 var data:String? 11 var received:Int? 12 13 14 15 override func viewDidLoad() { 16 super.viewDidLoad() 17 18 //おためしラベル 19 myLabel.text = data 20 indexLabel.text = String(received!) 21 } 22 23 24 override func didReceiveMemoryWarning() { 25 super.didReceiveMemoryWarning() 26 // Dispose of any resources that can be recreated. 27 } 28}

###試したこと
上記のコードでViewControllerの方のindexLabel.text = String(received!)で[EXC_BAD_INSTRUCTION]とエラーがでてデバックエリアには[fatal error: unexpectedly found nil while unwrapping an Optional value]とでます。
myLabel.text = data のところは何も表示されません。
indexLabelのコードを消してRunするとエラーも出ず画面遷移はできるのですが、myLabelの表示がありません。prepare~のところが機能していないのでしょうか?
初心者なので汚いコードで申し訳ないですが、宜しくお願いします。

###補足情報(言語/FW/ツール等のバージョンなど)
より詳細な情報
tableviewはNavigationControllerの部品を使って既成しています。
イメージ説明

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

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

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

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

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

guest

回答1

0

ベストアンサー

実際には確認していませんが、

swift

1tableView.deselectRow(at: indexPath, animated: false)

を呼んでセルの選択を解除しているようなので、prepare(for:sender:)の中のself.tableView.indexPathForSelectedRowで選択中のセルを取得できていないのではないのでしょうか?

【追記】

swift

1if let navi = segue.destination as? UINavigationController, 2 //※この中に処理が来ていないのでは? 3 let fVC = navi.viewControllers.first as? ViewController { 4 fVC.data = cellData.1 5 fVC.received = result 6}

上記コメント位置に処理が来ていないようであれば、下記のように書き換えて試してみて下さい。

swift

1if let vc = segue.destination as? ViewController { 2 vc.data = cellData.1 3 vc.received = result 4}

投稿2017/10/23 01:06

編集2017/10/23 04:00
fuzzball

総合スコア16731

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

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

torkia

2017/10/23 01:42

追記、ご回答ありがとうごございます。 tableView.deselectRow(at: indexPath, animated: false)のところを消して実行してみたのですが、エラーがでてしまいました。
fuzzball

2017/10/23 01:50

同じ場所で同じエラーが出たのでしょうか?
torkia

2017/10/23 01:54

はい。同じ場所、同じエラーメッセージでした。
fuzzball

2017/10/23 02:02

if let indexPath = self.tableView.indexPathForSelectedRow { の下の行に print(Int(indexPath.row)) を追加して、表示されるかどうか、表示された場合は正しい値が表示されるかどうかチェックして下さい。 あと、var result:Int? は何のために存在しているのでしょうか?
torkia

2017/10/23 03:11

度々のご回答ありがとうございます。 print(Int(indexPath.row))を追加してみましたが、デバックエリアには表示がありませんでした。 print("prepare(result)")の位置での表示はあるのですが、その位置のprintだと反応がありません。 class宣言の下のvar result:Int?は、index番号を他の画面に送るために作った変数名で、これを消すとresultのところが全てエラーがでてしまいます。index番号を別の入れ物(result)に入れないで送れる方法が分からなかったのでそうしています。dataプロパティに配列の中身(cellData.1)が入るようにして、receivedプロパティにindex番号(result)が入るようにしたいのですが、上手くいきません。 ここ数日この値渡しができなくて試行錯誤していたのですが、cellからセグエを引っ張って接続した時は、画面遷移と値渡しはできたのですが、複数画面に遷移したく、tableviewのあるviewControllerからセグエを各viewにつないだところ、エラーだらけになってしまいました。 cellからセグエを接続したときはprepare~のところの if let navi = segue.destination as? UINavigationController, とはしなくても、そのまま遷移先のViewContorollerの名前を呼ぶので遷移できて値も渡せていたのですが、ViewContorollerから接続するとできなくなったので、NavigationContorollerを先に呼び出すようにしてみたのですが、値が渡せません。そもそもNavigationContorollerを先に呼ぶのが正しいのかも分かりません。prepare内の記述が間違っているのか、遷移先の記述(受取方法)が間違っているのかも分からない状態です。
torkia

2017/10/23 03:33

すいません。訂正です。 >print(Int(indexPath.row))を追加してみましたが、デバックエリアには表示がありませんでした。 の部分。 最初に指摘された tableView.deselectRow(at: indexPath, animated: false) のところを消してprintするとデバックエリアに正しいindex番号が表示されました。 ですが、同じエラーがでます。 ViewContorollerの indexLabel.text = String(received!) を消して実行するとエラーなしで画面遷移はできるのですが、myLabelの表示(data(配列の中身))がありませんでした。やはり値渡しができていないのでしょうか?
fuzzball

2017/10/23 04:00

tableView.deselectRow(at: indexPath, animated: false) を削除した状態で、回答に追記した内容を確認して下さい。
torkia

2017/10/23 05:20

追記、回答ありがとうございます。 できました。 大変助かりました。本当にありがとうございます。 if let navi = segue.destination as? UINavigationController, を消して、 if let vc = segue.destination as? ViewController { vc.data = cellData.1 vc.received = result } に直したら画面遷移と値渡しができるようになりました。 NavigationContorllerを使っている場合でもそのまま遷移先を呼び出す方法でよかったんですね。 私が失敗していたのは、通常の呼び方で、? を ! にしていたり、ifが抜けていたように思います。 色々な本やネット検索してつぎはぎしながら作ったコードなのでいまいち理解していないところがあり、教えて頂きたいのですが、 tableView.deselectRow(at: indexPath, animated: false) セルの選択解除はした方がいいと何かに書いてあったので記述したのですが、これは特にしなくてもいい(あまり意味のない)ことなのでしょうか? あと、NavigationContorollerを先に呼ばなくてはいけない場合があるのはどんな条件のときなのでしょうか?  素人過ぎる質問で大変申し訳ないですが教えて頂けるとありがたいです。
fuzzball

2017/10/23 05:29 編集

>>セルの選択解除はした方がいい 見た目の問題でしょうかね?放っておくと選択したのがずっと残るんでしたっけ? 消したいのであれば、if let indexPath = self.tableView.indexPathForSelectedRow { の中で消すとか。 そもそもsegueで分岐しているのですから、self.tableView.indexPathForSelectedRow でindexPathを拾う必要は無いと思うのですが‥。 >>NavigationContorollerを先に呼ばなくてはいけない場合 質問の意味が分かりません。
torkia

2017/10/23 06:03

ご回答ありがとうございます。 >>セルの選択解除はした方がいい は、見た目の問題もそうなんですが、確か、appstoreでの認証が通らないとかだったような・・・。 >>if let indexPath = self.tableView.indexPathForSelectedRow { の中で消すとか。 を試してみたらできました。 >>そもそもsegueで分岐しているのですから、self.tableView.indexPathForSelectedRow でindexPathを拾う必要は無いと思うのですが‥。 と、言いますとsegueの分岐をさせている override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ の中でindex番号も一緒に渡せたりできるのでしょうか? と、いいますのも、この画面遷移した後にindex番号によってcsvファイルから配列を呼び出そうと思い、index番号を値として渡したかったのですが、prepareで渡す方法以外のいい方法があれば是非知りたいです。配列の中身とindex番号を渡したかったのですが、どちらかというとindex番号さえ渡せればできそうなので。 色々と気づいた点を言って頂いて大変有り難いです。他にも注意点があれば教えて頂けると幸いです。
fuzzball

2017/10/23 06:20 編集

>>確か、appstoreでの認証が通らないとかだったような・・・。 それはたぶん下記URLの「テーブルが表示される際(viewWillAppear:)に、データのリロード、選択行の解除。」のことですね。 http://blog.kishikawakatsumi.com/entry/20090121/1232548786 リジェクトされるかどうかはおいておいて、今回のケースでは全く関係がありません。 当時のことは知りませんが、未だかつてこれが原因でリジェクトされたことは無いです。 >>と、言いますとsegueの分岐をさせている... そんな難しい話ではなくて、"segue1"ならindexPath.row=0、"segue2"ならindexPath.row=1、"segue3"ならindexPath.row=2、でしょう?
torkia

2017/10/23 08:18

ご回答ありがとうございます。 詳しい説明ありがとうございます。 >>そんな難しい話ではなくて〜 のところが意味が分からなくて、 if result == 0 { performSegue(withIdentifier: "segue1",sender: self) の result をindexPath.row == 0 { にすればsegueの分岐はできると言うことだと思い試したところ、画面遷移はできました。 ですが、prepareでのindexPathForSelectedRowでindexPathを拾う必要がないというところの意味がよく分かりませんでした。勉強不足ですいません。
fuzzball

2017/10/23 08:24 編集

tableView(_:didSelectRowAt:)で、 ・indexPath.row=0なら"segue1" ・indexPath.row=1なら"segue2" ・indexPath.row=2なら"segue3" を呼び出してますよね? ということは、その後で呼ばれるprepare(for:sender:)の中では、 if segue.identifier == "segue1" { //この中はindexPath.row = 0です } if segue.identifier == "segue2" { //この中はindexPath.row = 1です } if segue.identifier == "segue3" { //この中はindexPath.row = 2です } ということなのですが。
torkia

2017/10/23 09:47

度々ありがとうございます。 理解するのに時間がかかってしまいました。すいません。 なんとかできるようになりました。 多分ですが、 ご指摘頂きました部分のコードを下記の様に修正したところ、 画面遷移とindex番号が送れました。 //セルがタップされた時の処理 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ result = indexPath.row if indexPath.row == 0 { performSegue(withIdentifier: "segue1",sender: self) }else if indexPath.row == 1 { performSegue(withIdentifier: "segue2",sender: self) }else if indexPath.row == 2 { performSegue(withIdentifier: "segue3",sender: self) } } //セグエで移動する前にデータを受け渡す override func prepare(for segue: UIStoryboardSegue, sender: Any?) { //セグエがsegue1のときの処理 if segue.identifier == "segue1" { //移動先のビューコントローラのプロパティに値を設定する if let fVC = segue.destination as? ViewController { fVC.received = result } } } 大変長々とありがとうございました。
fuzzball

2017/10/23 10:07 編集

わざわざresultを介さなくても fVC.received = 0 と書けばいいんですよ。 これで var result:Int? が必要なくなるでしょ?
torkia

2017/10/23 10:32

はい。できました。 この方法で配列の中身も送れたりするのでしょうか? やはり、配列の中身を送る場合は prepare~ の中の if let indexPath = self.tableView.indexPathForSelectedRow { の下に配列の行を取り出すようにしなければ送れないのでしょうか?
fuzzball

2017/10/24 00:14

performSegue(withIdentifier:sender:) から prepare(for:sender:) に値(パラメータ)を渡すのに一番いい方法は、senderを使うことです。 performSegue(withIdentifier:sender:) のsenderに渡した値は prepare(for:sender:) のsenderで取得できます。senderはAny?型ですので、数値だろうが配列だろうが何でも送れます。(今回のコードも、senderを使えばもう少しシンプルに書けるのではないかと思います) ここでのコメントはこれで最後にしますので、調べたり試したりしても分からないことがあったら新たに質問して下さい。
torkia

2017/10/24 02:04

追加の質問にも丁寧にご説明頂き、ありがとうございました。 大変勉強になりました。 また宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問