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

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

新規登録して質問してみよう
ただいま回答率
85.48%
保存

保存(save)とは、特定のファイルを、ハードディスク等の外部記憶装置に記録する行為を指します。

Swift

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

Q&A

解決済

1回答

526閲覧

[swift 5] キーをユニークにして画像を保存

Kaguya_4869

総合スコア116

保存

保存(save)とは、特定のファイルを、ハードディスク等の外部記憶装置に記録する行為を指します。

Swift

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

0グッド

1クリップ

投稿2020/02/28 13:32

#質問したいこと
現在、写真付きのTodoアプリを作っています。
3ヶ月ほど前、[swift 5]写真を保存すると他の内容に違う写真が反映されてしまっていることで困っている。
にて、キーをユニークにすると保存できるということを教えてもらいました。
そこで、保存はできているようなのですが、保存されているのに保存されていないとエラーが出てきます。
#コード

swift

1//追加画面 2import UIKit 3import SwiftyTesseract 4 5var MemonoNakami = [String]() 6//var MemoImageNakami = [UIImage]() 7 8class AddViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 9 10 @IBOutlet weak var memoTextView: UITextView! 11 @IBOutlet weak var imageView: UIImageView! 12 13 14 let saveData: UserDefaults = UserDefaults.standard 15 16 //カメラボタンがタップされた時の処理 17 @IBAction func launchCamera(_ sender: UIButton) { 18 let camera = UIImagePickerController.SourceType.camera 19 if UIImagePickerController.isSourceTypeAvailable(camera) { 20 let picker = UIImagePickerController() 21 picker.sourceType = camera 22 picker.delegate = self 23 self.present(picker, animated: true) 24 } 25 imageView.isHidden = false 26 // imageButton.isHidden = true 27 } 28 29 //カメラロールから写真を選択する処理 30 @IBAction func choosePicture() { 31 // カメラロールが利用可能か? 32 if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { 33 // 写真を選ぶビュー 34 let pickerView = UIImagePickerController() 35 // 写真の選択元をカメラロールにする 36 // 「.camera」にすればカメラを起動できる 37 pickerView.sourceType = .photoLibrary 38 // デリゲート 39 pickerView.delegate = self 40 // ビューに表示 41 self.present(pickerView, animated: true) 42 } 43 } 44 45 46 47 //ユーザーが撮影し終わった時の処理 48 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 49 50 imageView.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage 51 // fatalError("Expected a dictionary containing an image, but was provided the following: (info)") 52 let image = imageView.image 53 let pngImageData:Data = image!.pngData()! 54 let documentsURL:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! 55 let fileURL:URL = documentsURL.appendingPathComponent("image.png") 56 do{ 57 try pngImageData.write(to: fileURL) 58 }catch{ 59 print("書き込み失敗") 60 } 61 dismiss(animated: true, completion: nil) 62 63 64 } 65 66 override func viewDidLoad() { 67 super.viewDidLoad() 68 69 // donebuttonの実装 70 let kbToolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 40)) 71 kbToolBar.barStyle = UIBarStyle.default // スタイルを設定 72 kbToolBar.sizeToFit() // 画面幅に合わせてサイズを変更 73 // スペーサー 74 let spacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil) 75 // 閉じるボタン 76 let commitButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(self.commitButtonTapped)) 77 kbToolBar.items = [spacer, commitButton] 78 memoTextView.inputAccessoryView = kbToolBar 79 80 //first image 81 imageView.image = UIImage(named: "No-Image.PNG") 82 83 } 84 85 @objc func commitButtonTapped() { 86 self.view.endEditing(true) 87 } 88 89 90 @IBAction func save(_ sender: Any) { 91 92 let inputText = memoTextView.text 93 let ud = UserDefaults.standard 94 95 // 新しいメモの構造体を生成 96 let memo = Memo(id: UUID().uuidString, content: inputText!) 97 98 // 一度Dataとして取り出し、そのあとにMemoの配列にデコード 99 if let data = UserDefaults.standard.data(forKey: "MemoImageKey"), 100 let storedMemoArray = try? JSONDecoder().decode([Memo].self, from: data) { 101 var newMemoArray = storedMemoArray 102 newMemoArray.append(memo) 103 104 // Dataに戻して保存 105 if let newData = try? JSONEncoder().encode(newMemoArray) { 106 UserDefaults.standard.set(newData, forKey:"MemoImageKey") 107 } 108 } else { 109 // 何も保存されていなかった時の処理 110 showAlert(title: "何も保存されていません。") 111 print(inputText) 112 print(imageView.image) 113 } 114 115 // ImagePickerから選択された画像を取り出し、Dataに変換 116 if let image = imageView.image, 117 let imageData = image.jpegData(compressionQuality: 0.8) { 118 // キーには上で生成したMemoのid(UUID)を使う 119 UserDefaults.standard.set(imageData, forKey: memo.id) 120 } 121 showAlert(title: "保存完了") 122 ud.synchronize() 123 } 124 125 func showAlert(title:String){ 126 let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) 127 128 alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) 129 130 alert.addAction(UIAlertAction(title: "キャンセル", style: .cancel, handler: nil)) 131 132 self.present(alert, animated: true, completion:nil) 133 } 134}

#コンソール
上記のコードの中にSaveボタンを押した時に何も保存されていなかった時の処理として

print(inputText) print(imageView.image)

とありますが、画像を追加して、メモをかき、保存ボタンを押すと、コンソールに

Optional("Flower") Optional(<UIImage:0x600001ff3720 anonymous {3000, 2002}>)

と表示されています。

どうしてか、よくわからないのでご教授願います。

よろしくお願い致します。

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

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

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

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

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

guest

回答1

0

ベストアンサー

保存はできているようなのですが、保存されているのに保存されていないとエラーが出てきます。

保存できていません。

少なくとも提示されているコードを見る限り、初回起動時にキー名が"MemoImageKey"のデータを作成していないため、毎回保存されることなく終わっています。

問題となっているときの処理をちょっと抽象的に書くと、

Swift

1 // 一度Dataとして取り出し、そのあとにMemoの配列にデコード 2 if let data = UserDefaults.standard.data(forKey: "MemoImageKey"), 3 let storedMemoArray = try? JSONDecoder().decode([Memo].self, from: data) { 4 5 // (1) 既に "MemoImageKey" が存在していて、 6 // かつデコードに成功したときの処理 7 8 } else { 9 10 // (2) 一度も "MemoImageKey" というキーでデータを保存していない 11 // もしくはデコードに失敗していたときの処理 12 13 }

という処理になるわけでです。

現状のコードだと全く初めて起動するときには、当然 "MemoImageKey"が存在しませんので、(2)の処理しか行われませんし、(2)の処理で新たにキーを作る処理も行っていませんから、その後も(2)の処理しか行われません。

したがって、(2)の部分で初回起動時のための処理を追加する必要があります。

たとえば、こんな感じになります。

Swift

1 if let data = UserDefaults.standard.data(forKey: "MemoImageKey"), 2 let storedMemoArray = try? JSONDecoder().decode([Memo].self, from: data) { 3 var newMemoArray = storedMemoArray 4 5 newMemoArray.append(memo) 6 7 // newMemoArray の構造をコンソールにわかりやすく表示 8 dump(newMemoArray) 9 10 // Dataに戻して保存 11 if let newData = try? JSONEncoder().encode(newMemoArray) { 12 UserDefaults.standard.set(newData, forKey:"MemoImageKey") 13 } 14 } else { 15 // 初回の処理 16 var newMemoArray = [Memo]() 17 18 newMemoArray.append(memo) 19 dump(newMemoArray) 20 21 if let newData = try? JSONEncoder().encode(newMemoArray) { 22 UserDefaults.standard.set(newData, forKey:"MemoImageKey") 23 } 24 showAlert(title: "初回保存しました。") 25 }

一応このコードで動きましたが、ひとつのオプショナルバインディング(if-let文)で二つの条件を判定していますから、その後の処理(たとえば、キーは存在したけど、JSONのデコードには失敗した場合など)はもうちょっと丁寧に記述した方がいいと思います。

投稿2020/02/28 16:43

TsukubaDepot

総合スコア5086

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

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

Kaguya_4869

2020/02/28 23:31

速やかな回答ありがとうございます。 Dataに戻して保存のところで、 Instance member 'encode' cannot be used on type 'JSONEncoder'; did you mean to use a value of this type instead? というエラーが出てきてしまいます。 どうすればいいのかご教授願います。
TsukubaDepot

2020/02/29 01:18

JSONEncoderのあとに丸括弧をつけ忘れていませんか? ちなみに、この辺りの処理はこれと同じです(インスタンスを作るか否かの違い、のはず)。 // Dataに戻して保存 let JSON = JSONEncoder() if let newData = try? JSON.encode(newMemoArray) { UserDefaults.standard.set(newData, forKey:"MemoImageKey") }
Kaguya_4869

2020/02/29 02:01

おっしゃる通りカッコをつけ忘れておりました! 解決しました!! 本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問