IMChatMessageViewCell.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //
  2. // IMChatMessageViewCell.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. protocol IMChatMessageDelegate {
  11. func clickImageMessage(fileId: String?, tempPath: String?)
  12. }
  13. class IMChatMessageViewCell: UITableViewCell {
  14. @IBOutlet weak var avatarImage: UIImageView!
  15. @IBOutlet weak var titleLabel: UILabel!
  16. @IBOutlet weak var timeLabel: UILabel!
  17. @IBOutlet weak var messageBackgroundView: UIView!
  18. @IBOutlet weak var messageBackgroundWidth: NSLayoutConstraint!
  19. @IBOutlet weak var messageBackgroundHeight: NSLayoutConstraint!
  20. private let messageWidth = 176
  21. private lazy var audioView: IMAudioView = {
  22. let view = Bundle.main.loadNibNamed("IMAudioView", owner: self, options: nil)?.first as! IMAudioView
  23. view.frame = CGRect(x: 0, y: 0, width: IMAudioView.IMAudioView_width, height: IMAudioView.IMAudioView_height)
  24. return view
  25. }()
  26. var delegate: IMChatMessageDelegate?
  27. override func awakeFromNib() {
  28. super.awakeFromNib()
  29. }
  30. override func setSelected(_ selected: Bool, animated: Bool) {
  31. super.setSelected(selected, animated: animated)
  32. }
  33. //普通通知消息
  34. func setInstantContent(item: InstantMessage) {
  35. if let time = item.createTime {
  36. let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
  37. self.timeLabel.text = date.friendlyTime()
  38. }
  39. if let msg = item.title {
  40. textMsgRender(msg: msg)
  41. }
  42. if let type = item.type {
  43. if type.starts(with: "task_") {
  44. self.avatarImage.image = UIImage(named: "icon_daiban")
  45. self.titleLabel.text = "待办消息"
  46. } else if type.starts(with: "taskCompleted_") {
  47. self.avatarImage.image = UIImage(named: "icon_taskcompleted")
  48. self.titleLabel.text = "已办消息"
  49. } else if type.starts(with: "read_") {
  50. self.avatarImage.image = UIImage(named: "icon_read")
  51. self.titleLabel.text = "待阅消息"
  52. } else if type.starts(with: "readCompleted_") {
  53. self.avatarImage.image = UIImage(named: "icon_readcompleted")
  54. self.titleLabel.text = "已阅消息"
  55. } else if type.starts(with: "review_") || type.starts(with: "work_") || type.starts(with: "process_") {
  56. self.avatarImage.image = UIImage(named: "icon_daiban")
  57. self.titleLabel.text = "工作消息"
  58. } else if type.starts(with: "meeting_") {
  59. self.avatarImage.image = UIImage(named: "icon_meeting")
  60. self.titleLabel.text = "会议消息"
  61. } else if type.starts(with: "attachment_") {
  62. self.avatarImage.image = UIImage(named: "icon_yunpan")
  63. self.titleLabel.text = "云盘消息"
  64. } else if type.starts(with: "calendar_") {
  65. self.avatarImage.image = UIImage(named: "icon_calendar")
  66. self.titleLabel.text = "日历消息"
  67. } else if type.starts(with: "cms_") {
  68. self.avatarImage.image = UIImage(named: "icon_cms")
  69. self.titleLabel.text = "信息中心消息"
  70. } else if type.starts(with: "bbs_") {
  71. self.avatarImage.image = UIImage(named: "icon_bbs")
  72. self.titleLabel.text = "论坛消息"
  73. } else if type.starts(with: "mind_") {
  74. self.avatarImage.image = UIImage(named: "icon_mindMap")
  75. self.titleLabel.text = "脑图消息"
  76. } else {
  77. self.avatarImage.image = UIImage(named: "icon_email")
  78. self.titleLabel.text = "其他消息"
  79. }
  80. }
  81. }
  82. //聊天消息
  83. func setContent(item: IMMessageInfo) {
  84. //time
  85. if let time = item.createTime {
  86. let date = time.toDate(formatter: "yyyy-MM-dd HH:mm:ss")
  87. self.timeLabel.text = date.friendlyTime()
  88. }
  89. //name avatart
  90. if let person = item.createPerson {
  91. let urlstr = AppDelegate.o2Collect.generateURLWithAppContextKey(ContactContext.contactsContextKeyV2, query: ContactContext.personIconByNameQueryV2, parameter: ["##name##": person as AnyObject], generateTime: false)
  92. if let u = URL(string: urlstr!) {
  93. self.avatarImage.hnk_setImageFromURL(u)
  94. } else {
  95. self.avatarImage.image = UIImage(named: "icon_men")
  96. }
  97. //姓名
  98. self.titleLabel.text = person.split("@").first ?? ""
  99. } else {
  100. self.avatarImage.image = UIImage(named: "icon_men")
  101. self.titleLabel.text = ""
  102. }
  103. self.messageBackgroundView.removeSubviews()
  104. if let jsonBody = item.body, let body = parseJson(msg: jsonBody) {
  105. if body.type == o2_im_msg_type_emoji {
  106. emojiMsgRender(emoji: body.body!)
  107. } else if body.type == o2_im_msg_type_image {
  108. imageMsgRender(info: body)
  109. } else if o2_im_msg_type_audio == body.type {
  110. audioMsgRender(info: body)
  111. } else {
  112. textMsgRender(msg: body.body!)
  113. }
  114. }
  115. }
  116. //音频消息
  117. private func audioMsgRender(info: IMMessageBodyInfo) {
  118. self.messageBackgroundWidth.constant = IMAudioView.IMAudioView_width + 20
  119. self.messageBackgroundHeight.constant = IMAudioView.IMAudioView_height + 20
  120. self.audioView.translatesAutoresizingMaskIntoConstraints = false
  121. self.messageBackgroundView.addSubview(self.audioView)
  122. self.audioView.setDuration(duration: info.audioDuration ?? "0")
  123. //音频文件
  124. if let fileId = info.fileId {
  125. let urlStr = AppDelegate.o2Collect.generateURLWithAppContextKey(
  126. CommunicateContext.communicateContextKey,
  127. query: CommunicateContext.imDownloadFileQuery,
  128. parameter: ["##id##": fileId as AnyObject], generateTime: false)
  129. self.audioView.setPlayUrl(url: urlStr)
  130. } else if let filePath = info.fileTempPath {
  131. self.audioView.setPlayUrl(url: filePath)
  132. }
  133. self.audioView.addTapGesture { (tap) in
  134. self.playAudio(info: info)
  135. }
  136. self.constraintWithContent(contentView: self.audioView)
  137. }
  138. private func playAudio(info: IMMessageBodyInfo) {
  139. if let fileId = info.fileId {
  140. O2IMFileManager.shared.getFileLocalUrl(fileId: fileId)
  141. .then { (url) in
  142. do {
  143. let data = try Data(contentsOf: url)
  144. AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
  145. } catch {
  146. DDLogError(error.localizedDescription)
  147. }
  148. }.catch { (e) in
  149. DDLogError(e.localizedDescription)
  150. }
  151. } else if let filePath = info.fileTempPath {
  152. do {
  153. let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
  154. AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
  155. } catch {
  156. DDLogError(error.localizedDescription)
  157. }
  158. }
  159. }
  160. private func constraintWithContent(contentView: UIView) {
  161. let top = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: contentView.superview!, attribute: .top, multiplier: 1, constant: 10)
  162. let bottom = NSLayoutConstraint(item: contentView.superview!, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 10)
  163. let left = NSLayoutConstraint(item: contentView, attribute: .leading, relatedBy: .equal, toItem: contentView.superview!, attribute: .leading, multiplier: 1, constant: 10)
  164. let right = NSLayoutConstraint(item: contentView.superview!, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 10)
  165. NSLayoutConstraint.activate([top, bottom, left, right])
  166. }
  167. //图片消息
  168. private func imageMsgRender(info: IMMessageBodyInfo) {
  169. let width: CGFloat = 144
  170. let height: CGFloat = 192
  171. self.messageBackgroundWidth.constant = width + 20
  172. self.messageBackgroundHeight.constant = height + 20
  173. //图片
  174. let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
  175. if let fileId = info.fileId {
  176. DDLogDebug("file id :\(fileId)")
  177. let urlStr = AppDelegate.o2Collect.generateURLWithAppContextKey(
  178. CommunicateContext.communicateContextKey,
  179. query: CommunicateContext.imDownloadImageWithSizeQuery,
  180. parameter: ["##id##": fileId as AnyObject,
  181. "##width##": "144" as AnyObject,
  182. "##height##": "192" as AnyObject], generateTime: false)
  183. if let url = URL(string: urlStr!) {
  184. imageView.hnk_setImageFromURL(url)
  185. } else {
  186. imageView.image = UIImage(named: "chat_image")
  187. }
  188. } else if let filePath = info.fileTempPath {
  189. DDLogDebug("filePath :\(filePath)")
  190. imageView.hnk_setImageFromFile(filePath)
  191. } else {
  192. imageView.image = UIImage(named: "chat_image")
  193. }
  194. imageView.translatesAutoresizingMaskIntoConstraints = false
  195. self.messageBackgroundView.addSubview(imageView)
  196. imageView.addTapGesture { (tap) in
  197. self.delegate?.clickImageMessage(fileId: info.fileId, tempPath: info.fileTempPath)
  198. }
  199. self.constraintWithContent(contentView: imageView)
  200. }
  201. private func emojiMsgRender(emoji: String) {
  202. let emojiSize = 36
  203. let width = CGFloat(emojiSize + 20)
  204. let height = CGFloat(emojiSize + 20)
  205. self.messageBackgroundWidth.constant = width
  206. self.messageBackgroundHeight.constant = height
  207. //背景图片
  208. let bgImg = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
  209. let insets = UIEdgeInsets(top: 28, left: 10, bottom: 5, right: 5); // 上、左、下、右
  210. var bubble = UIImage(named: "chat_bubble_incomming")
  211. bubble = bubble?.resizableImage(withCapInsets: insets, resizingMode: .stretch)
  212. bgImg.image = bubble
  213. self.messageBackgroundView.addSubview(bgImg)
  214. //表情图
  215. let emojiImage = UIImageView(frame: CGRect(x: 0, y: 0, width: emojiSize, height: emojiSize))
  216. let bundle = Bundle().o2EmojiBundle(anyClass: IMChatMessageViewCell.self)
  217. let path = o2ImEmojiPath(emojiBody: emoji)
  218. emojiImage.image = UIImage(named: path, in: bundle, compatibleWith: nil)
  219. emojiImage.translatesAutoresizingMaskIntoConstraints = false
  220. self.messageBackgroundView.addSubview(emojiImage)
  221. self.constraintWithContent(contentView: emojiImage)
  222. }
  223. private func textMsgRender(msg: String) {
  224. let size = calTextSize(str: msg)
  225. self.messageBackgroundWidth.constant = size.width + 20
  226. self.messageBackgroundHeight.constant = size.height + 20
  227. //背景图片
  228. let bgImg = UIImageView(frame: CGRect(x: 0, y: 0, width: size.width + 20, height: size.height + 20))
  229. let insets = UIEdgeInsets(top: 28, left: 10, bottom: 5, right: 5); // 上、左、下、右
  230. var bubble = UIImage(named: "chat_bubble_incomming")
  231. bubble = bubble?.resizableImage(withCapInsets: insets, resizingMode: .stretch)
  232. bgImg.image = bubble
  233. self.messageBackgroundView.addSubview(bgImg)
  234. //文字
  235. let label = generateMessagelabel(str: msg, size: size)
  236. label.translatesAutoresizingMaskIntoConstraints = false
  237. self.messageBackgroundView.addSubview(label)
  238. let top = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: label.superview!, attribute: .top, multiplier: 1, constant: 10)
  239. let left = NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: label.superview!, attribute: .leading, multiplier: 1, constant: 10)
  240. let right = NSLayoutConstraint(item: label.superview!, attribute: .trailing, relatedBy: .equal, toItem: label, attribute: .trailing, multiplier: 1, constant: 10)
  241. NSLayoutConstraint.activate([top, left, right])
  242. }
  243. private func generateMessagelabel(str: String, size: CGSize) -> UILabel {
  244. let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
  245. label.text = str
  246. label.font = UIFont.systemFont(ofSize: 16)
  247. label.numberOfLines = 0
  248. label.lineBreakMode = .byCharWrapping
  249. label.preferredMaxLayoutWidth = size.width
  250. return label
  251. }
  252. private func calTextSize(str: String) -> CGSize {
  253. let size = CGSize(width: messageWidth.toCGFloat, height: CGFloat(MAXFLOAT))
  254. return str.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], context: nil).size
  255. }
  256. //解析json为消息对象
  257. private func parseJson(msg: String) -> IMMessageBodyInfo? {
  258. return IMMessageBodyInfo.deserialize(from: msg)
  259. }
  260. }