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

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

ただいまの
回答率

90.99%

  • Swift

    6097questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 283

torkia

score 10

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

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

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

該当のソースコード

import UIKit

//セルに表示するデータ
let sectionTitle = ["4月"]
let section0 = [("1week","1day"),("","2day"),("","3day"),("","4day"),("","5day"),
                ("2week","6day"),("","7day"),("","8day"),("","9day"),("","10day"),
                ("3week","11day"),("","12day"),("","33day"),("","14day"),("","15day"),
                ("4week","16day"),("","17day"),("","18day"),("","19day"),("","20day"),]

class TableViewController: UITableViewController {

    var result:Int?

    // MARK: - Table view data source

    //セクションの個数
    override func numberOfSections(in tableView: UITableView) -> Int {
        return sectionTitle.count
    }

    //セクション内の行数
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return section0.count
    }

    //セクションのタイトルを決める
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sectionTitle[section]
    }

    //セルを作る
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let cellData = section0[(indexPath as NSIndexPath).row]
        cell.textLabel?.text = cellData.0
        cell.detailTextLabel?.text = cellData.1
        cell.tag = indexPath.row //※rowを保存
        return cell
    }


    //セルがタップされた時の処理
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
        tableView.deselectRow(at: indexPath, animated: false)
        print("タップされたセルのindex番号: \(indexPath.row)")
        result = indexPath.row
        if result == 0 {
            performSegue(withIdentifier: "segue1",sender: self)
        }else if result == 1 {
            performSegue(withIdentifier: "segue2",sender: self)
        }else if result == 2 {
            performSegue(withIdentifier: "segue3",sender: self)
        }  
    }


    //セグエで移動する前にデータを受け渡す
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //セグエがsegue1のときの処理
        if segue.identifier == "segue1" {
            //セグエsegue1のとき実行する
            if let indexPath = self.tableView.indexPathForSelectedRow {
                //行のデータを取り出す
                let cellData = section0[indexPath.row]
                result = Int(indexPath.row)
                //移動先のビューコントローラのdataプロパティに値を設定する
                if let navi = segue.destination as? UINavigationController,
                    let fVC = navi.viewControllers.first as? ViewController {
                    fVC.data = cellData.1
                    fVC.received = result
                }
            }
            print("prepare\(result)")
        }
        //セグエがsegue2のときの処理
        if segue.identifier == "segue2" {
            //セグエsegue2のとき実行する
            if let indexPath = self.tableView.indexPathForSelectedRow {
                //行のデータを取り出す
                let cellData = section0[indexPath.row]
                result = Int(indexPath.row)
                //移動先のビューコントローラのdataプロパティに値を設定する
                if let navi = segue.destination as? UINavigationController,
                    let sVC = navi.viewControllers.first as? SecondViewController {
                    sVC.data = cellData.1
                    sVC.received = result
                }
            }
            print("prepare\(result)")
        }
        //セグエがsegue3のときの処理
        if segue.identifier == "segue3" {
            //セグエsegue3のとき実行する
            if let indexPath = self.tableView.indexPathForSelectedRow {
                //行のデータを取り出す
                let cellData = section0[indexPath.row]
                result = Int(indexPath.row)
                //移動先のビューコントローラのdataプロパティに値を設定する
                if let navi = segue.destination as? UINavigationController,
                    let tVC = navi.viewControllers.first as? ThirdViewController {
                    tVC.data = cellData.1
                    tVC.received = result
                }
            }
            print("prepare\(result)")
        }
    }


    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

該当のソースコード

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet weak var indexLabel: UILabel!


    //シーン移動の際に設定されるデータ
    var data:String?
    var received:Int?



    override func viewDidLoad() {
        super.viewDidLoad()

        //おためしラベル
        myLabel.text = data
        indexLabel.text = String(received!)
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

試したこと

上記のコードで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の部品を使って既成しています。
イメージ説明

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

回答 1

checkベストアンサー

+1

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

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

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

【追記】

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/23 10:42

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

    キャンセル

  • 2017/10/23 10:50

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

    キャンセル

  • 2017/10/23 10:54

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

    キャンセル

  • 2017/10/23 11:02

    if let indexPath = self.tableView.indexPathForSelectedRow {
    の下の行に
    print(Int(indexPath.row))
    を追加して、表示されるかどうか、表示された場合は正しい値が表示されるかどうかチェックして下さい。

    あと、var result:Int? は何のために存在しているのでしょうか?

    キャンセル

  • 2017/10/23 12: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内の記述が間違っているのか、遷移先の記述(受取方法)が間違っているのかも分からない状態です。

    キャンセル

  • 2017/10/23 12:33

    すいません。訂正です。
    >print(Int(indexPath.row))を追加してみましたが、デバックエリアには表示がありませんでした。
    の部分。

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

    キャンセル

  • 2017/10/23 13:00

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

    キャンセル

  • 2017/10/23 14: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を先に呼ばなくてはいけない場合があるのはどんな条件のときなのでしょうか? 
    素人過ぎる質問で大変申し訳ないですが教えて頂けるとありがたいです。

    キャンセル

  • 2017/10/23 14:29 編集

    >>セルの選択解除はした方がいい
    見た目の問題でしょうかね?放っておくと選択したのがずっと残るんでしたっけ?
    消したいのであれば、if let indexPath = self.tableView.indexPathForSelectedRow { の中で消すとか。

    そもそもsegueで分岐しているのですから、self.tableView.indexPathForSelectedRow でindexPathを拾う必要は無いと思うのですが‥。

    >>NavigationContorollerを先に呼ばなくてはいけない場合
    質問の意味が分かりません。

    キャンセル

  • 2017/10/23 15: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番号さえ渡せればできそうなので。
    色々と気づいた点を言って頂いて大変有り難いです。他にも注意点があれば教えて頂けると幸いです。

    キャンセル

  • 2017/10/23 15:20 編集

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

    >>と、言いますとsegueの分岐をさせている...
    そんな難しい話ではなくて、"segue1"ならindexPath.row=0、"segue2"ならindexPath.row=1、"segue3"ならindexPath.row=2、でしょう?

    キャンセル

  • 2017/10/23 17:18

    ご回答ありがとうございます。

    詳しい説明ありがとうございます。

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

    キャンセル

  • 2017/10/23 17: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です
    }

    ということなのですが。

    キャンセル

  • 2017/10/23 18: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
    }
    }
    }

    大変長々とありがとうございました。

    キャンセル

  • 2017/10/23 19:07 編集

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

    キャンセル

  • 2017/10/23 19:32

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

    キャンセル

  • 2017/10/24 09:14

    performSegue(withIdentifier:sender:) から prepare(for:sender:) に値(パラメータ)を渡すのに一番いい方法は、senderを使うことです。

    performSegue(withIdentifier:sender:) のsenderに渡した値は prepare(for:sender:) のsenderで取得できます。senderはAny?型ですので、数値だろうが配列だろうが何でも送れます。(今回のコードも、senderを使えばもう少しシンプルに書けるのではないかと思います)

    ここでのコメントはこれで最後にしますので、調べたり試したりしても分からないことがあったら新たに質問して下さい。

    キャンセル

  • 2017/10/24 11:04

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

    キャンセル

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

  • ただいまの回答率 90.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Swift

    6097questions

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