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

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

ただいまの
回答率

87.58%

swiftで日記アプリを作成したい。画面遷移の値渡しや、日記の編集ができなくて困っています。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,244

score 12

◇環境
【Xcode】11.4【macOS】 Catalina 10.15.4【Swift】5.2【iOS】13.3.1

◇実現したいこと
tableviewを用いた日記アプリの作成

TableViewのセルをタップした時に、別画面に遷移させたい。(←できました)
遷移させる際にセルの情報を取得したい。(←できません)
編集した情報を上書きする(←見当がつきません)

◇実行したこと(参考にしたHP)
https://qiita.com/pe-ta/items/8f474358d1dd789557f3
このページを参考にしてuserdefaultを用いたテーブルを作成しました

https://teratail.com/questions/219454
このページを参考にしてcellをタップして画面遷移・値渡しのコードを作成しました

ストーリーボードの画像です

◇実行したコマンド、エラーログ

ここでCannot force unwrap value of non-optional type 'String'のエラーログが出てしまうのですが改善方法がわかりません。

   //画面遷移後に値渡しを行う
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let watasi:AddViewController = (segue.destination as? AddViewController)!
        watasi.watasitext = selectedtext!
    }

日記のリスト画面の制御コードです

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource  {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        nikkinaiyou.count
    }

    //セルの再利用とリストに表示するアクション
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let nikkicell = tableView.dequeueReusableCell(withIdentifier: "nikkicell", for: indexPath)
        nikkicell.textLabel!.text = nikkinaiyou[indexPath.row]
        return nikkicell
 }

    var selectedtext:String = ""

    //選んだセルの特定と画面遷移
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedtext = nikkinaiyou[indexPath.row]
        performSegue(withIdentifier: "edit", sender: nil)
       }

    //画面遷移後に値渡しを行う
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let watasi:AddViewController = (segue.destination as? AddViewController)!
        watasi.watasitext = selectedtext!
    }

    //画面が開かれたときに行われるアクション
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        if  UserDefaults.standard.object (forKey: "kagi") != nil{ nikkinaiyou = UserDefaults.standard.object (forKey: "kagi") as! [String]}
    }
}

追加編集画面の制御コードです

import UIKit
var nikkinaiyou = [String]()
class AddViewController: UIViewController {


    @IBOutlet weak var nikkiText: UITextField!

    //値を受け取る箱
    var watasitext:String = ""

    //追加ボタンを押したときのアクション
    @IBAction func addbutton(_ sender: Any) {
        nikkinaiyou.append(nikkiText.text!)
        UserDefaults.standard.set(nikkinaiyou, forKey: "kagi")
        }

    //画面を開いたときに起こるアクション
    override func viewDidLoad() {
        super.viewDidLoad()
        nikkiText.text = watasitext

    }
}

追加画像

イメージ説明

ご質問がありましたので追記します。

>矢印が多いのと、両画面ともmodalなのは意図してやってることですか?
遷移形式のことでしょうか。画像のようにshowで遷移させています。
![イメージ説明](8980db124a4f026e092cfbf44ce8291b.png)
>参考にされてるページも...ですが、参考にされてるページとだいぶ違っているように見えますよ。
参考にしたページだけの情報では意図したものが作れなかったので試行錯誤しました。

>3つのメソッド内で変数の値を調べて、コンソールに表示される文字を教えて下さい。
すみません、おっしゃっている意味がよくわからないです。

>そのあと、"あああ"と打ったらうまくいくという方法で同じことを行って、結果を教えて下さい。
値が遷移画面に表示されないコード

    //画面遷移後に値渡しを行う
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let watasi:AddViewController = (segue.destination as? AddViewController)!
        watasi.watasitext = selectedtext

    }


値が遷移画面に表示されるコード

    //画面遷移後に値渡しを行う
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let watasi:AddViewController = (segue.destination as? AddViewController)!
        watasi.watasitext = "あああ"

    }


イメージ説明

入力したコードです

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource  {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        nikkinaiyou.count
    }

    //セルの再利用とリストに表示するアクション
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let nikkicell = tableView.dequeueReusableCell(withIdentifier: "nikkicell", for: indexPath)
        nikkicell.textLabel!.text = nikkinaiyou[indexPath.row]
        return nikkicell
 }

    var selectedtext:String = ""

    //選んだセルの特定と画面遷移
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedtext = nikkinaiyou[indexPath.row]
        print("didSelectRowAt/selectedtext: \(selectedtext)")
        performSegue(withIdentifier: "edit", sender: nil)

       }

    //画面遷移後に値渡しを行う
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        print("didSelectRowAt/selectedtext: \(selectedtext)")
        let watasi:AddViewController = (segue.destination as? AddViewController)!
        watasi.watasitext = selectedtext

    }

    //画面が開かれたときに行われるアクション
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        if  UserDefaults.standard.object (forKey: "kagi") != nil{ nikkinaiyou = UserDefaults.standard.object (forKey: "kagi") as! [String]}
       print("didSelectRowAt/selectedtext: \(selectedtext)")
    }
}


tretを選んだときに出力されたものです。
イメージ説明
イメージ説明

すみません、これでどうでしょうか。
イメージ説明

segueの詳細です。
イメージ説明
イメージ説明

追記です
イメージ説明
・unsegueを使って「戻る」仕様にしました
・条件分岐をつけて2画面に分けました

いろいろ試行錯誤してみましたが「作成」はできても「編集」というのは難しいように感じています。
アプローチを変えてrealmを使おうかと考えています。他にいい案ありましたら教えて下さい。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

エラーが出てる部分の、watasi.watasitext = selectedtext!の最後の!を削除して実行したらどうなりますか?


追記

矢印が多いのと、両画面ともmodalなのは意図してやってることですか?
参考にされてるページも...ですが、参考にされてるページとだいぶ違っているように見えますよ。

3つのメソッド内で変数の値を調べて、コンソールに表示される文字を教えて下さい。
そのあと、"あああ"と打ったらうまくいくという方法で同じことを行って、結果を教えて下さい。

   //選んだセルの特定と画面遷移
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedtext = nikkinaiyou[indexPath.row]
        print("didSelectRowAt/selectedtext: \(selectedtext)"  // <= 追加して
        performSegue(withIdentifier: "edit", sender: nil)
    }

    //画面遷移後に値渡しを行う
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        print("prepare/selectedtext: \(selectedtext)" // <= 追加して
        let watasi:AddViewController = (segue.destination as? AddViewController)!
        watasi.watasitext = selectedtext!
    }

class AddViewController: UIViewController {

    //画面を開いたときに起こるアクション
    override func viewDidLoad() {
        super.viewDidLoad()
     print("AddViewController/watasitext: \(watasitext)" // <= 追加して
        nikkiText.text = watasitext

    }
}

追記

矢印1本でもできますが、どうしても2本でやりたいなら↓のようにしたら?
セグエの矢印には別々のidentifierを設定してください。
セルからVCに矢印を貼るとdidSelect内のコードが動く前に遷移します。

//選んだセルの特定と画面遷移
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let str: = nikkinaiyou[indexPath.row]
    // identifierが"edit"のセグエの矢印を実行する
    performSegue(withIdentifier: "edit", sender: str)
}

//画面遷移後に値渡しを行う
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "edit" {
     // identifierが"edit"のセグエの矢印を実行する場合のみ値を遷移先にわたす。
        let str: String? = sender as? String
        let vc = segue.destination as? AddViewController
        vc?. watasitext = str
    }
}

戻ってくる処理はいまのままだと無限ぐるぐるになってるので、unwindSegueで設定し直してください。
storyBoard上の戻ってくる矢印は不要です。

古いけどなんとかなるでしょ

また、遷移先で一つのボタンで編集と新規作成行う(戻る処理)ようですが、このままだとプログラムがどちらの処理なのかわからないので、そのへんを切り分ける処理も必要になります。

もう用意してあればいいですが。


追記

面倒なので、適当に作ったの置いときます。参考にでもしてみてください。

gitHub

これができないのに新しいものに手を出してなにかできるとは思えませんが…。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/04/09 21:46

    あー上の矢印は新規で使うのね、じゃあ、下を貼り直して、identifierをつけてあげて、
    現状セルをタップすると、2つとも動いてるから、片方だけうごくようにしてください

    キャンセル

  • 2020/04/10 10:17

    編集して回答しました。

    キャンセル

  • 2020/04/10 11:31

    ありがとうございます、意図したものです。この方法では不可能だと思っていました。upして頂いたものを読み解いて改めてまとめなおしたいと思います。

    キャンセル

0

◇実現したいこと
tableviewを用いた日記アプリの作成

TableViewのセルをタップした時に、別画面に遷移させたい。(←できました)
遷移させる際にセルの情報を取得したい。(←できました)
編集した情報を上書きする(←できました)

教えていただいたことを参考にして試行錯誤して実現することができました。
感謝の報告と備忘録を兼ねてまとめていきたいと思います。

動きはしますが動き方もおかしいところがありますので、指摘いただけると嬉しいです。

◇難しかったところ

・テーブル作成方法について
1.何行のテーブルか決める
2.セルに何が書かれるか決める

・ストーリーボードを利用しない画面遷移の方法について
1.ストーリーボードにidをつける
2.どの画面に遷移するか紐付ける
3.どのように遷移するか決める

・テーブルセルを用いた画面遷移について
1.どのセルを押されたらアクションをするか決める

一人で考えていたら全然できませんでした。質問に答えていただき感謝しています。

イメージ説明

ViewController

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
    var 日記を入れる箱:[String] = ["テスト","aaa"]

    var インデックスパスを返してもらう箱 = 0
    var どこから来たかを受け取る箱 = ""
    @IBAction func unwindToTop(segue: UIStoryboardSegue) {
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 日記を入れる箱.count
    }
    @IBOutlet weak var リストテーブル: UITableView!

    func tableView(_ リストテーブル: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let セル = リストテーブル.dequeueReusableCell(withIdentifier: "セル", for: indexPath)

        セル.textLabel?.text = 日記を入れる箱[indexPath.row]
        return セル
    }
    func tableView(_ リストテーブル: UITableView, didSelectRowAt indexPath: IndexPath) {
        let リストから編集へ = self.storyboard?.instantiateViewController(identifier: "HenshuuViewController") as! HenshuuViewController
        リストから編集へ.編集したい内容を受け取る箱 = 日記を入れる箱[indexPath.row]
        リストから編集へ.インデックスパスを受け取る箱 = indexPath.row
        self.present(リストから編集へ, animated: true, completion: nil)
    }

    @IBAction func 新規移動ボタン(_ sender: Any) {
        let リストから新規作成へ = self.storyboard?.instantiateViewController(identifier: "SinkiViewController") as! SinkiViewController
        self.present(リストから新規作成へ, animated: true, completion: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        日記を入れる箱 = UserDefaults.standard.array(forKey: "鍵") as! [String]


    }
    override func viewWillAppear(_ animated: Bool) {
           super.viewWillAppear(animated)


       }

}

HenshuuViewController

import UIKit

class HenshuuViewController: UIViewController {
    @IBOutlet weak var 編集する内容: UITextView!
    var 編集したい内容を受け取る箱:String = ""
    var 日記を入れる箱 = UserDefaults.standard.array(forKey: "鍵") as! [String]

    var インデックスパスを受け取る箱:Int = 0
    override func viewDidLoad() {

        super.viewDidLoad()
        編集する内容.text = 編集したい内容を受け取る箱

        // Do any additional setup after loading the view.
    }

    @IBAction func 編集ボタン(_ sender: Any) {
        let 編集からリストへ = self.storyboard?.instantiateViewController(identifier: "ViewController") as! ViewController
        日記を入れる箱[インデックスパスを受け取る箱] = 編集する内容.text
        UserDefaults.standard.set(日記を入れる箱, forKey: "鍵")
        self.present(編集からリストへ, animated: true, completion: nil)
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */

}

SinkiViewController

import UIKit

class SinkiViewController: UIViewController {


    @IBOutlet weak var 日記の内容: UITextView!

    @IBOutlet weak var 日付: UILabel!

    let date = Date()

    let df = DateFormatter()
    var 日記を入れる箱:[String] = []



    override func viewDidLoad() {
        super.viewDidLoad()
       df.dateFormat = "yyyy-MM-dd HH:mm:ss"
        日付.text = df.string(from: date)
        日記を入れる箱 = UserDefaults.standard.array(forKey: "鍵") as! [String]


    }

    @IBAction func 保存(_ sender: Any) {
        let 新規からリストへ = self.storyboard?.instantiateViewController(identifier: "ViewController") as! ViewController
        日記を入れる箱.insert(日記の内容.text, at: 0)
        UserDefaults.standard.set(日記を入れる箱, forKey: "鍵")
        print("追加画面\(日記を入れる箱)")
        self.present(新規からリストへ, animated: true, completion: nil)

    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */

}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/04/13 21:39

    presentで戻らないでね。それ戻ってないからね。
    https://qiita.com/akatsuki174/items/45d4bd7cb150defbf116
    それと、皆さんがどうだか知りませんが、私は、コメント部分以外に日本語が書いてあるコードは読めないようです。

    キャンセル

  • 2020/04/13 21:48

    すみません、英語苦手で。。また質問することあると思いますが今後ともよろしくおねがいします。

    キャンセル

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

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

関連した質問

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

  • トップ
  • Swiftに関する質問
  • swiftで日記アプリを作成したい。画面遷移の値渡しや、日記の編集ができなくて困っています。