IMChatViewController.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. //
  2. // IMChatViewController.swift
  3. // O2Platform
  4. //
  5. // Created by FancyLou on 2020/6/8.
  6. // Copyright © 2020 zoneland. All rights reserved.
  7. //
  8. import UIKit
  9. import CocoaLumberjack
  10. import O2OA_Auth_SDK
  11. import BSImagePicker
  12. import Photos
  13. import Alamofire
  14. import AlamofireImage
  15. import SwiftyJSON
  16. import QuickLook
  17. class IMChatViewController: UIViewController {
  18. // MARK: - IBOutlet
  19. //消息列表
  20. @IBOutlet weak var tableView: UITableView!
  21. //消息输入框
  22. @IBOutlet weak var messageInputView: UITextField!
  23. //底部工具栏的高度约束
  24. @IBOutlet weak var bottomBarHeightConstraint: NSLayoutConstraint!
  25. //底部工具栏
  26. @IBOutlet weak var bottomBar: UIView!
  27. private let emojiBarHeight = 196
  28. //表情窗口
  29. private lazy var emojiBar: IMChatEmojiBarView = {
  30. let view = Bundle.main.loadNibNamed("IMChatEmojiBarView", owner: self, options: nil)?.first as! IMChatEmojiBarView
  31. view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: emojiBarHeight.toCGFloat)
  32. return view
  33. }()
  34. //语音录制按钮
  35. private lazy var audioBtnView: IMChatAudioView = {
  36. let view = Bundle.main.loadNibNamed("IMChatAudioView", owner: self, options: nil)?.first as! IMChatAudioView
  37. view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: emojiBarHeight.toCGFloat)
  38. view.delegate = self
  39. return view
  40. }()
  41. //录音的时候显示的view
  42. private var voiceIconImage: UIImageView?
  43. private var voiceIocnTitleLable: UILabel?
  44. private var voiceImageSuperView: UIView?
  45. //预览文件
  46. private lazy var previewVC: CloudFilePreviewController = {
  47. return CloudFilePreviewController()
  48. }()
  49. private lazy var viewModel: IMViewModel = {
  50. return IMViewModel()
  51. }()
  52. // MARK: - properties
  53. var conversation: IMConversationInfo? = nil
  54. //private
  55. private var chatMessageList: [IMMessageInfo] = []
  56. private var page = 1
  57. private var isShowEmoji = false
  58. private var isShowAudioView = false
  59. private var bottomBarHeight = 64 //底部输入框 表情按钮 的高度
  60. private let bottomToolbarHeight = 46 //底部工具栏 麦克风 相册 相机等按钮的位置
  61. // MARK: - functions
  62. override func viewDidLoad() {
  63. super.viewDidLoad()
  64. self.tableView.delegate = self
  65. self.tableView.dataSource = self
  66. self.tableView.register(UINib(nibName: "IMChatMessageViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageViewCell")
  67. self.tableView.register(UINib(nibName: "IMChatMessageSendViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageSendViewCell")
  68. self.tableView.separatorStyle = .none
  69. // self.tableView.rowHeight = UITableView.automaticDimension
  70. // self.tableView.estimatedRowHeight = 144
  71. self.tableView.backgroundColor = UIColor(hex: "#f3f3f3")
  72. self.messageInputView.delegate = self
  73. //底部安全距离 老机型没有
  74. self.bottomBarHeight = Int(iPhoneX ? 64 + IPHONEX_BOTTOM_SAFE_HEIGHT: 64) + self.bottomToolbarHeight
  75. self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
  76. self.bottomBar.topBorder(width: 1, borderColor: base_gray_color.alpha(0.5))
  77. self.messageInputView.backgroundColor = base_gray_color
  78. //标题
  79. if self.conversation?.type == o2_im_conversation_type_single {
  80. if let c = self.conversation {
  81. var person = ""
  82. c.personList?.forEach({ (p) in
  83. if p != O2AuthSDK.shared.myInfo()?.distinguishedName {
  84. person = p
  85. }
  86. })
  87. if !person.isEmpty {
  88. self.title = person.split("@").first ?? ""
  89. }
  90. }
  91. } else {
  92. self.title = self.conversation?.title
  93. }
  94. //群会话 添加修改标题的按钮
  95. if self.conversation?.type == o2_im_conversation_type_group &&
  96. O2AuthSDK.shared.myInfo()?.distinguishedName == self.conversation?.adminPerson {
  97. navigationItem.rightBarButtonItem = UIBarButtonItem(title: "修改", style: .plain, target: self, action: #selector(clickUpdate))
  98. }
  99. //获取聊天数据
  100. self.loadMsgList(page: page)
  101. //阅读
  102. self.viewModel.readConversation(conversationId: self.conversation?.id)
  103. }
  104. override func viewWillAppear(_ animated: Bool) {
  105. NotificationCenter.default.addObserver(self, selector: #selector(receiveMessageFromWs(notice:)), name: OONotification.websocket.notificationName, object: nil)
  106. }
  107. override func viewWillDisappear(_ animated: Bool) {
  108. NotificationCenter.default.removeObserver(self)
  109. }
  110. @objc private func receiveMessageFromWs(notice: Notification) {
  111. DDLogDebug("接收到websocket im 消息")
  112. if let message = notice.object as? IMMessageInfo {
  113. if message.conversationId == self.conversation?.id {
  114. self.chatMessageList.append(message)
  115. self.scrollMessageToBottom()
  116. self.viewModel.readConversation(conversationId: self.conversation?.id)
  117. }
  118. }
  119. }
  120. @objc private func clickUpdate() {
  121. self.showSheetAction(title: "", message: "选择要修改的项", actions: [
  122. UIAlertAction(title: "修改群名", style: .default, handler: { (action) in
  123. self.updateTitle()
  124. }),
  125. UIAlertAction(title: "修改成员", style: .default, handler: { (action) in
  126. self.updatePeople()
  127. })
  128. ])
  129. }
  130. private func updateTitle() {
  131. self.showPromptAlert(title: "", message: "修改群名", inputText: "") { (action, result) in
  132. if result.isEmpty {
  133. self.showError(title: "请输入群名")
  134. }else {
  135. self.showLoading()
  136. self.viewModel.updateConversationTitle(id: (self.conversation?.id!)!, title: result)
  137. .then { (c) in
  138. self.title = result
  139. self.conversation?.title = result
  140. self.showSuccess(title: "修改成功")
  141. }.catch { (err) in
  142. DDLogError(err.localizedDescription)
  143. self.showError(title: "修改失败")
  144. }
  145. }
  146. }
  147. }
  148. private func updatePeople() {
  149. //选择人员 反选已经存在的成员
  150. if let users = self.conversation?.personList {
  151. self.showContactPicker(modes: [.person], callback: { (result) in
  152. if let people = result.users {
  153. if people.count >= 3 {
  154. var peopleDNs: [String] = []
  155. var containMe = false
  156. people.forEach { (item) in
  157. peopleDNs.append(item.distinguishedName!)
  158. if O2AuthSDK.shared.myInfo()?.distinguishedName == item.distinguishedName {
  159. containMe = true
  160. }
  161. }
  162. if !containMe {
  163. peopleDNs.append((O2AuthSDK.shared.myInfo()?.distinguishedName)!)
  164. }
  165. self.showLoading()
  166. self.viewModel.updateConversationPeople(id: (self.conversation?.id!)!, users: peopleDNs)
  167. .then { (c) in
  168. self.conversation?.personList = peopleDNs
  169. self.showSuccess(title: "修改成功")
  170. }.catch { (err) in
  171. DDLogError(err.localizedDescription)
  172. self.showError(title: "修改失败")
  173. }
  174. }else {
  175. self.showError(title: "选择人数不足3人")
  176. }
  177. }else {
  178. self.showError(title: "请选择人员")
  179. }
  180. }, initUserPickedArray: users)
  181. }else {
  182. self.showError(title: "成员列表数据错误!")
  183. }
  184. }
  185. //获取消息
  186. private func loadMsgList(page: Int) {
  187. if let c = self.conversation, let id = c.id {
  188. self.viewModel.myMsgPageList(page: page, conversationId: id).then { (list) in
  189. self.chatMessageList = list
  190. self.scrollMessageToBottom()
  191. }
  192. } else {
  193. self.showError(title: "参数错误!!!")
  194. }
  195. }
  196. //刷新tableview 滚动到底部
  197. private func scrollMessageToBottom() {
  198. DispatchQueue.main.async {
  199. self.tableView.reloadData()
  200. if self.chatMessageList.count > 0 {
  201. self.tableView.scrollToRow(at: IndexPath(row: self.chatMessageList.count - 1, section: 0), at: .bottom, animated: true)
  202. }
  203. }
  204. }
  205. //发送文本消息
  206. private func sendTextMessage() {
  207. guard let msg = self.messageInputView.text else {
  208. return
  209. }
  210. self.messageInputView.text = ""
  211. let body = IMMessageBodyInfo()
  212. body.type = o2_im_msg_type_text
  213. body.body = msg
  214. sendMessage(body: body)
  215. }
  216. //发送表情消息
  217. private func sendEmojiMessage(emoji: String) {
  218. let body = IMMessageBodyInfo()
  219. body.type = o2_im_msg_type_emoji
  220. body.body = emoji
  221. sendMessage(body: body)
  222. }
  223. //发送地图消息消息
  224. private func sendLocationMessage(loc: O2LocationData) {
  225. let body = IMMessageBodyInfo()
  226. body.type = o2_im_msg_type_location
  227. body.body = o2_im_msg_body_location
  228. body.address = loc.address
  229. body.addressDetail = loc.addressDetail
  230. body.longitude = loc.longitude
  231. body.latitude = loc.latitude
  232. sendMessage(body: body)
  233. }
  234. //发送消息到服务器
  235. private func sendMessage(body: IMMessageBodyInfo) {
  236. let message = IMMessageInfo()
  237. message.body = body.toJSONString()
  238. message.id = UUID().uuidString
  239. message.conversationId = self.conversation?.id
  240. message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
  241. message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
  242. //添加到界面
  243. self.chatMessageList.append(message)
  244. self.scrollMessageToBottom()
  245. //发送消息到服务器
  246. self.viewModel.sendMsg(msg: message)
  247. .then { (result) in
  248. DDLogDebug("发送消息成功 \(result)")
  249. self.viewModel.readConversation(conversationId: self.conversation?.id)
  250. }.catch { (error) in
  251. DDLogError(error.localizedDescription)
  252. self.showError(title: "发送消息失败!")
  253. }
  254. }
  255. //选择照片
  256. private func chooseImage() {
  257. let vc = FileBSImagePickerViewController().bsImagePicker()
  258. vc.settings.fetch.assets.supportedMediaTypes = [.image]
  259. presentImagePicker(vc, select: { (asset) in
  260. //选中一个
  261. }, deselect: { (asset) in
  262. //取消选中一个
  263. }, cancel: { (assets) in
  264. //取消
  265. }, finish: { (assets) in
  266. //结果
  267. if assets.count > 0 {
  268. switch assets[0].mediaType {
  269. case .image:
  270. let options = PHImageRequestOptions()
  271. options.isSynchronous = true
  272. options.deliveryMode = .fastFormat
  273. options.resizeMode = .none
  274. PHImageManager.default().requestImageData(for: assets[0], options: options) { (imageData, result, imageOrientation, dict) in
  275. guard let data = imageData else {
  276. return
  277. }
  278. var newData = data
  279. //处理图片旋转的问题
  280. if imageOrientation != UIImage.Orientation.up {
  281. let newImage = UIImage(data: data)?.fixOrientation()
  282. if newImage != nil {
  283. newData = newImage!.pngData()!
  284. }
  285. }
  286. var fileName = ""
  287. if dict?["PHImageFileURLKey"] != nil {
  288. let fileURL = dict?["PHImageFileURLKey"] as! URL
  289. fileName = fileURL.lastPathComponent
  290. } else {
  291. fileName = "\(UUID().uuidString).png"
  292. }
  293. let localFilePath = self.storageLocalImage(imageData: newData, fileName: fileName)
  294. let msgId = self.prepareForSendImageMsg(filePath: localFilePath)
  295. self.uploadFileAndSendMsg(messageId: msgId, data: newData, fileName: fileName, type: o2_im_msg_type_image)
  296. }
  297. break
  298. default:
  299. //
  300. DDLogError("不支持的类型")
  301. self.showError(title: "不支持的类型!")
  302. break
  303. }
  304. }
  305. }, completion: nil)
  306. }
  307. //临时存储本地
  308. private func storageLocalImage(imageData: Data, fileName: String) -> String {
  309. let fileTempPath = FileUtil.share.cacheDir().appendingPathComponent(fileName)
  310. do {
  311. try imageData.write(to: fileTempPath)
  312. return fileTempPath.path
  313. } catch {
  314. print(error.localizedDescription)
  315. return fileTempPath.path
  316. }
  317. }
  318. //发送消息前 先载入界面
  319. private func prepareForSendImageMsg(filePath: String) -> String {
  320. let body = IMMessageBodyInfo()
  321. body.type = o2_im_msg_type_image
  322. body.body = o2_im_msg_body_image
  323. body.fileTempPath = filePath
  324. let message = IMMessageInfo()
  325. let msgId = UUID().uuidString
  326. message.body = body.toJSONString()
  327. message.id = msgId
  328. message.conversationId = self.conversation?.id
  329. message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
  330. message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
  331. //添加到界面
  332. self.chatMessageList.append(message)
  333. self.scrollMessageToBottom()
  334. return msgId
  335. }
  336. //发送消息前 先载入界面
  337. private func prepareForSendFileMsg(tempMessage: IMMessageBodyInfo) -> String {
  338. let message = IMMessageInfo()
  339. let msgId = UUID().uuidString
  340. message.body = tempMessage.toJSONString()
  341. message.id = msgId
  342. message.conversationId = self.conversation?.id
  343. message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
  344. message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
  345. //添加到界面
  346. self.chatMessageList.append(message)
  347. self.scrollMessageToBottom()
  348. return msgId
  349. }
  350. //上传图片 音频 等文件到服务器并发送消息
  351. private func uploadFileAndSendMsg(messageId: String, data: Data, fileName: String, type: String) {
  352. guard let cId = self.conversation?.id else {
  353. return
  354. }
  355. self.viewModel.uploadFile(conversationId: cId, type: type, fileName: fileName, file: data).then { back in
  356. DDLogDebug("上传文件成功")
  357. guard let message = self.chatMessageList.first (where: { (info) -> Bool in
  358. return info.id == messageId
  359. }) else {
  360. DDLogDebug("没有找到对应的消息")
  361. return
  362. }
  363. let body = IMMessageBodyInfo.deserialize(from: message.body)
  364. body?.fileId = back.id
  365. body?.fileExtension = back.fileExtension
  366. body?.fileTempPath = nil
  367. message.body = body?.toJSONString()
  368. //发送消息到服务器
  369. self.viewModel.sendMsg(msg: message)
  370. .then { (result) in
  371. DDLogDebug("消息 发送成功 \(result)")
  372. self.viewModel.readConversation(conversationId: self.conversation?.id)
  373. }.catch { (error) in
  374. DDLogError(error.localizedDescription)
  375. self.showError(title: "发送消息失败!")
  376. }
  377. }.catch { err in
  378. self.showError(title: "上传错误,\(err.localizedDescription)")
  379. }
  380. }
  381. // MARK: - IBAction
  382. //点击表情按钮
  383. @IBAction func clickEmojiBtn(_ sender: UIButton) {
  384. self.isShowEmoji.toggle()
  385. self.view.endEditing(true)
  386. if self.isShowEmoji {
  387. //audio view 先关闭
  388. self.isShowAudioView = false
  389. self.audioBtnView.removeFromSuperview()
  390. //开始添加emojiBar
  391. self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + self.emojiBarHeight.toCGFloat
  392. self.emojiBar.delegate = self
  393. self.emojiBar.translatesAutoresizingMaskIntoConstraints = false
  394. self.bottomBar.addSubview(self.emojiBar)
  395. let top = NSLayoutConstraint(item: self.emojiBar, attribute: .top, relatedBy: .equal, toItem: self.emojiBar.superview!, attribute: .top, multiplier: 1, constant: CGFloat(self.bottomBarHeight))
  396. let width = NSLayoutConstraint(item: self.emojiBar, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: SCREEN_WIDTH)
  397. let height = NSLayoutConstraint(item: self.emojiBar, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.emojiBarHeight.toCGFloat)
  398. NSLayoutConstraint.activate([top, width, height])
  399. } else {
  400. self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
  401. self.emojiBar.removeFromSuperview()
  402. }
  403. self.view.layoutIfNeeded()
  404. }
  405. @IBAction func micBtnClick(_ sender: UIButton) {
  406. DDLogDebug("点击了麦克风按钮")
  407. self.isShowAudioView.toggle()
  408. self.view.endEditing(true)
  409. if self.isShowAudioView {
  410. //emoji view 先关闭
  411. self.isShowEmoji = false
  412. self.emojiBar.removeFromSuperview()
  413. //开始添加emojiBar
  414. self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + self.emojiBarHeight.toCGFloat
  415. self.audioBtnView.translatesAutoresizingMaskIntoConstraints = false
  416. self.bottomBar.addSubview(self.audioBtnView)
  417. let top = NSLayoutConstraint(item: self.audioBtnView, attribute: .top, relatedBy: .equal, toItem: self.audioBtnView.superview!, attribute: .top, multiplier: 1, constant: CGFloat(self.bottomBarHeight))
  418. let width = NSLayoutConstraint(item: self.audioBtnView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: SCREEN_WIDTH)
  419. let height = NSLayoutConstraint(item: self.audioBtnView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.emojiBarHeight.toCGFloat)
  420. NSLayoutConstraint.activate([top, width, height])
  421. } else {
  422. self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
  423. self.audioBtnView.removeFromSuperview()
  424. }
  425. self.view.layoutIfNeeded()
  426. }
  427. @IBAction func imgBtnClick(_ sender: UIButton) {
  428. DDLogDebug("点击了图片按钮")
  429. self.chooseImage()
  430. }
  431. @IBAction func cameraBtnClick(_ sender: UIButton) {
  432. DDLogDebug("点击了相机按钮")
  433. self.takePhoto(delegate: self)
  434. }
  435. @IBAction func locationBtnClick(_ sender: UIButton) {
  436. DDLogDebug("点击了位置按钮")
  437. let vc = IMLocationChooseController.openChooseLocation { (data) in
  438. self.sendLocationMessage(loc: data)
  439. }
  440. self.navigationController?.pushViewController(vc, animated: false)
  441. }
  442. }
  443. // MARK: - 录音delegate
  444. extension IMChatViewController: IMChatAudioViewDelegate {
  445. func showAudioRecordingView() {
  446. if self.voiceIconImage == nil {
  447. self.voiceImageSuperView = UIView()
  448. self.view.addSubview(self.voiceImageSuperView!)
  449. self.voiceImageSuperView?.backgroundColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.6)
  450. self.voiceImageSuperView?.snp_makeConstraints { (make) in
  451. make.center.equalTo(self.view)
  452. make.size.equalTo(CGSize(width:140, height:140))
  453. }
  454. self.voiceIconImage = UIImageView()
  455. self.voiceImageSuperView?.addSubview(self.voiceIconImage!)
  456. self.voiceIconImage?.snp_makeConstraints { (make) in
  457. make.top.left.equalTo(self.voiceImageSuperView!).inset(UIEdgeInsets(top: 20, left: 35, bottom: 0, right: 0))
  458. make.size.equalTo(CGSize(width: 70, height: 70))
  459. }
  460. let voiceIconTitleLabel = UILabel()
  461. self.voiceIocnTitleLable = voiceIconTitleLabel
  462. self.voiceIconImage?.addSubview(voiceIconTitleLabel)
  463. voiceIconTitleLabel.textColor = UIColor.white
  464. voiceIconTitleLabel.font = .systemFont(ofSize: 12)
  465. voiceIconTitleLabel.text = "松开发送,上滑取消"
  466. voiceIconTitleLabel.snp_makeConstraints { (make) in
  467. make.bottom.equalTo(self.voiceImageSuperView!).offset(-15)
  468. make.centerX.equalTo(self.voiceImageSuperView!)
  469. }
  470. }
  471. self.voiceImageSuperView?.isHidden = false
  472. self.voiceIconImage?.image = UIImage(named: "chat_audio_voice")
  473. self.voiceIocnTitleLable?.text = "松开发送,上滑取消";
  474. }
  475. func hideAudioRecordingView() {
  476. self.voiceImageSuperView?.isHidden = true
  477. }
  478. func changeRecordingView2uplide() {
  479. self.voiceIocnTitleLable?.text = "松开手指,取消发送";
  480. self.voiceIconImage?.image = UIImage(named: "chat_audio_cancel")
  481. }
  482. func changeRecordingView2down() {
  483. self.voiceIconImage?.image = UIImage(named: "chat_audio_voice")
  484. self.voiceIocnTitleLable?.text = "松开发送,上滑取消";
  485. }
  486. func sendVoice(path: String, voice: Data, duration: String) {
  487. let msg = IMMessageBodyInfo()
  488. msg.fileTempPath = path
  489. msg.body = o2_im_msg_body_audio
  490. msg.type = o2_im_msg_type_audio
  491. msg.audioDuration = duration
  492. let msgId = self.prepareForSendFileMsg(tempMessage: msg)
  493. let fileName = path.split("/").last ?? "MySound.ilbc"
  494. DDLogDebug("音频文件:\(fileName)")
  495. self.uploadFileAndSendMsg(messageId: msgId, data: voice, fileName: fileName, type: o2_im_msg_type_audio)
  496. }
  497. }
  498. // MARK: - 拍照delegate
  499. extension IMChatViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
  500. func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
  501. if let image = info[.editedImage] as? UIImage {
  502. let fileName = "\(UUID().uuidString).png"
  503. let newData = image.pngData()!
  504. let localFilePath = self.storageLocalImage(imageData: newData, fileName: fileName)
  505. let msgId = self.prepareForSendImageMsg(filePath: localFilePath)
  506. self.uploadFileAndSendMsg(messageId: msgId, data: newData, fileName: fileName, type: o2_im_msg_type_image)
  507. } else {
  508. DDLogError("没有选择到图片!")
  509. }
  510. picker.dismiss(animated: true, completion: nil)
  511. // var newData = data
  512. // //处理图片旋转的问题
  513. // if imageOrientation != UIImage.Orientation.up {
  514. // let newImage = UIImage(data: data)?.fixOrientation()
  515. // if newImage != nil {
  516. // newData = newImage!.pngData()!
  517. // }
  518. // }
  519. // var fileName = ""
  520. // if dict?["PHImageFileURLKey"] != nil {
  521. // let fileURL = dict?["PHImageFileURLKey"] as! URL
  522. // fileName = fileURL.lastPathComponent
  523. // } else {
  524. // fileName = "\(UUID().uuidString).png"
  525. // }
  526. }
  527. }
  528. // MARK: - 图片消息点击 delegate
  529. extension IMChatViewController: IMChatMessageDelegate {
  530. func openApplication(storyboard: String) {
  531. if storyboard == "mind" {
  532. let flutterViewController = O2FlutterViewController()
  533. flutterViewController.setInitialRoute("mindMap")
  534. self.present(flutterViewController, animated: false, completion: nil)
  535. }else {
  536. let storyBoard = UIStoryboard(name: storyboard, bundle: nil)
  537. guard let destVC = storyBoard.instantiateInitialViewController() else {
  538. return
  539. }
  540. destVC.modalPresentationStyle = .fullScreen
  541. if destVC.isKind(of: ZLNavigationController.self) {
  542. self.show(destVC, sender: nil)
  543. }else{
  544. self.navigationController?.pushViewController(destVC, animated: true)
  545. }
  546. }
  547. }
  548. func openWork(workId: String) {
  549. let storyBoard = UIStoryboard(name: "task", bundle: nil)
  550. let destVC = storyBoard.instantiateViewController(withIdentifier: "todoTaskDetailVC") as! TodoTaskDetailViewController
  551. let json = """
  552. {"work":"\(workId)", "workCompleted":"", "title":""}
  553. """
  554. let todo = TodoTask(JSONString: json)
  555. destVC.todoTask = todo
  556. destVC.backFlag = 3 //隐藏就行
  557. self.show(destVC, sender: nil)
  558. }
  559. func openLocatinMap(info: IMMessageBodyInfo) {
  560. let map = IMShowLocationViewController()
  561. map.address = info.address
  562. map.addressDetail = info.addressDetail
  563. map.latitude = info.latitude
  564. map.longitude = info.longitude
  565. self.navigationController?.pushViewController(map, animated: false)
  566. }
  567. func clickImageMessage(info: IMMessageBodyInfo) {
  568. if let id = info.fileId {
  569. self.showLoading()
  570. var ext = info.fileExtension ?? "png"
  571. if ext.isEmpty {
  572. ext = "png"
  573. }
  574. O2IMFileManager.shared
  575. .getFileLocalUrl(fileId: id, fileExtension: ext)
  576. .always {
  577. self.hideLoading()
  578. }.then { (path) in
  579. let currentURL = NSURL(fileURLWithPath: path.path)
  580. DDLogDebug(currentURL.description)
  581. DDLogDebug(path.path)
  582. if QLPreviewController.canPreview(currentURL) {
  583. self.previewVC.currentFileURLS.removeAll()
  584. self.previewVC.currentFileURLS.append(currentURL)
  585. self.previewVC.reloadData()
  586. self.pushVC(self.previewVC)
  587. } else {
  588. self.showError(title: "当前文件类型不支持预览!")
  589. }
  590. }
  591. .catch { (error) in
  592. DDLogError(error.localizedDescription)
  593. self.showError(title: "获取文件异常!")
  594. }
  595. } else if let temp = info.fileTempPath {
  596. let currentURL = NSURL(fileURLWithPath: temp)
  597. DDLogDebug(currentURL.description)
  598. DDLogDebug(temp)
  599. if QLPreviewController.canPreview(currentURL) {
  600. self.previewVC.currentFileURLS.removeAll()
  601. self.previewVC.currentFileURLS.append(currentURL)
  602. self.previewVC.reloadData()
  603. self.pushVC(self.previewVC)
  604. } else {
  605. self.showError(title: "当前文件类型不支持预览!")
  606. }
  607. }
  608. }
  609. }
  610. // MARK: - 表情点击 delegate
  611. extension IMChatViewController: IMChatEmojiBarClickDelegate {
  612. func clickEmoji(emoji: String) {
  613. DDLogDebug("发送表情消息 \(emoji)")
  614. self.sendEmojiMessage(emoji: emoji)
  615. }
  616. }
  617. // MARK: - tableview delegate
  618. extension IMChatViewController: UITableViewDelegate, UITableViewDataSource {
  619. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  620. return self.chatMessageList.count
  621. }
  622. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  623. let msg = self.chatMessageList[indexPath.row]
  624. if msg.createPerson == O2AuthSDK.shared.myInfo()?.distinguishedName { //发送者
  625. if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageSendViewCell", for: indexPath) as? IMChatMessageSendViewCell {
  626. cell.setContent(item: self.chatMessageList[indexPath.row])
  627. cell.delegate = self
  628. return cell
  629. }
  630. } else {
  631. if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageViewCell", for: indexPath) as? IMChatMessageViewCell {
  632. cell.setContent(item: self.chatMessageList[indexPath.row])
  633. cell.delegate = self
  634. return cell
  635. }
  636. }
  637. return UITableViewCell()
  638. }
  639. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  640. let msg = self.chatMessageList[indexPath.row]
  641. return cellHeight(item: msg)
  642. }
  643. func cellHeight(item: IMMessageInfo) -> CGFloat {
  644. if let jsonBody = item.body, let body = IMMessageBodyInfo.deserialize(from: jsonBody){
  645. if body.type == o2_im_msg_type_emoji {
  646. // 上边距 69 + emoji高度 + 内边距 + 底部空白高度
  647. return 69 + 36 + 20 + 10
  648. } else if body.type == o2_im_msg_type_image {
  649. // 上边距 69 + 图片高度 + 内边距 + 底部空白高度
  650. return 69 + 192 + 20 + 10
  651. } else if o2_im_msg_type_audio == body.type {
  652. // 上边距 69 + audio高度 + 内边距 + 底部空白高度
  653. return 69 + IMAudioView.IMAudioView_height + 20 + 10
  654. } else if o2_im_msg_type_location == body.type {
  655. // 上边距 69 + 位置图高度 + 内边距 + 底部空白高度
  656. return 69 + IMLocationView.IMLocationViewHeight + 20 + 10
  657. } else {
  658. let size = body.body!.getSizeWithMaxWidth(fontSize: 16, maxWidth: messageWidth)
  659. // 上边距 69 + 文字高度 + 内边距 + 底部空白高度
  660. return 69 + size.height + 28 + 10
  661. }
  662. }
  663. return 132
  664. }
  665. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  666. tableView.deselectRow(at: indexPath, animated: false)
  667. }
  668. }
  669. // MARK: - textField delegate
  670. extension IMChatViewController: UITextFieldDelegate {
  671. func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
  672. DDLogDebug("准备开始输入......")
  673. closeOtherView()
  674. return true
  675. }
  676. private func closeOtherView() {
  677. self.isShowEmoji = false
  678. self.isShowAudioView = false
  679. self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
  680. self.view.layoutIfNeeded()
  681. }
  682. func textFieldShouldReturn(_ textField: UITextField) -> Bool {
  683. DDLogDebug("回车。。。。")
  684. self.sendTextMessage()
  685. return true
  686. }
  687. }