У меня есть UIView с UITableView, который перечисляет чаты. При нажатии на указанный c разговор, я модально перехожу к другому UIView, который снова содержит UITableView, в котором перечислены отдельные сообщения этого чата - т.е. детали чата.
ChatDetailViewController выглядит следующим образом:
class ChatDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var UserLogo: UIImageView!
@IBOutlet weak var UserName: UILabel!
@IBOutlet weak var MessageList: UITableView!
@IBOutlet weak var MessageTextField: UITextField!
@IBOutlet weak var SendButton: UIButton!
var conversation: Int = 0
let apiService = APIService()
var conversationDetails = ConversationDetails ()
var groupedMessages = [Date : [ChatMessage]]()
var keys = [Date]()
override func viewDidLoad() {
super.viewDidLoad()
self.UserName.text = ""
self.MessageList.separatorStyle = .none
self.MessageList.delegate = self
self.MessageList.dataSource = self
// self.SendButton.addTarget(self, action: #selector(sendMessage), for: .touchUpInside)
// load messages:
self.apiService.getChatMessages(conversation: self.conversation, completion: {result in
switch result {
case .success(let conversationDetails):
DispatchQueue.main.async {
self.conversationDetails = conversationDetails
// prepare header section:
let URLstring = self.conversationDetails.participants![0].profileimage
let imgURL = URL(string: URLstring ?? "www.foo.com") // TODO: insert placeholder image
self.UserLogo.sd_setImage(with: imgURL, placeholderImage: UIImage(named: "icon.turq.png"))
self.UserLogo.clipsToBounds = true
self.UserLogo.layer.cornerRadius = self.UserLogo.frame.size.width / 2
self.UserName.text = self.conversationDetails.participants![0].username
// group and sort messages:
let cal = Calendar.current
self.groupedMessages = Dictionary(grouping: self.conversationDetails.messages!, by: { cal.startOfDay(for: $0.tsp!) })
self.keys = self.groupedMessages.keys.sorted(by: { $0 < $1 })
// reload:
self.MessageList.reloadData()
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
...
}
...
func numberOfSections(in tableView: UITableView) -> Int {
return self.keys.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = customSectionHeaderLabel()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
label.text = dateFormatter.string(from: self.keys[section])
let headingContainer = UIView()
headingContainer.addSubview(label)
label.centerXAnchor.constraint(equalTo: headingContainer.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: headingContainer.centerYAnchor).isActive = true
return headingContainer
}
...
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.groupedMessages[keys[section]]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MessageViewCell", for: indexPath) as! ChatMessageViewCellController
let tspSortedInGroup = self.groupedMessages[self.keys[indexPath.section]]!.sorted(by: { $0.tsp! < $1.tsp! })
let chatMessage = tspSortedInGroup[indexPath.row]
cell.ChatMessageText.text = chatMessage.message
cell.isIncoming = self.isIncoming(message: chatMessage)
return cell
}
func isIncoming(message: ChatMessage) -> Bool {
return message.sender?.id != Globals.shared.user?.id
}
@IBAction func sendMessage(_ sender: Any) {
print(self.MessageTextField.text)
// create new ChatMessage object
let now = Date()
var newMessage = ChatMessage()
newMessage.tsp = now
newMessage.message = self.MessageTextField.text
newMessage.sender = Globals.shared.user
// call api to store sent message
self.apiService.sendChatMessage(conversation: self.conversation, message: newMessage.message!, completion: {result in
switch result {
case .success(let stringResponse):
DispatchQueue.main.async {
// append to grouped messages
self.groupedMessages[self.keys.last!]?.append(newMessage)
var path = IndexPath(row: self.groupedMessages[self.keys.last!]!.count-1, section: self.keys.count-1)
self.MessageList.beginUpdates()
self.MessageList.insertRows(at: [path], with: .fade)
self.MessageList.endUpdates()
self.MessageTextField.text = nil
self.MessageList.scrollToRow(at: path, at: .bottom, animated: true)
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
}
...
}
Практически все работает, как и следовало ожидать. Когда я запускаю приложение, затем нажимаю на детали чата c, все сообщения загружаются. Когда я набираю сообщение и нажимаю кнопку отправить, оно сохраняется в базе данных и отображается на экране. Экран прокручивается вниз. Все идеально. Я могу повторить отправку сообщений. Все тоже хорошо.
Когда я возвращаюсь к обзору чата (пролистывая модальное) и go в другой чат, все тоже хорошо. То же поведение, что и выше.
Проблемы начинаются, как только я go возвращаюсь в ранее открытый чат. Экран загружается и все выглядит здоровым для начала. Но когда я набираю сообщение и нажимаю «Отправить», оно показывает не только что набранное сообщение, а последнее из этого чата - то, которое было отправлено до того, как я покинул экран. Ввод и отправка второго сообщения делает то же самое - то есть показывает не только что набранное или предыдущее сообщение, но и последнее, которое я отправил перед тем, как покинуть экран.
Что делает это еще более странным, так это тот факт, что вывод консоли в пределах func sendMessage
- т.е. print(self.MessageTextField.text)
показывает правильный текст, и при проверке базы данных было сохранено правильное сообщение чата. Выход из ChatDetails и возврат к перезагрузке экрана, и отображаются правильные сообщения.
Так что я предполагаю, что это как-то связано с тем, как я добавляю сообщение в UITableView, как только возвращается вызов API.
self.groupedMessages[self.keys.last!]?.append(newMessage)
var path = IndexPath(row: self.groupedMessages[self.keys.last!]!.count-1, section: self.keys.count-1)
self.MessageList.beginUpdates()
self.MessageList.insertRows(at: [path], with: .fade)
self.MessageList.endUpdates()
self.MessageTextField.text = nil
self.MessageList.scrollToRow(at: path, at: .bottom, animated: true)
Но я не могу понять, что именно является причиной ошибки.