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

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

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

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

iOS

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

Xcode

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

Swift

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

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

2回答

646閲覧

チャット画面のセルが、特定の動作をしたあとランダムに非表示になっていく

beginner_sa

総合スコア5

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

iOS

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

Xcode

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

Swift

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

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

0クリップ

投稿2021/05/09 15:02

コード含め長文な相談となりますが、ご回答いただけますと幸いです。

前提・実現したいこと

■アプリ概要
・チャットアプリ
・FirebaseAuthでユーザー登録(メールアドレス・パスワード)
・Firestoreを利用してチャットルームやチャットルーム内のメッセージデータを管理

■実現したいこと
チャットルーム内のメッセージが、「自分のメッセージ」「相手のメッセージ」に分けて正常に表示されるようにしたい

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

「すでに他のユーザーが送信したメッセージが存在するチャットルーム」にて、自分のメッセージを送信すると、ランダムで既存のメッセージが非表示になっていき、何度もそれを繰り返すとやがてすべてのメッセージが非表示となる

該当のソースコード

文字数上限を超えてしまうため、追記いたします。

試したこと

db.collection("room").document(roomName).collection("messages").order(by: "date").addSnapshotListener

・上記で取得したメッセージデータを、for文を使ってMessage型の配列へ追加し、Message型の配列の数をPrint > 正常

・Firestore上ではメッセージデータが正常に反映されていることを確認
・cellForRowAtで構築するメッセージの情報(bodyやtimeStamp、isHiddenの状態など)をすべてPrint > 正常
・新規チャットルーム(仮名:TestRoomA)を作成 > TestRoomA内でメッセージを送信 > 何通送信しても正常に反映(表示)される
・ログアウト > 新規アカウント(別アカウント)作成 > TestRoomAを開く > 上記で送信したメッセージは相手からのメッセージとして表示されているが、ここでメッセージを送信すると、送信する度ランダムに相手からのメッセージが非表示になっていき、やがて自分のメッセージ含めすべてのメッセージがそのチャット画面から非表示になる
※メッセージのデータはFirestore上に残ったままであるため、メッセージが「消える」ではなく「非表示になる」という表現を使っております

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

macOS 11.2.3 Xcode 12.3 iOS 14.3(Simulator) ※手持ちのiPhoneはmacに認識されないため、Simulatorのみで検証しております メッセージのセルはカスタムセルを使用しているため、カスタムセルの画像も添付致します。![イメージ説明](9becfb1f8b0371a532cca86420f98d22.png)

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

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

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

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

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

guest

回答2

0

ベストアンサー

cellForRowAt の中で isHidden = true にしたものを false に戻してないのが原因ですね。TableView のセルは再利用されるので、他の人のメッセージ表示するために使われたセルが自分のメッセージを表示するために再利用されたり、その逆が起こる可能性があり、そのたびに非表示のものが増えていくと思います。

swift

1 if message.sender == Auth.auth().currentUser?.uid{ 2 3 print("自分のメッセージ: (message.body)") 4 cell.partnersNameLabel.isHidden = true 5 cell.partnersImageView.isHidden = true 6 cell.partnersMessageTextView.isHidden = true 7 cell.timestampByPartner.isHidden = true 8 9 }else{ 10 11 print("相手のメッセージ: (message.body)") 12 cell.myMessageTextView.isHidden = true 13 cell.timestampByMyself.isHidden = true 14 15 }

または、自分と他の人のメッセージ表示するセルはクラスや xib ファイルを分けてもいいかも…。

投稿2021/05/10 13:39

編集2021/05/10 13:40
hoshi-takanori

総合スコア7901

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

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

0

該当のコードは下記となります。

①チャット画面のコード

Swift

1// 2// RoomViewController.swift 3// SimpleDiscussionApp 4// 5 6import UIKit 7import Firebase 8import FirebaseFirestore 9import SDWebImage 10 11class ChatViewController: UIViewController,UITextFieldDelegate,UITableViewDelegate,UITableViewDataSource { 12 13 @IBOutlet weak var tableView: UITableView! 14 @IBOutlet weak var messageTextField: UITextField! 15 @IBOutlet weak var roomImageView: UIImageView! 16 @IBOutlet weak var sendButton: UIButton! 17 18 let db = Firestore.firestore() 19 var roomName = String() 20 var userName = String() 21 var userImageString = String() 22 var roomImageString = String() 23 var messages = [Message]() 24 25 override func viewDidLoad() { 26 super.viewDidLoad() 27 28 tableView.delegate = self 29 tableView.dataSource = self 30 messageTextField.delegate = self 31 //Cell(xib)の情報を呼び出す 32 tableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "MessageCell") 33 //userImage(icon)とuserNameを取り出しておく 34 if let savedUserImageString = UserDefaults.standard.object(forKey: "iconImage") as? String, 35 let userName = UserDefaults.standard.object(forKey: "userName") as? String{ 36 37 self.userImageString = savedUserImageString 38 self.userName = userName 39 40 } 41 42 } 43 44 override func viewWillAppear(_ animated: Bool) { 45 super.viewWillAppear(animated) 46 47 loadMessages(roomName: roomName) 48 roomImageView.sd_setImage(with: URL(string: roomImageString), completed: nil) 49 navigationController?.isNavigationBarHidden = false 50 sendButton.isEnabled = false 51 sendButton.layer.masksToBounds = true 52 sendButton.layer.cornerRadius = 25 53 54 } 55 56 //ルームデータの取得------------------------------------ 57 func loadMessages(roomName:String){ 58 59 //メッセージ 60 db.collection("room").document(roomName).collection("messages").order(by: "date").addSnapshotListener { (snapShot, error) in 61 62 self.messages = [] 63 print("⓪メッセージの配列を空にしておきます") 64 var timeStamp = String() 65 let dateFormatter = DateFormatter() 66 dateFormatter.dateFormat = "HH:mm" 67 68 if error != nil{ 69 70 print("error: " + error.debugDescription) 71 return 72 } 73 74 if let snapShotDoc = snapShot?.documents{ 75 76 for doc in snapShotDoc{ 77 78 let data = doc.data() 79 if let sender = data["sender"] as? String, 80 let userName = data["userName"] as? String, 81 let body = data["body"] as? String, 82 let userImageString = data["userImageString"] as? String, 83 let date = data["date"] as? Timestamp{ 84 85 let time = date.dateValue() 86 timeStamp = dateFormatter.string(from: time) 87 let newMessage = Message(sender: sender, userName: userName, body: body, userimageString: userImageString, timeStamp: timeStamp) 88 89 self.messages.append(newMessage) 90 print("①取得したメッセージ件数分、配列へ追加します") 91 92 } 93 94 } 95 96 print("メッセージ件数: " + String(self.messages.count)) 97 self.tableView.reloadData() 98 print("③tableViewのデータをリロードします") 99 if self.messages.count > 0{ 100 //[0]からカウントのため、rowが10あれば[0~9]と数えるので、self.messages.count - 1 と記述 101 let indexPath = IndexPath(row: self.messages.count - 1, section: 0) 102 //送信したメッセージの箇所まで自動的にスクロールする 103 self.tableView.scrollToRow(at: indexPath, at: .top, animated: true) 104 105 } 106 107 } 108 109 } 110 111 } 112 113 //tableView------------------------------------ 114 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 115 return messages.count 116 } 117 118 func numberOfSections(in tableView: UITableView) -> Int { 119 return 1 120 } 121 122 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 123 124 print("④配列にある数だけ、セルを構築します((messages.count)件)") 125 let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell 126 let message = messages[indexPath.row] 127 128 if message.sender == Auth.auth().currentUser?.uid{ 129 130 print("自分のメッセージ: (message.body)") 131 print("⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤") 132 cell.myMessageTextView.text = message.body 133 print(cell.myMessageTextView.text!) 134 cell.timestampByMyself.text = message.timeStamp 135 print(cell.timestampByMyself.text!) 136 cell.partnersNameLabel.isHidden = true 137 print(cell.partnersNameLabel.isHidden) 138 cell.partnersImageView.isHidden = true 139 print(cell.partnersImageView.isHidden) 140 cell.partnersMessageTextView.isHidden = true 141 print(cell.partnersMessageTextView.isHidden) 142 cell.timestampByPartner.isHidden = true 143 print(cell.timestampByPartner.isHidden) 144 print("⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤") 145 146 }else{ 147 148 print("相手のメッセージ: (message.body)") 149 print("⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤") 150 cell.partnersMessageTextView.text = message.body 151 print(cell.partnersMessageTextView.text!) 152 cell.partnersNameLabel.text = message.userName 153 print(cell.partnersNameLabel.text!) 154 cell.timestampByPartner.text = message.timeStamp 155 print(cell.timestampByPartner.text!) 156 cell.partnersImageView.sd_setImage(with: URL(string: messages[indexPath.row].userimageString), completed: nil) 157 cell.myMessageTextView.isHidden = true 158 print(cell.myMessageTextView.isHidden) 159 cell.timestampByMyself.isHidden = true 160 print(cell.timestampByMyself.isHidden) 161 if let image = cell.partnersImageView.image{ 162 163 print(image) 164 165 } 166 print("⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤⏤") 167 168 } 169 170 return cell 171 } 172 173 174 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 175 return UITableView.automaticDimension 176 } 177 178 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 179 180 if textField.text != nil{ 181 182 sendButton.isEnabled = true 183 184 } 185 186 return true 187 } 188 //メッセージの送信------------------------------------ 189 @IBAction func sendAction(_ sender: Any) { 190 191 if let messageBody = messageTextField.text, 192 let sender = Auth.auth().currentUser?.uid{ 193 194 let timeInterval = Date().timeIntervalSince1970 195 let sendTimeInterval = TimeInterval(timeInterval) 196 let time = Date(timeIntervalSince1970: TimeInterval(sendTimeInterval)) 197 let userName = self.userName 198 199 db.collection("room").document(roomName).collection("messages").addDocument(data: [ 200 201 "sender":sender,"userName":userName,"body":messageBody,"userImageString":self.userImageString,"date":time 202 203 ]) { (error) in 204 205 if error != nil{ 206 207 print("error: " + error.debugDescription) 208 return 209 210 } 211 212 self.messageTextField.text = "" 213 self.messageTextField.resignFirstResponder() 214 215 } 216 217 db.collection("room").document(roomName).updateData(["latestMessage":messageBody], completion: { (error) in 218 219 if error != nil{ 220 221 print("error: " + error.debugDescription) 222 return 223 224 } 225 226 }) 227 228 } 229 } 230 231 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 232 233 messageTextField.resignFirstResponder() 234 235 } 236 237 func textFieldShouldReturn(_ textField: UITextField) -> Bool { 238 239 messageTextField.resignFirstResponder() 240 241 } 242 243} 244

投稿2021/05/09 15:05

beginner_sa

総合スコア5

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

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

hoshi-takanori

2021/05/09 20:14 編集

MessageCell は再利用されるので、他の人のメッセージ表示するために使われたセルが自分のメッセージを表示するために再利用されたり、その逆が起こります。ので、isHidden = true にしたやつは isHidden = false に戻す必要があります。(または、自分と他の人のメッセージ表示するセルはクラスを分けてもいいかも…。)
beginner_sa

2021/05/09 23:10

ご回答ありがとうございます!! そうゆうことでしたか…MessagaCellが再利用されるというポイントを理解できておりませんでした、、 > (または、自分と他の人のメッセージ表示するセルはクラスを分けてもいいかも…。) ご助言いただいた上記のことなのですが、クラスを分ける場合、カスタムセルももう一つ作るという理解で合っていますでしょうか??
hoshi-takanori

2021/05/09 23:15

> クラスを分ける場合、カスタムセルももう一つ作るという理解で合っていますでしょうか?? はい、クラスを分けて、xib も分けるという意味です。(クラスは分けずに、xib だけ分けてもいいかも…。)
beginner_sa

2021/05/09 23:18

ありがとうございます! 今晩コードを修正/追記して、またご報告させていただきます^ ^!
beginner_sa

2021/05/10 10:33

先ほど必要なコードを追記しましたところ、無事にセルが表示されるようになりました! 数日ずっと行き詰まっていたので大変助かりました・・・ ありがとうございました!
beginner_sa

2021/05/10 10:42

ベストアンサーをお付けしたいので、もしよろしければ、ご回答くださった内容を回答の方へ再投稿いただけますと幸いです!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問