teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

誤字訂正

2021/05/09 10:39

投稿

hoshi-takanori
hoshi-takanori

スコア7903

answer CHANGED
@@ -153,7 +153,7 @@
153
153
  guard let stars = UserDefaults.standard.array(forKey: "stars") as? [String] else {
154
154
  return
155
155
  }
156
- print("loadStars: (stars)")
156
+ print("loadStars: (stars)") // デバッグ用、確認したら消す
157
157
 
158
158
  for animal in animals {
159
159
  if stars.contains(animal.nameJP) {

2

誤字訂正

2021/05/09 10:39

投稿

hoshi-takanori
hoshi-takanori

スコア7903

answer CHANGED
@@ -101,11 +101,11 @@
101
101
  > UserDefaults.standard.set(okiniYellow.isHidden, forKey: "saveYellow")
102
102
  > UserDefaults.standard.set(animal.star, forKey: "saveCellStar")
103
103
 
104
- 3 つも必要でしょうか? 保存したいのはお気に入りかどうかなのでanimal.star を保存すれば良くて、okiniGray や okiniYellow を保存するかは animal.star の値が決まれば自動的に決まります。
104
+ 3 つも必要でしょうか? 保存したいのはお気に入りかどうかなので animal.star を保存すれば充分で、okiniGray や okiniYellow の値は animal.star の値が決まれば自動的に決まります。
105
105
 
106
106
  > UserDefaults.standard.set(animal.star, forKey: "saveCellStar")
107
107
 
108
- また、これだと別の問題があって、どの動物をお気に入りにしたか区別つかないですよね。
108
+ また、これだと別の問題があって、どの動物をお気に入りにしたか区別つかないですよね。
109
109
 
110
110
  というわけで、保存したいのは「各動物をお気に入りにしたかどうか」になります。もっと言うと、「犬をお気に入りにしたか」「猫をお気に入りにしたか」「猿をお気に入りにしたか」「鳥をお気に入りにしたか」…、をそれぞれ保存する必要があるということです。
111
111
 

1

追記

2021/05/09 10:35

投稿

hoshi-takanori
hoshi-takanori

スコア7903

answer CHANGED
@@ -91,4 +91,116 @@
91
91
  }
92
92
  ```
93
93
 
94
- とりあえずこれでお気に入りの処理はご希望通りになると思います。UserDefaults への保存は、ここまでの修正でちゃんと動くことを確認できてから追記しますので、コメント欄で結果をお知らせください。
94
+ とりあえずこれでお気に入りの処理はご希望通りになると思います。UserDefaults への保存は、ここまでの修正でちゃんと動くことを確認できてから追記しますので、コメント欄で結果をお知らせください。
95
+
96
+ ---
97
+
98
+ UserDefaults への保存ですが、まずどんな値をどのような形で保存するかを考える必要があります。
99
+
100
+ > UserDefaults.standard.set(okiniGray.isHidden, forKey: "saveGray")
101
+ > UserDefaults.standard.set(okiniYellow.isHidden, forKey: "saveYellow")
102
+ > UserDefaults.standard.set(animal.star, forKey: "saveCellStar")
103
+
104
+ 3 つも必要でしょうか? 保存したいのはお気に入りかどうかなので、animal.star を保存すれば良くて、okiniGray や okiniYellow を保存するかは animal.star の値が決まれば自動的に決まります。
105
+
106
+ > UserDefaults.standard.set(animal.star, forKey: "saveCellStar")
107
+
108
+ また、これだと別の問題があって、どの動物をお気に入りにしたかが区別つかないですよね。
109
+
110
+ というわけで、保存したいのは「各動物をお気に入りにしたかどうか」になります。もっと言うと、「犬をお気に入りにしたか」「猫をお気に入りにしたか」「猿をお気に入りにしたか」「鳥をお気に入りにしたか」…、をそれぞれ保存する必要があるということです。
111
+
112
+ 方法はいくつか考えられますが、直感的なのは辞書を使うことではないでしょうか。
113
+
114
+ ```swift
115
+ let stars: [String: Bool] = [
116
+ "犬": true,
117
+ "猫": false,
118
+ "猿": true,
119
+ "鳥": false,
120
+ ]
121
+ ```
122
+
123
+ もう一つの方法として、お気に入りの動物の配列を使うこともできます。この場合、配列に含まれないものはお気に入りではない、ということになります。
124
+
125
+ ```swift
126
+ let stars: [String] = ["犬", "猿"]
127
+ ```
128
+
129
+ 配列の方が簡単なので、配列を使うことにしましょう。まず保存のメソッドを ListViewController に書きます。一時的にお気に入りの動物の名前だけを集めた配列を作って、それを保存します。
130
+ (デバッグビルドの場合に synchronize してますが、これは Xcode からプログラムを停止させると UserDefaults が保存されないことがあるのを防ぐためです。)
131
+
132
+ ```swift
133
+ func saveStars() {
134
+ var stars = [String]()
135
+ for animal in animals {
136
+ if animal.star {
137
+ stars.append(animal.nameJP)
138
+ }
139
+ }
140
+ print("saveStars: (stars)") // デバッグ用、確認したら消す
141
+
142
+ UserDefaults.standard.set(stars, forKey: "stars")
143
+ #if DEBUG
144
+ UserDefaults.standard.synchronize()
145
+ #endif
146
+ }
147
+ ```
148
+
149
+ 次に読み込みです。これも ListViewController に書きます。お気に入りの配列を読み込んで、それに含まれる動物の star を true にします。(含まれない動物の star は初期値 false なので、そのままで良い。)
150
+
151
+ ```swift
152
+ func loadStars() {
153
+ guard let stars = UserDefaults.standard.array(forKey: "stars") as? [String] else {
154
+ return
155
+ }
156
+ print("loadStars: (stars)")
157
+
158
+ for animal in animals {
159
+ if stars.contains(animal.nameJP) {
160
+ animal.star = true
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ これらを呼び出す方法ですが、loadStars は loadData で animals の配列を作り終わった後に一回だけ呼び出せば良いでしょう。
167
+
168
+ 問題は saveStars ですが、お気に入りボタンを押すたびに保存したいので、DetailViewController から ListViewController のメソッドを呼び出すことになります。このためには DetailViewController が ListViewController の参照を持つ必要がありますが、循環参照を防ぐために weak (弱参照) にする必要があります。(本来はプロトコルを定義して [delegate](https://qiita.com/chanNaru/items/326bd50a78cf34371169) にしたいところですが、今はまだいいでしょう。)
169
+
170
+ ```diff
171
+ class DetailViewController: UIViewController {
172
+
173
+ + weak var listViewController: ListViewController?
174
+
175
+ // 略
176
+ ```
177
+
178
+ 次に、画面遷移の際に listViewController を設定します。
179
+
180
+ ```diff
181
+ override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
182
+ if segue.identifier == "showDetailSegue" {
183
+ if let indexPath = myTableView.indexPathForSelectedRow {
184
+ let destination = segue.destination as! DetailViewController
185
+ + destination.listViewController = self
186
+ destination.animal = dupList[indexPath.row]
187
+ }
188
+ }
189
+ }
190
+ ```
191
+
192
+ 最後に、お気に入りボタンを押すたびに saveStars を呼び出せば完成です。
193
+
194
+ ```diff
195
+ @IBAction func tapOkiniGray(_ sender: Any) {
196
+ // 略
197
+ animal.star = true
198
+ + listViewController?.saveStars()
199
+ }
200
+
201
+ @IBAction func tapOkiniYellow(_ sender: Any) {
202
+ // 略
203
+ animal.star = false
204
+ + listViewController?.saveStars()
205
+ }
206
+ ```