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

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

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

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

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

Q&A

解決済

1回答

1378閲覧

[SWIFT]設定画面からカスタムセルの色を変更したい。

kee4234

総合スコア23

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

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

0グッド

0クリップ

投稿2020/09/03 06:25

編集2020/09/03 07:47

前提・実現したいこと

設定画面からアラートをクリックしてユーザーデフォルトに値を保存しつつカスタムセルのViewの色を変更したい。

発生している問題・エラーメッセージ

UIView Unexpectedly found nil while implicitly unwrapping an Optional value

カスタムセルのソースコード(必要部分抜粋)

SWIFT

1import UIKit 2 3class StickyCell: UITableViewCell { 4 5 @IBOutlet weak var stickyBubble: UIView! 6 @IBOutlet weak var label: UILabel! 7 8 let defaults = UserDefaults.standard 9 10 override func awakeFromNib() { 11 super.awakeFromNib() 12 13 let cellColor = defaults.color(forKey: "CellColor") 14 15 if (cellColor != nil){ 16 stickyBubble.backgroundColor = cellColor 17 } 18 } 19}

メイン画面のソースコード

SWIFT

1import UIKit 2import CoreData 3import GrowingTextView 4 5class ViewController: UIViewController,UITableViewDelegate,UIGestureRecognizerDelegate { 6 7 @IBOutlet weak var inputToolbar: UIView! 8 @IBOutlet weak var tableView: UITableView! 9 @IBOutlet weak var textView: GrowingTextView! 10 @IBOutlet weak var textViewBottomConstraint: NSLayoutConstraint! 11 12 var itemArray = [Item]() 13 let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext 14 15 let defaults = UserDefaults.standard 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 20 // *** Customize GrowingTextView *** 21 textView.layer.cornerRadius = 4.0 22 tableView.contentInset = UIEdgeInsets(top: 15,left: 0,bottom: 15,right: 0) 23 24 print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)) 25 26 textView.delegate = self 27 tableView.dataSource = self 28 tableView.delegate = self 29 30 tableView.register(UINib(nibName: "StickyCell", bundle: nil), forCellReuseIdentifier: "ReusableCell") 31 32 loadItems() 33 34 // ロングプレス 35 let longPressGesture = 36 UILongPressGestureRecognizer(target: self, 37 action: #selector(ViewController.longPress(_:))) 38 39 longPressGesture.delegate = self 40 self.view.addGestureRecognizer(longPressGesture) 41 } 42 43 override func viewWillAppear(_ animated: Bool) { 44 super.viewWillAppear(animated) 45 46 47 } 48 49 // Long Press イベント 50 @objc func longPress(_ sender: UILongPressGestureRecognizer){ 51 let point = sender.location(in: tableView) 52 let indexPath = tableView.indexPathForRow(at: point) 53 if sender.state == .began { 54 let alert: UIAlertController = UIAlertController(title: "メッセージの削除", message: "削除してもいいですか?", preferredStyle: UIAlertController.Style.alert) 55 56 let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler:{ 57 (action: UIAlertAction!) -> Void in 58 print(indexPath!.row) 59 self.context.delete(self.itemArray[indexPath!.row]) 60 self.itemArray.remove(at: indexPath!.row) 61 62 self.saveItems() 63 }) 64 65 let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel, handler:{ 66 (action: UIAlertAction!) -> Void in 67 print("Cancel") 68 }) 69 70 alert.addAction(cancelAction) 71 alert.addAction(defaultAction) 72 73 present(alert, animated: true, completion: nil) 74 } 75 } 76 77 @IBAction func addButton(_ sender: UIButton) { 78 loadMessages() 79 } 80 81 func textFieldShouldReturn(_ textView: UITextField) -> Bool { 82 loadMessages() 83 84 return false 85 } 86 87 88 func loadMessages() { 89 if textView.text == "" { 90 print("error") 91 } else { 92 93 let newItem = Item(context: context) 94 newItem.text = textView.text! 95 96 itemArray.append(newItem) 97 98 textView.resignFirstResponder() 99 100 saveItems() 101 } 102 DispatchQueue.main.async { 103 self.textView.text = "" 104 let indexPath = IndexPath(row: self.itemArray.count - 1, section: 0) 105 self.tableView.scrollToRow(at: indexPath, at: .top, animated: true) 106 } 107 } 108 109 func saveItems() { 110 111 do{ 112 try context.save() 113 } catch { 114 print("Error saving context (error)") 115 } 116 117 tableView.reloadData() 118 119 } 120 121 func loadItems() { 122 let request : NSFetchRequest<Item> = Item.fetchRequest() 123 do { 124 itemArray = try context.fetch(request) 125 } catch { 126 print("Error fetching data from context (error)") 127 } 128 } 129 130 @IBAction func settingButtonAction(_ sender: UIBarButtonItem) { 131 performSegue(withIdentifier: "goSetting", sender: nil) 132 } 133 134 func cellUpdate(){ 135 136 itemArray.removeAll() 137 print("remove") 138 139 loadItems() 140 141 DispatchQueue.main.async { 142 self.tableView.reloadData() 143 } 144 145 } 146 147} 148 149extension ViewController: UITableViewDataSource{ 150 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 151 return itemArray.count 152 } 153 154 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 155 let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! StickyCell 156 cell.label.text = itemArray[indexPath.row].text 157 return cell 158 } 159} 160 161extension UserDefaults { 162 163 func color(forKey key: String) -> UIColor? { 164 165 guard let colorData = data(forKey: key) else { return nil } 166 167 do { 168 return try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorData) 169 } catch let error { 170 print("color error (error.localizedDescription)") 171 return nil 172 } 173 174 } 175 176 func set(_ value: UIColor?, forKey key: String) { 177 178 guard let color = value else { return } 179 do { 180 let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) 181 set(data, forKey: key) 182 } catch let error { 183 print("error color key data not saved (error.localizedDescription)") 184 } 185 186 } 187 188} 189 190extension ViewController: GrowingTextViewDelegate { 191 192 // *** Call layoutIfNeeded on superview for animation when changing height *** 193 194 func textViewDidChangeHeight(_ textView: GrowingTextView, height: CGFloat) { 195 UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [.curveLinear], animations: { () -> Void in 196 self.view.layoutIfNeeded() 197 }, completion: nil) 198 } 199} 200

設定画面のソースコード(必要部分抜粋)

SWIFT

1import UIKit 2 3class SettingTableViewController: UITableViewController { 4 5 let defaults = UserDefaults.standard 6 7 override func viewDidLoad() { 8 super.viewDidLoad() 9 } 10 11 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 12 let alert: UIAlertController = UIAlertController(title: "セルカラー", message: "", preferredStyle: UIAlertController.Style.alert) 13 14 let defaultAction_1: UIAlertAction = UIAlertAction(title: "標準", style: UIAlertAction.Style.default, handler:{ 15 (action: UIAlertAction!) -> Void in 16 self.defaults.set(originalGreen, forKey: "CellColor") 17 let cellColor = self.defaults.color(forKey: "CellColor") 18 19 let stickyCell = StickyCell() 20 stickyCell.stickyBubble.backgroundColor = cellColor 21 22 }) 23 24 alert.addAction(defaultAction_1) 25 26 present(alert, animated: true, completion: nil) 27 } 28}

問題となっているコード

SWIFT

1let stickyCell = StickyCell() 2stickyCell.stickyBubble.backgroundColor = cellColor

試したこと(設定画面アラートクリックアクション)

アラートクリックで色を変更しようとしましたが上記のエラーでクラッシュしてしまいます。
カスタムセル上でstickyCell.stickyBubble.backgroundColorをプリントした際はnilではない。

アプリを再起動した際にはセルの色が変更されているのでユーザーデフォルトへの保存までは成功しております。

ユーザーデファルトに値は保存されているのでメインの画面に戻りセルをリロードして色を変更しようと試しましたが新しいセルを追加した際は色が変更されるが既存のセルは色が変更されておらずうまくいきませんでした。

補足情報

SWIFT初心者でいたらないこともあると思いますが何卒よろしくお願い申し上げます。

実現したいイメージ

最初の画面
イメージ説明

設定画面
イメージ説明

セルカラーをタップ後カラー変更アラート表示
イメージ説明

カラー変更アラートのカラーを選択でセルの色を変更
イメージ説明

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

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

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

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

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

guest

回答1

0

ベストアンサー

Swift

1 let stickyCell = StickyCell() 2 stickyCell.stickyBubble.backgroundColor = cellColor

ここで UITableViewCell のインスタンスを作成していますが、class StickyCell: UITableViewCell の内部で

Swift

1 @IBOutlet weak var stickyBubble: UIView! 2 @IBOutlet weak var label: UILabel!

と、styckyBubble@IBOutlet 、つまり StoryBoard 経由でインスタンス化した時に値が入るようになっています。

したがって、直接インスタンス化したのであれば、当然これらの値は nil となっていまいますので、別の方法を考える必要があります。

最終的に実現したいことがいまいち把握できていないのですが、セルをタップしてアラートを出した後、最終的にどのような状態にしたいのかもう少し明確にしていただければ、解決法が出るかもしれません。

##修正案(追記)

awakeFromNib()でセル背景の色を設定しても、それは xib を登録する時にしか呼び出されないと思いますので、tableView でセルを生成する時に色を変更してはどうでしょうか。

条件には書かれていませんが、スクリーンショットから、画面遷移は Navigation Controller で行っていると仮定します。

たとえば、

Swift

1// StickyCell.swift 2import UIKit 3 4class StickyCell: UITableViewCell { 5 @IBOutlet weak var stickyBubble: UIView! 6 @IBOutlet weak var label: UILabel! 7 8 override func awakeFromNib() { 9 super.awakeFromNib() 10 } 11 12 override func setSelected(_ selected: Bool, animated: Bool) { 13 super.setSelected(selected, animated: animated) 14 } 15}

とし、

Swift

1// SettingTableViewController.swift 2import UIKit 3 4class SettingTableViewController: UITableViewController { 5 6 let defaults = UserDefaults.standard 7 8 override func viewDidLoad() { 9 super.viewDidLoad() 10 } 11 12 // 中略 13 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 14 let alert: UIAlertController = UIAlertController(title: "セルカラー", message: "", preferredStyle: .actionSheet) 15 16 let defaultAction1: UIAlertAction = UIAlertAction(title: "オレンジ", style: UIAlertAction.Style.default, handler:{ 17 (action: UIAlertAction!) -> Void in 18 self.defaults.set(.orange, forKey: "CellColor") 19 }) 20 21 let defaultAction2: UIAlertAction = UIAlertAction(title: "グリーン", style: UIAlertAction.Style.default, handler:{ 22 (action: UIAlertAction!) -> Void in 23 self.defaults.set(.green, forKey: "CellColor") 24 }) 25 26 alert.addAction(defaultAction1) 27 alert.addAction(defaultAction2) 28 29 present(alert, animated: true, completion: nil) 30 } 31 // 後略 32}

のように、UIAlert で色変更する部分も UserDefaults に保存するだけにします。

ViewController では、

Swift

1// ViewController.swift 2import UIKit 3import GrowingTextView 4 5class ViewController: UIViewController, UITableViewDelegate { 6 // 中略 7 // セル背景色 8 var cellColor: UIColor? 9 10 // 追加 11 // ビューが表示される直前に UserDefaults から色を読み込み、変数にセットし、セルをリロードする 12 override func viewWillAppear(_ animated: Bool) { 13 super.viewWillAppear(animated) 14 15 cellColor = defaults.color(forKey: "CellColor") 16 tableView.reloadData() 17 } 18 // 中略 19} 20extension ViewController: UITableViewDataSource{ 21 // 中略 22 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 23 let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! StickyCell 24 cell.label.text = itemArray[indexPath.row].text 25 // 追加 26 // セルを表示する時に背景色をセットする 27 cell.stickyBubble.backgroundColor = cellColor 28 return cell 29 } 30}

コメントにもあるように、ViewWillAppear(_:) でセルの背景色を読み込み、tableView(_:cellForRowAt:)でセルを生成する時に背景色をセットします。

こんな感じかと思います。

注意しなければいけないのは、iOS13 以降の設定で、かつもしNavigationController を使わずに画面遷移し、表示が Pop Over だった場合には、元の画面に戻った時にViewWillAppear(_:)が呼び出されません。

なので、そのような場合には Full Screen で遷移させるか、あるいは適切な関連記事を読んでいただいて、元の画面に戻ったときの処理を設定していただければと思います。

投稿2020/09/03 06:59

編集2020/09/03 09:16
TsukubaDepot

総合スコア5086

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

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

kee4234

2020/09/03 07:11

早速のご回答ありがとうございます。 実現したいイメージを追加いたしましたのでもし解決法いただけるようでしたら是非ともよろしくお願いいたします。
TsukubaDepot

2020/09/03 07:36

操作画面はわかりましたが、全体のつながりがちょっとわからないので、可能であればコード全体を載せていただいた方がわかりやすいかもしれません。 いただいたコードの範囲で想像すると、Alert で色を選択したあとはカスタムセルのインスタンスを作り直す必要はなく、元の画面に戻った後 Cell を dequeue する時に設定した色を背景色にすれば良さそうに思えるのですが、それだとうまくいかないのでしょうか(画面の遷移方法などによってはうまくいかないかもしれません)
kee4234

2020/09/03 07:49

文字の制限で設定画面のコードは一部省略しておりますがメインの画面のソースコード載せさせていただいたのですがこちらでいかがでしょうか? お手数おかけいたしますが何卒よろしくお願いいたします。
TsukubaDepot

2020/09/03 09:16

変更案を回答本文に追記しましたので、ご確認いただけますでしょうか。
kee4234

2020/09/03 12:24

TsukubaDepot様 いただいた内容でコードを修正したころ希望の動きに対応することが出来ました。 いろいろ試してみて自分では解決出来ず諦めかけていたので本当に助かりました。 これでまた開発が進めることができます。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問