スポンサーリンク
import UIKit import Firebase import MessageKit import SCLAlertView class MessageKitViewController: MessagesViewController, Utils, WebViewUtils { let chatCnt: Int = 50 var db: Firestore! var messageList: [MockMessage] = [] var chatName: String? let appDelegate = UIApplication.shared.delegate as! AppDelegate private var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView() var blockUsers: [String]? = nil var blockKeywords: [String]? = nil var curDate: NSAttributedString? = nil lazy var timeFmt: DateFormatter = { let timeFmt = DateFormatter() timeFmt.dateStyle = .none timeFmt.timeStyle = .medium return timeFmt }() lazy var dateFmt: DateFormatter = { let dateFmt = DateFormatter() dateFmt.dateStyle = .medium dateFmt.timeStyle = .none return dateFmt }() override func viewDidLoad() { super.viewDidLoad() self.title = Strings().MENU_CHAT appDelegate.updateChatBackFlg = false self.chkChatTerms() initChatView() } func chkChatTerms(){ if !self.getChkChatTerms() { self.simplePopup(msg: Strings().CHK_TERMS_MSG) // 利用規約の同意を求める self.saveChkChatTerms() } } func initChatView(){ self.startIndicator(aic: &activityIndicator, vc: self) // クルクルスタート // 通信状態を確認 if CheckReachability(host_name: "google.com") { self.dispChatPage() } } func dispChatPage(){ self.chatName = getChatName() // データベース読み取り self.setupFirebase() // アイコンの表示を消し、その分ラベルを移動させる if let layout = self.messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout { layout.setMessageIncomingAvatarSize(.zero) layout.setMessageOutgoingAvatarSize(.zero) layout.setMessageIncomingMessageTopLabelAlignment(LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 10))) layout.setMessageIncomingMessageBottomLabelAlignment(LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 10))) layout.setMessageOutgoingMessageTopLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 10))) layout.setMessageOutgoingMessageBottomLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 10))) } messagesCollectionView.messagesDataSource = self messagesCollectionView.messagesLayoutDelegate = self messagesCollectionView.messagesDisplayDelegate = self messagesCollectionView.messageCellDelegate = self messageInputBar.delegate = self messageInputBar.sendButton.tintColor = UIColor.lightGray // メッセージ入力欄の左に画像選択ボタンを追加 // 画像選択とかしたいときに let items = [ makeButton(named: "haguruma").onTextViewDidChange { button, textView in button.tintColor = UIColor.lightGray button.isEnabled = textView.text.isEmpty } ] items.forEach { $0.tintColor = .lightGray } messageInputBar.setStackViewItems(items, forStack: .left, animated: false) messageInputBar.setLeftStackViewWidthConstant(to: 45, animated: false) // メッセージ入力時に一番下までスクロール scrollsToBottomOnKeybordBeginsEditing = true // default false maintainPositionOnKeyboardFrameChanged = true // default false } // ボタンの作成 func makeButton(named: String) -> InputBarButtonItem { return InputBarButtonItem() .configure { $0.spacing = .fixed(10) $0.image = UIImage(named: named)?.withRenderingMode(.alwaysTemplate) $0.setSize(CGSize(width: 30, height: 30), animated: true) }.onSelected { $0.tintColor = UIColor.gray }.onDeselected { $0.tintColor = UIColor.lightGray }.onTouchUpInside { _ in print("Item Tapped") } } func setupFirebase() { // Firestoreのインスタンス化 self.db = Firestore.firestore() // 最新のデータが追加されるたびに最新データを取得する self.addMessageList() } func addMessageList() { // 最新50件のデータをデータベースから取得する self.db.collection("chat1").order(by: "timestamp", descending: true).limit(to: self.chatCnt + 25).addSnapshotListener { (snapShot, error) in guard let value = snapShot else { print("snapShot is nil") return } snapShot!.documentChanges.forEach { diff in if (diff.type == .added) { print("New city: \(diff.document.data())") let snapshotValue = diff.document.data() let text = snapshotValue["text"] as! String let name = snapshotValue["name"] as! String let date = snapshotValue["timestamp"] as! Date self.messageList.append(self.createMessage(text: text, sender: name, name: name, date: date)) } if (diff.type == .modified) { print("Modified city: \(diff.document.data())") } if (diff.type == .removed) { print("Removed city: \(diff.document.data())") } } //逆順(昇順)ソート self.messageList.reverse() // リロード self.messagesCollectionView.reloadData() self.messagesCollectionView.scrollToBottom() self.stopIndicator(aic: self.activityIndicator) // クルクルストップ } } func chkBlockUser(name: String) ->Bool { if self.blockUsers == nil { self.blockUsers = self.getBlockUser() } if (self.blockUsers?.contains(name))! { return true } return false } func chkBlockKeyword(keyword: String) ->Bool { if self.blockKeywords == nil { self.blockKeywords = self.getBlockKeywords() } for k in self.blockKeywords! { if keyword.lowercased().contains(k.lowercased()) { return true } } return false } func createMessage(text: String, sender: String, name: String, date: Date) -> MockMessage { let attributedText = NSAttributedString(string: text, attributes: [.font: UIFont.systemFont(ofSize: 15), .foregroundColor: UIColor.white]) return MockMessage(text: text, attributedText: attributedText, sender: otherSender(sender: sender, name: name), messageId: UUID().uuidString, date: date) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(false, animated: false) } override func viewDidAppear(_ animated: Bool) { self.becomeFirstResponder() // inputバーを再表示 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } extension MessageKitViewController: MessagesDataSource { func currentSender() -> Sender { return Sender(id: chatName!, displayName: chatName!) } func otherSender(sender: String, name: String) -> Sender { return Sender(id: sender, displayName: name) } func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int { return messageList.count } func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType { return messageList[indexPath.section] } // 日付区切り文字を表示 // func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { //// if indexPath.section % 3 == 0 { //// return NSAttributedString( //// string: MessageKitDateFormatter.shared.string(from: message.sentDate), //// attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 10), //// NSAttributedStringKey.foregroundColor: UIColor.darkGray] //// ) //// } // print(indexPath.section) // let dateString = lFormatter.string(from: message.sentDate) // let sentDateStr = NSAttributedString(string: dateString, attributes: [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .caption2)]) // if indexPath.section == 0 || self.curDate != sentDateStr { // self.curDate = sentDateStr // return sentDateStr // } else { // return nil // } // } // メッセージの上に文字を表示(名前) func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { let name = message.sender.displayName return NSAttributedString(string: name, attributes: [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .caption1)]) } // メッセージの下に文字を表示(日付) func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { let today = dateFmt.string(from: Date()) let date = dateFmt.string(from: message.sentDate) let time = timeFmt.string(from: message.sentDate) if today == date { return NSAttributedString(string: time, attributes: [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .caption2)]) } else { return NSAttributedString(string: date + " " + time, attributes: [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .caption2)]) } } } // メッセージのdelegate extension MessageKitViewController: MessagesDisplayDelegate { // メッセージの色を変更(黒) func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { return .darkText } // メッセージの背景色を変更している(デフォルトは自分:緑、相手:グレー) func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { return ColorStyles().chatMsgBackColor } // メッセージの枠にしっぽを付ける func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageStyle { let corner: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft return .bubbleTail(corner, .curved) } // アイコンをセット // func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) { // // message.sender.displayNameとかで送信者の名前を取得できるので // // そこからイニシャルを生成するとよい // let avatar = Avatar(initials: "人") // avatarView.set(avatar: avatar) // } } // 各ラベルの高さを設定(デフォルト0なので必須) extension MessageKitViewController: MessagesLayoutDelegate { func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { if indexPath.section % 3 == 0 { return 10 } return 0 } func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { return 16 } func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { return 16 } } let topViewController = UIApplication.shared.windows.last!.rootViewController! extension MessageKitViewController: MessageCellDelegate { // メッセージをタップした時の挙動 func didTapMessage(in cell: MessageCollectionViewCell) { let indexPath = messagesCollectionView.indexPath(for: cell) let msgData = messageList[(indexPath?.section)!] let appearance = SCLAlertView.SCLAppearance( kCircleIconHeight: 35.0, kTitleFont: UIFont(name: "HelveticaNeue", size: 0)!, kTextFont: UIFont(name: "HelveticaNeue", size: 0)!, kButtonFont: UIFont(name: "HelveticaNeue-Bold", size: 13)!, showCloseButton: false ) let alertView = SCLAlertView(appearance: appearance) alertView.addButton(Strings().CHAT_REPORT_BTN, backgroundColor: ColorStyles().chatMsgBackColor, textColor: UIColor.white) { // chat_reportテーブルへinsert self.executeReport(msgData: msgData) } alertView.addButton(Strings().CHAT_USER_BLOCK_BTN, backgroundColor: ColorStyles().chatMsgBackColor, textColor: UIColor.white) { // UserDefaultへユーザーを保存 self.saveBlockUser(name: msgData.sender.displayName) // 通報完了メッセージ self.simplePopup(msg: Strings().ADD_BLOCK_USER_FINISH) // TODO リフレッシュ処理 } alertView.addButton(Strings().CHAT_CANCEL_BTN, backgroundColor: ColorStyles().chatMsgBackColor, textColor: UIColor.white) { } alertView.showCustom("", subTitle: "", color: ColorStyles().chatMsgBackColor, icon: UIImage(named:"bikkuri_white")!) } func executeReport(msgData: MockMessage){ // 通報(chat_reportテーブル更新) self.getAddChatReport(msgData: msgData) // 通報完了メッセージ self.simplePopup(msg: Strings().CHAT_REPORT_FINISH) } func getAddChatReport(msgData: MockMessage){ let from: String = msgData.sender.id let name: String = msgData.sender.displayName let text: String = msgData.text let sentDate: String = self.formatDate(date: msgData.sentDate) // http通信 let url = URL(string: makeSiteUrl() + Strings().addChatReportUrl) var request = URLRequest(url: url!) let postWord = "from=" + from + "&name=" + name + "&text=" + text + "&sentDate=" + sentDate request.httpMethod = "POST" request.httpBody = postWord.data(using: .utf8) let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in if error != nil { print(error) return } }) task.resume() } } extension MessageKitViewController: MessageInputBarDelegate { // メッセージ送信ボタンをタップした時の挙動 func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) { for component in inputBar.inputTextView.components { if let image = component as? UIImage { let imageMessage = MockMessage(image: image, sender: currentSender(), messageId: UUID().uuidString, date: Date()) messageList.append(imageMessage) messagesCollectionView.insertSections([messageList.count - 1]) } else if let text = component as? String { //Firestoreにデータを送信、保存する var ref: DocumentReference? = nil ref = db.collection("chat1").addDocument(data: [ "name": chatName as! String, "text": text, "timestamp": NSDate() ]) { err in if let err = err { print("Error adding document: \(err)") } else { print("Document added with ID: \(ref!.documentID)") } } } } inputBar.inputTextView.text = String() messagesCollectionView.scrollToBottom() } } fileprivate extension UIEdgeInsets { init(top: CGFloat = 0, bottom: CGFloat = 0, left: CGFloat = 0, right: CGFloat = 0) { self.init(top: top, left: left, bottom: bottom, right: right) } }
Firebaseの新DB「Firestore」を使ったチャットページです。
Firebase部分以外はほぼ参考ページのままですが、メッセージタップ時に
「ユーザーブロック or 通報する」アラートを表示するよう機能追加しています。
このソースだけじゃ動かないけど、ご参考までに。
参考にしたページ → MessageKitの使いかた(初歩)
スポンサーリンク
スポンサーリンク