// // ChatVC.swift // yinmeng-ios // // Created by MaiMang on 2024/2/25. // 回话详情 import UIKit import NIMSDK class ChatVC: BaseViewController, HiddenNavigationBarProtocol { // 键盘收起/弹出 滚动的时候收起输入栏 private var isBecome: Bool = false /// 是否发送完成 private var isSended: Bool = true /// 输入栏默认的高度 private let ToolBarLastH: CGFloat = (52 + SafeAraeBottomHeight) public init(session: NIMSession) { vm = ChatViewModel(session: session) super.init(nibName: nil, bundle: nil) vm.delegate = self } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } var vm:ChatViewModel override var preferredStatusBarStyle: UIStatusBarStyle { .darkContent } override open func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if NIMSDK.shared().mediaManager.isPlaying() { NIMSDK.shared().mediaManager.stopPlay() } } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = ThemeColor(hexStr: "#F8F8FB") requestInfo() loadChatList() registerChatCell() let height = ScreenHeight - (ToolBarLastH + NavHeight) chatTableView.frame = CGRect(x: 0, y: NavHeight, width: ScreenWidth, height: height) navView.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: NavHeight) view.addSubview(chatTableView) view.addSubview(keyboardView) view.addSubview(navView) } ///注册cell private func registerChatCell() { chatTableView.register(cellType: ChatTextCell.self) chatTableView.register(cellType: ChatTimeCell.self) } private lazy var chatTableView: UITableView = { let tableView = UITableView(frame: .zero, style: .plain) tableView.delegate = self tableView.dataSource = self tableView.tableFooterView = UIView() tableView.separatorStyle = .none tableView.backgroundColor = .clear if #available(iOS 11.0, *) { tableView.contentInsetAdjustmentBehavior = .never } return tableView }() private lazy var keyboardView: ChatKeyboardView = { let toolBarY = ScreenHeight - ToolBarLastH let view = ChatKeyboardView(frame: CGRect(x: 0, y: toolBarY, width: ScreenWidth, height: ToolBarLastH)) view.backgroundColor = .white view.delegate = self return view }() private lazy var navView: ChatNavView = { let view = ChatNavView(frame: .zero) view.backgroundColor = .white return view }() } // MARK: - ChatKeyboardViewDelegate extension ChatVC: ChatKeyboardViewDelegate { func keyboard(_ keyboard: ChatKeyboardView, voiceDidFinish path: String) { vm.sendAudioMessage(filePath: path) { error in } } func keyboard(_ keyboard: ChatKeyboardView, DidFinish content: String) { ///发送消息 vm.sendTextMessage(text: content) { error in } } func keyboard(_ keyboard: ChatKeyboardView, DidBecome isBecome: Bool) { self.isSended = true self.isBecome = isBecome } func keyboard(_ keyboard: ChatKeyboardView, DidMoreMenu type: ChatMoreMenuType) { //TODO: 点击更多 } func keyboard(_ keyboard: ChatKeyboardView, DidObserver offsetY: CGFloat) { } } extension ChatVC { func requestInfo() { let params = ["uid": vm.session.sessionId] RequestGet(path: "user/get", parma: params) { data in if let info = Deserialized.toModel(with: data) { self.navView.name = info.nick } } fail: { code, msg in } let par:[String : Any] = ["uid": AuthManager.userUid, "isLikeUid": vm.session.sessionId] RequestGet(path: "fans/isLike", parma: par) { data in if let isLike = data as? Bool { self.navView.isLike = isLike } } fail: { code, msg in } } func loadChatList() { weak var weakSelf = self vm.queryRoamMsgHasMoreTime_v2 { error, historyEnd, newEnd, models, index in if let ms = models, ms.count > 0 { weakSelf?.chatTableView.reloadData() if weakSelf?.vm.isHistoryAnchorChat == true { let indexPath = IndexPath(row: index, section: 0) weakSelf?.chatTableView.scrollToRow(at: indexPath, at: .none, animated: false) } else { if let tempArrayJJom = weakSelf?.vm.messageObjects, tempArrayJJom.count > 0 { DispatchQueue.main.async { weakSelf?.chatTableView.scrollToRow( at: IndexPath(row: tempArrayJJom.count - 1, section: 0), at: .bottom, animated: false ) } } } } else if let err = error { HUDTool.show(with: err.localizedDescription) } } } private func insertRows() { let oldRows = chatTableView.numberOfRows(inSection: 0) if oldRows == 0 { chatTableView.reloadData() return } if oldRows == vm.messageObjects.count { chatTableView.reloadData() return } var indexs = [IndexPath]() for (i, _) in vm.messageObjects.enumerated() { if i >= oldRows { indexs.append(IndexPath(row: i, section: 0)) } } if !indexs.isEmpty { chatTableView.insertRows(at: indexs, with: .none) chatTableView.scrollToRow( at: IndexPath(row: vm.messageObjects.count - 1, section: 0), at: .bottom, animated: false ) } } public func tableViewDeleteIndexs(_ indexs: [IndexPath]) { chatTableView.beginUpdates() chatTableView.deleteRows(at: indexs, with: .none) chatTableView.endUpdates() } public func tableViewReloadIndexs(_ indexs: [IndexPath]) { chatTableView.beginUpdates() chatTableView.reloadRows(at: indexs, with: .none) chatTableView.endUpdates() } } extension ChatVC: ChatViewModelDelegate{ public func onRecvMessages(_ messages: [NIMMessage]) { insertRows() vm.markRead(messages: messages) { error in } } public func willSend(_ message: NIMMessage) { } public func didAppend(_ message: NIMMessage) { } public func send(_ message: NIMMessage, progress: Float) {} public func send(_ message: NIMMessage, didCompleteWithError error: Error?) { } private func indexPathsWithMessags(_ messages: [NIMMessage]) -> [IndexPath] { var indexPaths = [IndexPath]() for messageModel in messages { for (i, model) in vm.messageObjects.enumerated() { if model.msg?.messageId == messageModel.messageId { indexPaths.append(IndexPath(row: i, section: 0)) } } } return indexPaths } public func onDeleteMessage(_ message: NIMMessage, atIndexs: [IndexPath]) { if atIndexs.isEmpty { return } tableViewDeleteIndexs(atIndexs) } } extension ChatVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return vm.messageObjects.count } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let model = vm.messageObjects[safe: indexPath.row] if model?.type == .text { let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTextCell.self) cell.model = model } else if model?.type == .time { let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ChatTimeCell.self) cell.model = model } return UITableViewCell() } public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) } public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let m = vm.messageObjects[safe:indexPath.row] return CGFloat(m?.height ?? 0) } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { if isSended { isSended = false } } func scrollViewDidScroll(_ scrollView: UIScrollView) { if isSended { return } if chatTableView.y <= 0 && isBecome { DispatchQueue.main.async { NotificationCenter.default.post(name: .kChatTextKeyboardNeedHide, object: nil) } } } }