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

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

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

RealmとはSQLiteやCore Dataに代わるモバイルデータベースです。iOSとAndroidの両方でサポートされています。

Swift

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

Q&A

解決済

2回答

132閲覧

[Swift] Realmを用いたUpdate

Kaguya_4869

総合スコア116

Realm

RealmとはSQLiteやCore Dataに代わるモバイルデータベースです。iOSとAndroidの両方でサポートされています。

Swift

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

0グッド

0クリップ

投稿2024/03/19 01:01

編集2024/03/19 01:14

実現したいこと

現在、Realmを用いてUITextViewに入力された文字列およびtextViewの座標の保存を行なっています。
実際にあるアプリの機能で例えると、Instagramの投稿をする時のような感じです。
例えばストーリーに写真をアップする際に文字列を写真の上に載せたい場合、
ユーザーがテキストを表示→入力→画面上の適当な部分をタップして文字列の編集終了→文字列をドラッグして好きな場所に文字を置く
というような流れがあります。この一連の流れの実現はできたのですが、これをRealmで保存する際に少々手こずってしまっています。ユーザーは自由にtextViewの内容を編集したり、textViewの位置を変更することができます。変更した後の状態をRealmで保存したいです。Realmに保存される内容は文字列、x座標, y座標です。

発生している問題・分からないこと

RealmのUpdateは、

Swift

1update: .modified

とすれば良いと思っていたのですが、これだと普通の保存になってしまいます。例えば、ドラッグ&ドロップで元々のTextViewの位置を移動させた時、ユーザーが移動させる前と後の両方の状態が残ってしまっています。

該当のソースコード

Swift

1// 保存の関数 2func save(text: String, x: Double, y: Double) { 3 let realm = try! Realm() 4 let model = Model() 5 model.title = text 6 model.x = x 7 model.y = y 8 try! realm.write { 9 realm.add(model, update: .modified) 10 } 11 }

Swift

1// PanGesture(ユーザーがドラッグ&ドロップをさせた時) 2@objc func handlePanGesture(_ recognizer: UIPanGestureRecognizer) { 3 guard let textView = recognizer.view as? UITextView else { return } 4 let translation = recognizer.translation(in: textView.superview) 5 var newX = textView.center.x + translation.x 6 var newY = textView.center.y + translation.y 7 8 newX = min(max(newX, textView.frame.width / 2 + 10), bgView.frame.width - textView.frame.width / 2 - 10) 9 newY = min(max(newY, textView.frame.height / 2 + 10), bgView.frame.height - textView.frame.height / 2 - 10) 10 11 textView.center = CGPoint(x: newX, y: newY) 12 recognizer.setTranslation(.zero, in: textView.superview) 13 if recognizer.state == .ended { 14 save(text: textView.text, x: newX, y: newY) 15 } 16 }

Swift

1// TextViewの内容が変更された時 2func textViewDidChange(_ textView: UITextView) { 3 let width = estimateFrameForTextView(text: textView.text).width + 10 4 let height = estimateFrameForTextView(text: textView.text).height + 10 5 textView.frame.size = CGSize(width: width, height: height) 6 UserDefaults.standard.set(textView.text, forKey: "text") 7 UserDefaults.standard.set(textView.center.x, forKey: "x") 8 UserDefaults.standard.set(textView.center.y, forKey: "y") 9 } 10 11override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 12 let text = UserDefaults.standard.string(forKey: "text") ?? "" 13 let x = UserDefaults.standard.double(forKey: "x") 14 let y = UserDefaults.standard.double(forKey: "y") 15 print(text, x, y) 16 save(text: text, x: x, y: y) 17 self.view.endEditing(true) 18 }

Swift

1//読み込み(Realmの内容を読み込んで、保存されている数に合わせてtextViewを生成。それぞれのtextViewを保存されている座標の位置に設定し、内容を反映させる。) 2override func viewWillAppear(_ animated: Bool) { 3 super.viewWillAppear(animated) 4 5 let realm = try! Realm() 6 self.itemList = realm.objects(Model.self).filter("date == %@", dateLabel.text!) // 今日の分の内容だけ表示させるようにfilterをかける。 7 print(itemList!) 8 9 for i in 0..<itemList.count { 10 let textView = UITextView() 11 textView.delegate = self 12 textView.text = itemList[i].title 13 textView.frame = CGRect(x: itemList[i].x, y: itemList[i].y, width: 100, height: 44) 14 textView.textColor = .black 15 textView.font = UIFont.systemFont(ofSize: 20) 16 let width = estimateFrameForTextView(text: textView.text).width + 10 17 let height = estimateFrameForTextView(text: textView.text).height + 10 18 textView.frame.size = CGSize(width: width, height: height) 19 textView.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0.8) 20 textView.textAlignment = .center 21 textView.isEditable = true 22 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) 23 textView.addGestureRecognizer(panGesture) 24 self.bgView.insertSubview(textView, belowSubview: self.dateLabel) 25 } 26 }

Swift

1// Model 2import Foundation 3import RealmSwift 4 5class Model: Object { 6 @objc dynamic var title: String = "" 7 @objc dynamic var x: Double = 0.0 8 @objc dynamic var y: Double = 0.0 9 @objc dynamic var id: String = UUID().uuidString 10 override static func primaryKey() -> String? { 11 return "id" 12 } 13} 14

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

元々textViewDidChange内でsave()を実行していたのですが、それだとtextViewの内容が変更される度に保存されてしまいました。(例えば、AAAと入力した時、"", "A", "AA", "AAA"が保存されてしまう)
update: .modifiedは、プライマリーキーを参照してCreateかUpdateを決めていると思うのですが、idをUUID().uuidStringというユニークなキーで保存してしまっているのが原因と考えています。しかし、どのように変更すれば良いか思いつかなかったので、ご教示いただけますと幸いです。
よろしくお願いいたします。

補足

特になし

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

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

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

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

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

guest

回答2

0

ベストアンサー

提示のsave関数はどのような場合でも新しいModelを生成しRealmにストアする関数になっています。
これはあなたの望む関数ではありません。

テキストの更新を実現するには

  1. ModelからUITextViewを生成するときにModel.idUITextViewの結びつきを何らかの形で保存します。この保存された関連をRとします。
  2. 更新するUITextViewを用い関連RからModel.idを取り出す。
  3. 新規作成したModelidおよびtitle, x, yを設定する。この時のidは先に取り出した 更新するUITextViewに結びつけられたidを用いる。
  4. update:引数を.modifiedとして3.で作成したModelをRealmにaddする。

とします。

ModelがRealmにupdate:引数が.modifiedであるaddにより追加されるとき、同じprimaryKeyModelがあればそれを上書きします。

関連Rは状況により決定してください。

また新しくテキストを追加するときも、まずModelを作成しそのidとテキストを表示するUITextViewとの結びつきを関連Rに保存する必要があります。

テキストの削除も同様に行ってください。

投稿2024/03/21 05:45

MasakiHori

総合スコア3384

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

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

Kaguya_4869

2024/03/22 05:44

ありがとうございます。 提示いただいた方法を試したところ、思うような動作になりました。 結びつきの部分については、textViewを生成するときに、textView.accessibilityIdentifierをユニークな値に設定して、save()関数において、モデルをforPrimaryKey: idとして、idが一致する場合はupdate, そうでない場合はcreateにすることで解決することができました。(もしかしたらもっと効率の良い方法があるのかもしれませんが…) ありがとうございました。
guest

0

idをUUID().uuidStringというユニークなキーで保存してしまっているのが原因と考えています。しかし、どのように変更すれば良いか思いつかなかったので、ご教示いただけますと幸いです。

アプリの仕様を全部把握できているわけではないですので、
見当違いなことを書いてしまったらごめんなさい。

プライマリーキーを制御しづらいUUIDのStringにしていることが問題でしたら、
Int型などで独自に採番していく感じかなと思いました。

でも、わからないですが、それでも他に問題がありそうな気もします。
画面上のUITextViewとModelインスタンスの関連がないように見えますので、
これが根本の問題になっているように思いました。
UITextViewとModelインスタンスを関連づけて保持するようにしておけば(Dictionaryとか)
進めやすいのではないかと思いました。
(saveの中で毎回Modelのインスタンスを生成しない)

Realmの公式のドキュメントも読み込むと良いかもしれません。
CRUD - Update - Swift SDK — Atlas Device SDK

投稿2024/03/20 00:39

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

Kaguya_4869

2024/03/22 05:45

ご回答いただきありがとうございます。また、ご返信が遅くなってしまい、大変申し訳ございません。 おっしゃる通り、UITextViewとModelの関連がないことが実際の原因でした。 解決の糸口を与えていただき、本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問