🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

1回答

921閲覧

realmでTODOアプリを作った時のcheckmarkの表示について

tyu

総合スコア17

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2021/02/13 23:55

編集2021/02/14 02:28

【現在作成中のアプリの概要】

TODOリストのアプリを作成しています。
データの永続化には『realm』を使用しています。

realmに記録するデータは下記のToDoModel.swiftに記載しています。

【現在詰まっている箇所】

セルを選択した時にチェックマークを表示できるようにさせたいのですが、うまくいきません。
また、チェックマークの有無もrealmで記録させたいのですが方法がわかりません。

自身で調べたところ、チェックマークの表示にbool型が絡んでくるのはわかりましたが解決には至りませんでした。
上記の実装を可能にする方法を知っている方がいらっしゃいましたら、ご教授の程よろしくお願い致します。

swift

1import UIKit 2import RealmSwift 3 4 5 6class ViewController: UIViewController,UITextFieldDelegate,UITableViewDelegate,UITableViewDataSource { 7 8 9 10 @IBOutlet weak var todoTableView: UITableView! 11 12 13 14 15 var itemList: Results<TodoModel>! 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 20 let realmInstance = try! Realm() 21 self.itemList = realmInstance.objects(TodoModel.self).sorted(byKeyPath: "order") 22 23 //更新 24 self.todoTableView.reloadData() 25 26 27 // UITableViewDataSource を self に設定 28 self.todoTableView.dataSource = self 29 // UITableViewDelegate を self に設定 30 self.todoTableView.delegate = self 31 32 33 34 //常時編集状態にする(isEditing,allowsSelectionDuringEditing) 35// todoTableView.isEditing = true 36 todoTableView.isEditing = false 37 todoTableView.allowsSelectionDuringEditing = true 38 39 // trueで複数選択、falseで単一選択 40 todoTableView.allowsMultipleSelection = true 41 42 43 44 45 } 46 // Add ボタンをクリックした際に実行する処理 47 //セルに文字を入力する 48 @IBAction func tapAddButton(_ sender: Any) { 49 50 var textField = UITextField() 51 // ① UIAlertControllerクラスのインスタンスを生成 参照サイト(https://qiita.com/funacchi/items/b76e62eb82fc8d788da5) 52 let alert = UIAlertController(title: "タスクを追加", message: "", preferredStyle: .alert) 53 // ② Actionの設定 54 let action = UIAlertAction(title: "追加する", style: .default) { (action) in 55 let instancedTodoModel:TodoModel = TodoModel() 56 instancedTodoModel.todo = textField.text 57 58 print(instancedTodoModel) 59 let realmInstance = try! Realm() 60 try! realmInstance.write{ 61 realmInstance.add(instancedTodoModel) 62 } 63 self.todoTableView.reloadData() 64 } 65 66 alert.addTextField { (alertTextField) in 67 alertTextField.placeholder = "タスクを入力" 68 textField = alertTextField 69 } 70 // キャンセルボタンの追加 71 let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel, handler:{ 72 // ボタンが押された時の処理を書く(クロージャ実装) 73 (action: UIAlertAction!) -> Void in 74 print("Cancel") 75 }) 76 // ③ UIAlertControllerにActionを追加 77 alert.addAction(cancelAction) 78 alert.addAction(action) 79 present(alert, animated: true, completion: nil) 80 81 } 82 // Remove All ボタンをクリックした際に実行する処理 83 //セルに表示されている文字をすべて削除する 84 @IBAction func tapRemoveAllButton(_ sender: Any) { 85 let realmInstance = try! Realm() 86 try! realmInstance.write{ 87 //↓以下のコードを有効にするとrealmに記録しているすべてのデータが消えてしまう 88// realmInstance1.deleteAll() 89 //↑上記のコードだとrealmに記録しているすべてのデータが消えてしまうため下記のコードに書き換えた↓ 90 realmInstance.delete(itemList) 91 } 92 self.todoTableView.reloadData() 93 } 94 95 //セルの編集モードをONにする 96 @IBAction func edit(_ sender: Any) { 97 todoTableView.isEditing = true 98 } 99 100 // セルが選択された時に呼び出される 101 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 102 print("セル(indexPath)が選択されました") 103 //テーブルビューのセルをクリックしたら、アラートコントローラを表示 104// showAlertController(indexPath) 105 let item: TodoModel = self.itemList[(indexPath as NSIndexPath).row] 106 107 item.done = !item.done 108// Realm に保存したデータを UIAlertController に入力されたデータで更新 109 let realmInstance = try! Realm() 110 try! realmInstance.write{ 111 itemList[indexPath.row].done = item.done 112 } 113 // リロードしてUIに反映 114 self.todoTableView.reloadData() 115 } 116 117 //セルの選択が外れた時に呼び出される 118 func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 119 120 } 121 122 func numberOfSectionsInTableView(tableView: UITableView) -> Int { 123 return 1 124 } 125 //セルの数を返す 126 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 127 return self.itemList.count 128 } 129 130 //必須メソッド_埋め込むセルの値を設定 131 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 132 133 let testCell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "testCell")! 134 let item: TodoModel = self.itemList[(indexPath as NSIndexPath).row] 135 testCell.textLabel?.text = item.todo 136 //チェックマークを表示する処理ーitemのdoneがtrueだったら表示falseだったら非表示 137 testCell.accessoryType = item.done ? .checkmark : .none 138 return testCell 139 } 140 //並べ替え可能にするメソッド 141 func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { 142 return true 143 } 144 145 //realmでtableviewのセルを並び替えるメソッド(https://ja.stackoverflow.com/questions/35653/realmをデータソースにしてテーブルビューの並べ替えをしたい) 146 //並べ替え結果を処理するメソッド 147 func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 148 // TODO: 入れ替え時の処理を実装する(データ制御など) 149 print("並び替え開始") 150 let realmInstance = try! Realm() 151 try! realmInstance.write{ 152 let sourceObject = itemList[sourceIndexPath.row] 153 print("最初の行",sourceObject.order) 154 let destinationObject = itemList[destinationIndexPath.row] 155 156 let destinationObjectOrder = destinationObject.order 157 158 159 if sourceIndexPath.row < destinationIndexPath.row { 160 // 上から下に移動した場合、間の項目を上にシフト 161 for index in sourceIndexPath.row...destinationIndexPath.row { 162 let object = itemList[index] 163 object.order -= 1 164 } 165 } else { 166 // 下から上に移動した場合、間の項目を下にシフト 167 for index in (destinationIndexPath.row..<sourceIndexPath.row).reversed() { 168 let object = itemList[index] 169 object.order += 1 170 } 171 } 172 // 移動したセルの並びを移動先に更新 173 sourceObject.order = destinationObjectOrder 174 print("最後の行",sourceObject.order) 175// self.todoTableView.reloadData() 176 } 177 178 } 179 180 //以下のコードを追加すると削除モードが表示されなくなる(editingStyleForRowAt,shouldIndentWhileEditingRowAt) 181 //編集状態の見た目を編集(左側の+やーを表示)これを消すとマイナスボタンが表示される 182// func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { 183// return .none //表示させない。 184// } 185 186 187 //編集モード時に左にずれるか。 188 func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool { 189// return false//ずれない。 190 return true//ずれる 191 } 192 193// テーブルビューの編集を許可 194 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 195 return true 196 } 197 198 // テーブルビューのセルとデータを削除 199 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 200 if editingStyle == UITableViewCell.EditingStyle.delete { 201 // データを削除 202 let realmInstance = try! Realm() 203 try! realmInstance.write { 204 realmInstance.delete(itemList[indexPath.row]) 205 } 206 // セルを削除 207 tableView.deleteRows(at: [indexPath as IndexPath], with: UITableView.RowAnimation.automatic) 208 } 209 } 210 211 212 //テーブルビューのセルをクリックしたら、アラートコントローラを表示する処理 213 func showAlertController(_ indexPath: IndexPath){ 214 let alertController: UIAlertController = UIAlertController(title: "ToDo を編集", message: "", preferredStyle: .alert) 215 // アラートコントローラにテキストフィールドを表示 テキストフィールドには入力された情報を表示させておく処理 216 alertController.addTextField(configurationHandler: {(textField: UITextField!) in 217 textField.text = self.itemList[indexPath.row].todo}) 218 219 // アラートコントローラに"OK"ボタンを表示 "OK"ボタンをクリックした際に、テキストフィールドに入力した文字で更新する処理を実装 220 alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { 221 (action) -> Void in self.updateAlertControllerText(alertController,indexPath) 222 })) 223 // アラートコントローラに"Cancel"ボタンを表示 224 alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) 225 self.present(alertController, animated: true, completion: nil) 226 227 //removeTask 228 } 229 230 // "OK"ボタンをクリックした際に、テキストフィールドに入力した文字で更新 231 func updateAlertControllerText(_ alertcontroller:UIAlertController, _ indexPath: IndexPath) { 232 // guard を利用して、nil チェック 233 guard let textFields = alertcontroller.textFields else {return} 234 guard let text = textFields[0].text else {return} 235 236 // UIAlertController に入力された文字をコンソールに出力 237 print(text) 238 239 // Realm に保存したデータを UIAlertController に入力されたデータで更新 240 let realmInstance = try! Realm() 241 try! realmInstance.write{ 242 itemList[indexPath.row].todo = text 243 } 244 self.todoTableView.reloadData() 245 } 246}

swift

1import Foundation 2import RealmSwift 3 4class TodoModel: Object{ 5 @objc dynamic var todo: String? = nil 6 @objc dynamic var order: Int = 0 // 並べ替えのためのカラムが必要 7 @objc dynamic var done: Bool = false 8 9} 10

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

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

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

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

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

hoshi-takanori

2021/02/14 00:33

うまくいかないとは、具体的に何がどううまくいかないのでしょうか? Realm よく分かりませんが、item.done を反転させる処理も realmInstance.write する必要がありそう…。
tyu

2021/02/14 00:57

現状は、セルをタップしてチェックマークを表示させようとすると、「Thread 1: "Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first."」というエラーが表示されてしまいます。 item.done を反転後の realmInstance.write 処理も必須ですね。こちらはrealmのBool値の保存方法を調べて実装してみます。
hoshi-takanori

2021/02/14 01:18

もしかして、write transaction というのがまさに realmInstance.write のことなのでは?
tyu

2021/02/14 01:23

ありがとうございます! realmInstance.writeの処理を追加したらエラーもなくなるかもしれませんね! 一応realmのbool値の保存について調べているのですが、まだ解決に至っていません。 「こうしたらいいんじゃないか」等がありましたら、教えていただけないでしょうか...
hoshi-takanori

2021/02/14 01:36

Realm 知らないので一般論ですが、item.done = !item.done で、item.done の値を反転 (元が true なら false、false なら true にする) して item.done を更新する処理になっていて、たぶん Realm でもそれでいいのでは…。
tyu

2021/02/14 02:26

ご教授いただきありがとうございます! func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 内に item.done の更新をrealmに更新する処理を追記したのですが(上記のコードを修正しました)、「Thread 1: "Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first."」のエラーが出てしまいます... realmって難しいですね
hoshi-takanori

2021/02/14 09:33

item.done = !item.done が try! realmInstance.write { 〜 } の外にありますが、これを中に入れれば良いのでは。 (そして、たぶん itemList[indexPath.row].done = item.done は不要かと…。)
TsukubaDepot

2021/02/14 12:08

回答を書く前にコメントを確認してよかったです。 hoshi-takanoriさんご指摘の通り、item.done = !item.done を write block の中に入れる必要があります。 itemList(SwiftのResult型)はマネージドオブジェクトでライブアップデートが効きますが、インスタンスやそのコピーに書き込み操作を行う場合には write block 内で行う必要があります。 itemList[indexPath.row].done = item.done も不要ですね(直前のコードで参照先に直接書き込んでいるので)。 あと、現在のコードだと、並び替えても操作は有効になりません。 TodoModel に order というプロパティがありますが、新しい ToDo を書き込むごとに、この order に常に最新で最大の番号を与えるようにしなければ、現在のロジックだと並び替えの結果が思った通りにならないはずです。
tyu

2021/02/14 16:10

hoshi-takanoriさん、TsukubaDepotさん、ご教授いただきありがとうございます! お二人のお陰でチェックマークの永続化ができました。もう少しrealmについて勉強します... TsukubaDepotさんがおっしゃっているように並び替えの結果が思うようにいきません。 別で質問をあげますので、お時間がありましたら、再度ご教授お願いしたいです。
TsukubaDepot

2021/02/14 22:57

では、とりあえずこのご質問は自己解決ということで処理をお願いできますでしょうか。 できれば、簡単に修正点を挙げていただくと、別の方の参考になるかと思います。
tyu

2021/02/14 23:31

承知いたしました。いつもお力添えありがとうございます。
guest

回答1

0

自己解決

下記コードに修正することで解決に至りました。
hoshi-takanoriさん、TsukubaDepotさん、お力添え誠にありがとうございます。

swift

1// セルが選択された時に呼び出される 2 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 3 print("セル(indexPath)が選択されました") 4 //テーブルビューのセルをクリックしたら、アラートコントローラを表示 5// showAlertController(indexPath) 6 let item: TodoModel = self.itemList[(indexPath as NSIndexPath).row] 7 8 9 10 // Realm に保存したデータを UIAlertController に入力されたデータで更新 11 let realmInstance = try! Realm() 12 try! realmInstance.write{ 13 item.done = !item.done 14// itemList[indexPath.row].done = item.done 15 } 16 // リロードしてUIに反映 17 self.todoTableView.reloadData() 18 }

投稿2021/02/14 23:34

tyu

総合スコア17

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問