IMChatMessageViewCell.swift 14 KB

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