IMChatMessageViewCell.swift 14 KB

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