#質問したいこと
いつもお世話になっています。
現在、カメラを取り入れたメモアプリを作成中です。
起動させた時に
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
というエラーが出てきます。
ただ一番最初の画面はViewControllerというところに作っていて、二つ目のファイルをsecondViewControllerというファイルで作っているのですが、エラーが出るのはなぜかsecondViewControllerの方です。
また落ちるところも2パターンあり、
①起動した時点で落ちる
②チュートリアル画面からホーム画面に行くところで落ちる
の2パターンあります。
その時エラー内容はどちらも
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
です。
#コード
viewcontroller
1 //ホーム画面 2import UIKit 3 4 5 6class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { 7 8 @IBOutlet weak var memoTableView: UITableView! 9 10// var memoArray = [String]() 11 12 let ud = UserDefaults.standard 13 14 let saveData: UserDefaults = UserDefaults.standard 15 16 var deleteNakami = [String]() 17 18 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 19 return MemonoNakami.count 20// return MemoImageNakami.count 21 } 22 23 24 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 25 let cell = tableView.dequeueReusableCell(withIdentifier: "memoCell", for: indexPath) 26 cell.textLabel?.text = MemonoNakami[indexPath.row] 27 return cell 28 } 29 30 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 31 self.performSegue(withIdentifier: "toDetail", sender: nil) 32 //押したら押した状態を解除 33 tableView.deselectRow(at: indexPath, animated: true) 34 } 35 36 37 override func viewDidLoad() { 38 super.viewDidLoad() 39 40 //追加画面で入力した内容を取得する 41 if UserDefaults.standard.object(forKey: "memoArray") != nil { 42 MemonoNakami = UserDefaults.standard.object(forKey: "memoArray") as! [String] 43 } 44 if UserDefaults.standard.object(forKey: "MemoImage") != nil{ 45// MemoImageNakami = UserDefaults.standard.object(forKey: "MemoImage") as! [UIImage] 46 MemoImageNakami = [ud.image(forKey: "MemoImage")] 47 48 } 49 memoTableView.delegate = self 50 memoTableView.dataSource = self 51 memoTableView.reloadData() 52 } 53 54 override func viewWillAppear(_ animated: Bool) { 55 loadMemo() 56 } 57 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 58 //destinationのクラッシュ防ぐ 59 if segue.identifier == "toDetail"{ 60 //detailViewControllerを取得 61 //as! DetailViewControllerでダウンキャストしている 62 let detailViewController = segue.destination as! DetailViewController 63 //遷移前に選ばれているCellが取得できる 64 let selectedIndexPath = memoTableView.indexPathForSelectedRow! 65 detailViewController.selectedMemo = MemonoNakami[selectedIndexPath.row] 66 detailViewController.selectedRow = selectedIndexPath.row 67 68 let selectedIndexPathImage = memoTableView.indexPathForSelectedRow! 69 detailViewController.selectedImageMemo = MemoImageNakami[selectedIndexPathImage.row] 70 print(MemoImageNakami.count) 71 } 72 } 73 func loadMemo(){ 74 if ud.array(forKey: "memoArray") != nil{ 75 //取得 またas!でアンラップしているのでnilじゃない時のみ 76 MemonoNakami = ud.array(forKey: "memoArray") as![String] 77 //reloadしてくれる 78 memoTableView.reloadData() 79 } 80 if ud.array(forKey: "MemoImage") != nil{ 81 82 MemonoNakami = ud.array(forKey: "memoArray") as! [String] 83 84 memoTableView.reloadData() 85 } 86 } 87 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 88 89 if editingStyle == .delete { 90// saveData.set(deleteNakami, forKey: "deleteMemo") 91 //resultArray内のindexPathのrow番目をremove(消去)する 92 MemonoNakami.remove(at: indexPath.row) 93 94 //再びアプリ内に消去した配列を保存 95 ud.set(MemonoNakami, forKey: "memoArray") 96 ud.set(MemonoNakami, forKey: "MemoImage") 97 98 //tableViewを更新 99 tableView.reloadData() 100 } 101 } 102}
secondViewcontroller
1import UIKit 2 3 4class secondViewcontroller: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 5 6 @IBOutlet weak var memoTextView: UITextView! 7 @IBOutlet weak var imageView: UIImageView! 8 9 var selectedRow: Int! 10 var selectedMemo : String! 11// var selectedImageRow: UIImage! 12 var selectedImageMemo: UIImage! 13 14 let saveData: UserDefaults = UserDefaults.standard 15 let ud = UserDefaults.standard 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 memoTextView.text = selectedMemo 20 21 MemoImageNakami = [ud.image(forKey: "MemoImage")] 22 imageView.image = selectedImageMemo 23 24 let kbToolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 40)) 25 kbToolBar.barStyle = UIBarStyle.default // スタイルを設定 26 kbToolBar.sizeToFit() // 画面幅に合わせてサイズを変更 27 // スペーサー 28 let spacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil) 29 // 閉じるボタン 30 let commitButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(self.commitButtonTapped)) 31 kbToolBar.items = [spacer, commitButton] 32 memoTextView.inputAccessoryView = kbToolBar 33 34 35 } 36 37 @objc func commitButtonTapped() { 38 self.view.endEditing(true) 39 } 40 41 //画面遷移する時にタップするボタン(保存) 42 @IBAction func save(_ sender: Any) { 43 44 let inputText = memoTextView.text 45 let ud = UserDefaults.standard 46 if ud.array(forKey: "memoArray") != nil{ 47 //saveMemoArrayに取得 48 var saveMemoArray = ud.array(forKey: "memoArray") as! [String] 49 50 //テキストに何か書かれているか? 51 if inputText != ""{ 52 //配列に追加 53 saveMemoArray[selectedRow] = inputText! 54 ud.set(saveMemoArray, forKey: "memoArray") 55 }else{ 56 showAlert(title: "何も入力されていません") 57 58 } 59 60 }else{ 61 //最初、何も書かれていない場合 62 var newMemoArray = [String]() 63 //nilを強制アンラップはエラーが出るから 64 if inputText != ""{ 65 //inputtextはoptional型だから強制アンラップ 66 newMemoArray.append(inputText!) 67 ud.set(newMemoArray, forKey: "memoArray") 68 }else{ 69 showAlert(title: "何も入力されていません") 70 } 71 } 72 73 showAlert(title: "保存完了") 74 ud.synchronize() 75 } 76 77 func showAlert(title:String){ 78 let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) 79 80 alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 81 82 alert.addAction(UIAlertAction(title: "キャンセル", style: .cancel, handler: nil)) 83 84 self.present(alert, animated: true, completion:nil) 85 } 86 //削除ボタン 87 @IBAction func deleteMemo(_ sender: Any) { 88 let ud = UserDefaults.standard 89 if ud.array(forKey: "memoArray") != nil{ 90 var saveMemoArray = ud.array(forKey: "memoArray") as![String] 91 saveMemoArray.remove(at: selectedRow) 92 ud.set(saveMemoArray, forKey: "memoArray" ) 93 ud.synchronize() 94 //画面遷移 95 self.navigationController?.popViewController(animated: true) 96 } 97 } 98 99 100 101 @IBAction func showActivityView(_ sender: UIBarButtonItem) { 102 let controller = UIActivityViewController(activityItems: [imageView.image!, memoTextView.text!], applicationActivities: nil) 103 self.present(controller, animated: true, completion: nil) 104 105 } 106 107} 108 109extension UserDefaults { 110 111 // 保存したいUIImage, 保存するUserDefaults, Keyを取得 112 func setUIImageToData(image: UIImage, forKey: String) { 113 // UIImageをData型へ変換 114 let nsdata = image.pngData() 115 // UserDefaultsへ保存 116 self.set(nsdata, forKey: "MemoImage") 117 } 118 119 // 参照するUserDefaults, Keyを取得, UIImageを返す 120 func image(forKey: String) -> UIImage { 121 // UserDefaultsからKeyを基にData型を参照 122 let data = self.data(forKey: "MemoImage") 123 // UIImage型へ変換 124 let returnImage = UIImage(data: data!) //ここでエラー発生!!!!!!!!! 125 // UIImageを返す 126 return returnImage! 127 } 128 129}
#やってみたこと
どこかの値がnilなのかなと思っているのですが見当たらず、接続も見直してみましたが正常でした。
#参考サイト
UIImageをUserDefaultsに保存するためのextensionを作った
#追記
Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/Kaguya_4869/Desktop/MemoApp/MemoApp/secondViewcontroller.swift, line 452
上記のコードだと、
swift
1// UIImage型へ変換 2 let returnImage = UIImage(data: data!)
の部分です(secondViewcontrollerのextensionの中です。)
よろしくお願いします。
カメラで撮影した画像をそのままのサイズで UserDefaults に保存してるのでしょうか? そういう非常識なことはマジでやめて欲しいです。
まだプログラミング初心者で、データの保存はUserDefaultsでしかやったことがありませんでした。
これが非常識とは知らず、使ってしまっていました。
本当に申し訳ありません。
UserDefaultsでもできるかなと思ったのですが…
他のサイトではいいものがあまりなく、どうすればいいのか迷っています。
具体的にどうすればいいのかご教授願えますでしょうか?
ちょっと言い過ぎましたね。別に怒ってるわけではないので、謝らなくても大丈夫です。
UserDefaults は確かに手軽に使えて便利なんですが、本来は最後に開いていた画面はどれだったかとか、ちょっとした設定内容なんかを覚えておくためのものです。
「カメラを取り入れたメモアプリ」とのことで、画像は個別のファイルで、メモはなんらかのデータベース的なものに保存するのが一般的だと思いますが、初心者にはちょっと大変かもしれませんね…。
コンソールにでているエラーメッセージが、どこの値がnilなのかを知る大事な手がかりです。それを示していただけますか。
@hoshi-takanori
丁寧な回答ありがとうございます。
メモをデータベースに保存して、画像は個別に保存する方法試してみたいと思います。
@eytyet
追記しておきました。
よろしくお願いします。
回答1件
あなたの回答
tips
プレビュー