TableViewCell⇨UIViewControllerに画面遷移。
遷移先に「お気に入りボタン(★)」を配置。
タップで黄色⇄グレーに変化。
この内容をTableViewCellにも配置してる「お気に入りボタン(★)」に反映させたい。
Swift
Xcode12.3
ビルドはできますが、画面遷移時に下記のエラーメッセージになります。
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
試したこと
インスタンスを代入するコードを入力しましたが、うまく行きません。
(下記コード内に記載してます)
全コード記載します。
モデル
import Foundation struct Animal { private(set) public var nameJP : String private(set) public var imageName : String private(set) public var star : String init(nameJP: String, imageName: String,star: String) { self.nameJP = nameJP self.imageName = imageName self.star = star } }
import UIKit class ViewController: UIViewController { @IBOutlet weak var kensaku: UIButton! //タップでTableViewに画面遷移 override func viewDidLoad() { super.viewDidLoad() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } }
import UIKit @available(iOS 10.0, *) class ListViewController: UIViewController,UITableViewDelegate, UITableViewDataSource{ @IBOutlet weak var myTableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! var animals: [Animal] = [] var dupList: [Animal] = [] var detailViewController : DetailViewController! var animalTableViewCell : AnimalTableViewCell! override func viewDidLoad() { super.viewDidLoad() myTableView.dataSource = self myTableView.delegate = self searchBar.delegate = self loadData() animalTableViewCell.star.isHidden = true **//ViewControllerからの画面遷移時 ここでエラーメッセージ** } func loadData() { animals.append(Animal(nameJP: "犬", imageName: "dog", star: "staryellow")) animals.append(Animal(nameJP: "猫", imageName: "cat", star: "staryellow")) animals.append(Animal(nameJP: "猿", imageName: "monkey", star: "staryellow")) animals.append(Animal(nameJP: "鳥", imageName: "bird", star: "staryellow")) //以下多数続く self.dupList = self.animals } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.dupList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = myTableView.dequeueReusableCell(withIdentifier: "AnimalTableViewCell", for: indexPath) as? AnimalTableViewCell else { fatalError("Dequeue failed: AnimalTableViewCell.") } cell.animalNameJPLabel.text = self.dupList[indexPath.row].nameJP // cell.animalNameENLabel.text = self.dupList[indexPath.row].nameEN cell.animalImageView.image = UIImage(named: self.dupList[indexPath.row].imageName) cell.star.image = UIImage(named: self.dupList[indexPath.row].star) return cell } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetailSegue" { if let indexPath = myTableView.indexPathForSelectedRow { guard let destination = segue.destination as? DetailViewController else { fatalError("Failed to prepare DetailViewController.") detailViewController.animalTableViewCell = self.animalTableViewCell detailViewController.listViewController = self } destination.animal = dupList[indexPath.row] } } } override func viewWillAppear(_ animated: Bool) { if let indexPath = myTableView.indexPathForSelectedRow{ myTableView.deselectRow(at: indexPath, animated: true) } } } //searchBar の機能を拡張 @available(iOS 10.0, *) extension ListViewController: UISearchBarDelegate { func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.text = "" searchBar.endEditing(true) dupList = animals } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { print("search = (searchText)") dupList = searchText.isEmpty ? animals : animals.filter({ (Animal) -> Bool in return Animal.nameJP.range(of: searchText, options: .caseInsensitive, range: nil, locale: nil) != nil }) myTableView.reloadData() } }
import UIKit @available(iOS 10.0, *) class DetailViewController: UIViewController { var animalTableViewCell : AnimalTableViewCell! var listViewController : ListViewController! var animal: Animal! @IBOutlet weak var animalImageView: UIImageView! @IBOutlet weak var animalNameJPLabel: UILabel! @IBOutlet weak var okiniYellow: UIButton! @IBOutlet weak var okiniGray: UIButton! override func viewDidLoad() { super.viewDidLoad() okiniYellow.isHidden = true animalImageView.image = UIImage(named: animal.imageName) animalNameJPLabel.text = animal.nameJP } //お気に入り★マークをタップして黄色とグレーに切り替える。 //この内容をTableViewselの★マークにも反映させたい。 @IBAction func tapOkiniGray(_ sender: Any) { okiniGray.isHidden = true okiniYellow.isHidden = false animalTableViewCell.star.isHidden = false } @IBAction func tapOkiniYellow(_ sender: Any) { okiniGray.isHidden = false okiniYellow.isHidden = true animalTableViewCell.star.isHidden = true } }
import UIKit @available(iOS 10.0, *) class AnimalTableViewCell: UITableViewCell { var detailViewController : DetailViewController! var listViewController : ListViewController! @IBOutlet weak var animalNameJPLabel: UILabel! @IBOutlet weak var animalImageView: UIImageView! @IBOutlet weak var star: UIImageView! override func awakeFromNib() { super.awakeFromNib() listViewController.animalTableViewCell = self detailViewController.animalTableViewCell = self //これを入れてみましたが、うまく行きません。 } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } }
初めてTableViewを使ってますが、理解仕切れてないので、助けて頂けますでしょうか。
また、この内容でお気に入り★マークの内容がちゃんと「各セル別に内容反映」されるのか、
「セル別にユーザーデフォルツ保存」できるのか、合わせてご教授お願いいたします。
どの画面に遷移した時に、どこでエラーになってるかを書いて欲しいのですが、とりあえずどこかの @IBOutlet の接続を忘れてるのでは。
「class ListViewController」のコード内に記載してます。
「class ViewController」から「class ListViewController」に画面遷移した際、
「animalTableViewCell.star.isHidden = true」の部分でエラーになります。
宜しくお願いいたします。
ListViewController が animalTableViewCell というプロパティを持ってますが、そもそもそれがおかしいですね。UITableView の使い方をあまりよく理解されてないのでは…。
アプリの仕様を確認したいのですが、動物図鑑的なもので、動物のリストは固定 (ユーザーが動物を増やしたり減らしたりはしない) で、 UserDefaults に保存したいのは各動物がお気に入りかどうかの情報ってことでしょうか?
詳細の確認、ありがとうございます!動物は質問用に内容替えたものですが、仰る内容に近いものです。
リストは固定。ユーザーは増やせません。私の方で定期的に追加してバージョンUPはあり。
ユーザーがお気に入り⭐️チェックをしたものを、cellの部分でも⭐️表示して、cellとcellからの遷移先の両方で保存したい。という内容になります。宜しくお願いいたします。
1. loadData()の中のself.dupList = self.animalsは何のためでしょうか?
2. どうしてviewDidLoadの中でloadData()を呼ぶのでしょうか?viewDidLoadの役割を知っていますか?
3. loadData()の最後にmyTableViewをreloadDataしていないのでは?
代わりにお答えします。
1. dupList は検索 (絞り込み) 結果です。最初に animals を代入するのは、最初は全てを表示するためです。(変数名がいけてないというご指摘であれば、その通りだと思います。)
2. viewDidLoad の本来の目的は xib や storyboard から読み込んだ直後にビューを設定することです。以前 (iOS 5 あたりまで) は画面遷移に伴ってメモリの節約のためにビューを unload して load し直すということがありましたが、現在ではそれは廃止されたので、事実上 viewDidLoad はビューコントローラーの初期化のためにも使われています。(本来は init でやるべきでしょうけど、UIViewController の init は複数あるため、あまり定義したくないという理由もあります。)
3. loadData() を呼ぶのは viewDidLoad だけなので、reloadData の必要はありません。
回答1件
あなたの回答
tips
プレビュー