回答編集履歴

3

誤字訂正

2021/05/09 10:39

投稿

hoshi-takanori
hoshi-takanori

スコア7895

test CHANGED
@@ -308,7 +308,7 @@
308
308
 
309
309
  }
310
310
 
311
- print("loadStars: (stars)")
311
+ print("loadStars: (stars)") // デバッグ用、確認したら消す
312
312
 
313
313
 
314
314
 

2

誤字訂正

2021/05/09 10:39

投稿

hoshi-takanori
hoshi-takanori

スコア7895

test CHANGED
@@ -204,7 +204,7 @@
204
204
 
205
205
 
206
206
 
207
- 3 つも必要でしょうか? 保存したいのはお気に入りかどうかなのでanimal.star を保存すれば良くて、okiniGray や okiniYellow を保存するかは animal.star の値が決まれば自動的に決まります。
207
+ 3 つも必要でしょうか? 保存したいのはお気に入りかどうかなので animal.star を保存すれば充分で、okiniGray や okiniYellow の値は animal.star の値が決まれば自動的に決まります。
208
208
 
209
209
 
210
210
 
@@ -212,7 +212,7 @@
212
212
 
213
213
 
214
214
 
215
- また、これだと別の問題があって、どの動物をお気に入りにしたか区別つかないですよね。
215
+ また、これだと別の問題があって、どの動物をお気に入りにしたか区別つかないですよね。
216
216
 
217
217
 
218
218
 

1

追記

2021/05/09 10:35

投稿

hoshi-takanori
hoshi-takanori

スコア7895

test CHANGED
@@ -185,3 +185,227 @@
185
185
 
186
186
 
187
187
  とりあえずこれでお気に入りの処理はご希望通りになると思います。UserDefaults への保存は、ここまでの修正でちゃんと動くことを確認できてから追記しますので、コメント欄で結果をお知らせください。
188
+
189
+
190
+
191
+ ---
192
+
193
+
194
+
195
+ UserDefaults への保存ですが、まずどんな値をどのような形で保存するかを考える必要があります。
196
+
197
+
198
+
199
+ > UserDefaults.standard.set(okiniGray.isHidden, forKey: "saveGray")
200
+
201
+ > UserDefaults.standard.set(okiniYellow.isHidden, forKey: "saveYellow")
202
+
203
+ > UserDefaults.standard.set(animal.star, forKey: "saveCellStar")
204
+
205
+
206
+
207
+ 3 つも必要でしょうか? 保存したいのはお気に入りかどうかなので、animal.star を保存すれば良くて、okiniGray や okiniYellow を保存するかは animal.star の値が決まれば自動的に決まります。
208
+
209
+
210
+
211
+ > UserDefaults.standard.set(animal.star, forKey: "saveCellStar")
212
+
213
+
214
+
215
+ また、これだと別の問題があって、どの動物をお気に入りにしたかが区別つかないですよね。
216
+
217
+
218
+
219
+ というわけで、保存したいのは「各動物をお気に入りにしたかどうか」になります。もっと言うと、「犬をお気に入りにしたか」「猫をお気に入りにしたか」「猿をお気に入りにしたか」「鳥をお気に入りにしたか」…、をそれぞれ保存する必要があるということです。
220
+
221
+
222
+
223
+ 方法はいくつか考えられますが、直感的なのは辞書を使うことではないでしょうか。
224
+
225
+
226
+
227
+ ```swift
228
+
229
+ let stars: [String: Bool] = [
230
+
231
+ "犬": true,
232
+
233
+ "猫": false,
234
+
235
+ "猿": true,
236
+
237
+ "鳥": false,
238
+
239
+ ]
240
+
241
+ ```
242
+
243
+
244
+
245
+ もう一つの方法として、お気に入りの動物の配列を使うこともできます。この場合、配列に含まれないものはお気に入りではない、ということになります。
246
+
247
+
248
+
249
+ ```swift
250
+
251
+ let stars: [String] = ["犬", "猿"]
252
+
253
+ ```
254
+
255
+
256
+
257
+ 配列の方が簡単なので、配列を使うことにしましょう。まず保存のメソッドを ListViewController に書きます。一時的にお気に入りの動物の名前だけを集めた配列を作って、それを保存します。
258
+
259
+ (デバッグビルドの場合に synchronize してますが、これは Xcode からプログラムを停止させると UserDefaults が保存されないことがあるのを防ぐためです。)
260
+
261
+
262
+
263
+ ```swift
264
+
265
+ func saveStars() {
266
+
267
+ var stars = [String]()
268
+
269
+ for animal in animals {
270
+
271
+ if animal.star {
272
+
273
+ stars.append(animal.nameJP)
274
+
275
+ }
276
+
277
+ }
278
+
279
+ print("saveStars: (stars)") // デバッグ用、確認したら消す
280
+
281
+
282
+
283
+ UserDefaults.standard.set(stars, forKey: "stars")
284
+
285
+ #if DEBUG
286
+
287
+ UserDefaults.standard.synchronize()
288
+
289
+ #endif
290
+
291
+ }
292
+
293
+ ```
294
+
295
+
296
+
297
+ 次に読み込みです。これも ListViewController に書きます。お気に入りの配列を読み込んで、それに含まれる動物の star を true にします。(含まれない動物の star は初期値 false なので、そのままで良い。)
298
+
299
+
300
+
301
+ ```swift
302
+
303
+ func loadStars() {
304
+
305
+ guard let stars = UserDefaults.standard.array(forKey: "stars") as? [String] else {
306
+
307
+ return
308
+
309
+ }
310
+
311
+ print("loadStars: (stars)")
312
+
313
+
314
+
315
+ for animal in animals {
316
+
317
+ if stars.contains(animal.nameJP) {
318
+
319
+ animal.star = true
320
+
321
+ }
322
+
323
+ }
324
+
325
+ }
326
+
327
+ ```
328
+
329
+
330
+
331
+ これらを呼び出す方法ですが、loadStars は loadData で animals の配列を作り終わった後に一回だけ呼び出せば良いでしょう。
332
+
333
+
334
+
335
+ 問題は saveStars ですが、お気に入りボタンを押すたびに保存したいので、DetailViewController から ListViewController のメソッドを呼び出すことになります。このためには DetailViewController が ListViewController の参照を持つ必要がありますが、循環参照を防ぐために weak (弱参照) にする必要があります。(本来はプロトコルを定義して [delegate](https://qiita.com/chanNaru/items/326bd50a78cf34371169) にしたいところですが、今はまだいいでしょう。)
336
+
337
+
338
+
339
+ ```diff
340
+
341
+ class DetailViewController: UIViewController {
342
+
343
+
344
+
345
+ + weak var listViewController: ListViewController?
346
+
347
+
348
+
349
+ // 略
350
+
351
+ ```
352
+
353
+
354
+
355
+ 次に、画面遷移の際に listViewController を設定します。
356
+
357
+
358
+
359
+ ```diff
360
+
361
+ override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
362
+
363
+ if segue.identifier == "showDetailSegue" {
364
+
365
+ if let indexPath = myTableView.indexPathForSelectedRow {
366
+
367
+ let destination = segue.destination as! DetailViewController
368
+
369
+ + destination.listViewController = self
370
+
371
+ destination.animal = dupList[indexPath.row]
372
+
373
+ }
374
+
375
+ }
376
+
377
+ }
378
+
379
+ ```
380
+
381
+
382
+
383
+ 最後に、お気に入りボタンを押すたびに saveStars を呼び出せば完成です。
384
+
385
+
386
+
387
+ ```diff
388
+
389
+ @IBAction func tapOkiniGray(_ sender: Any) {
390
+
391
+ // 略
392
+
393
+ animal.star = true
394
+
395
+ + listViewController?.saveStars()
396
+
397
+ }
398
+
399
+
400
+
401
+ @IBAction func tapOkiniYellow(_ sender: Any) {
402
+
403
+ // 略
404
+
405
+ animal.star = false
406
+
407
+ + listViewController?.saveStars()
408
+
409
+ }
410
+
411
+ ```