TSChatViewController.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. //
  2. // MIT License
  3. //
  4. // Copyright (c) 2017-2020 MessageKit
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in all
  14. // copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. // SOFTWARE.
  23. import MessageKit
  24. import UIKit
  25. class TSChatViewController: MessagesViewController, MessagesDataSource {
  26. var deleteBlock:(()->Void)?
  27. //数据
  28. var viewModel:TSAIChatVM = TSAIChatVM()
  29. lazy var messageList: [TSChatMessage] = []
  30. //导航栏
  31. // lazy var vipBtn: UIButton = creatVipBtn
  32. lazy var navBarView: TSBaseNavContentBarView = creatNavBarView
  33. lazy var navBarContentView: UIView = creatNavBarContentView
  34. lazy var normalNavBarView: TSNormalNavigationBarView = creatNormalNavBarView
  35. //collectionView 布局
  36. lazy var textMessageSizeCalculator = TSTextLayoutSizeCalculator(layout:self.messagesCollectionView.messagesCollectionViewFlowLayout)
  37. //键盘
  38. lazy var inputBarVC: TSChatInputBarVC = creatInputBarVC
  39. lazy var inputBarBgView:UIView = creatInputBarBgView
  40. let inputBarTopView:UIView = UIView()
  41. lazy var scrollToBottomButton: UIButton = creatScrollToBottomButton
  42. // vip 相关
  43. lazy var freeText: UILabel = creatFreeText
  44. lazy var upgradeVipBg: UIView = creatUpgradeVipBg
  45. let formatter: DateFormatter = {
  46. let formatter = DateFormatter()
  47. formatter.dateStyle = .medium
  48. return formatter
  49. }()
  50. override func viewDidLoad() {
  51. //在父类前先设置好inputBarType,否则会先加载默认白色,再加载自定义黑色,会一闪而过白色默认的
  52. setUpInputBarType()
  53. super.viewDidLoad()
  54. configureNaviBarView()
  55. configureMessageCollectionView()
  56. configureMessageInputBar()
  57. loadFirstMessages()
  58. // updateVipView()
  59. dealThings()
  60. }
  61. func dealThings(){
  62. // vipBtn.isHidden = PurchaseManager.default.isVip
  63. // NotificationCenter.default.addObserver(self, selector: #selector(vipInfoChanged), name: .kPurchaseDidChanged, object: nil)
  64. if viewModel.uiStyle == .chat {
  65. // 注册通知监听,App死的时候,保存本次聊天记录到本地
  66. NotificationCenter.default.addObserver(self, selector: #selector(saveChatList), name: .kApplicationWillTerminate, object: nil)
  67. }
  68. }
  69. // @objc func vipInfoChanged() {
  70. // kExecuteOnMainThread {
  71. // self.vipBtn.isHidden = PurchaseManager.default.isVip
  72. // self.updateVipView()
  73. // }
  74. // }
  75. @objc func saveChatList() {
  76. messageList.removeFirst()
  77. if messageList.count > 0 {
  78. //保存本次聊天记录
  79. viewModel.updateMessages(msgModels: messageList)
  80. }
  81. }
  82. func loadFirstMessages() {
  83. self.messageList = viewModel.getHistoryChatMessage()
  84. self.messagesCollectionView.reloadData()
  85. self.messagesCollectionView.scrollToLastItem(animated: false)
  86. }
  87. func configureMessageCollectionView() {
  88. clearAndResetConstraints()
  89. view.backgroundColor = .mainBg
  90. //设置自定义FlowLayout,itemsize等,都在这里控制
  91. let flowLayout = CustomMessagesFlowLayout()
  92. flowLayout.sectionInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)
  93. messagesCollectionView.collectionViewLayout = flowLayout
  94. messagesCollectionView.backgroundColor = .clear
  95. messagesCollectionView.register(TSTextMessageContentCell.self)
  96. messagesCollectionView.messagesLayoutDelegate = self
  97. messagesCollectionView.messagesDisplayDelegate = self
  98. messagesCollectionView.messagesDataSource = self
  99. messagesCollectionView.messageCellDelegate = self
  100. messagesCollectionView.clipsToBounds = true
  101. scrollsToLastItemOnKeyboardBeginsEditing = true // default false
  102. maintainPositionOnInputBarHeightChanged = true // default false
  103. showMessageTimestampOnSwipeLeft = false // default false
  104. // messagesCollectionView.refreshControl = refreshControl
  105. messagesCollectionView.reloadData()
  106. }
  107. func clearAndResetConstraints() {
  108. // 筛选出与 messagesCollectionView 相关的约束
  109. let constraintsToRemove = view.constraints.filter { constraint in
  110. return (constraint.firstItem as? UIView == messagesCollectionView) || (constraint.secondItem as? UIView == messagesCollectionView)
  111. }
  112. // 停用并移除这些约束
  113. NSLayoutConstraint.deactivate(constraintsToRemove)
  114. for constraint in constraintsToRemove {
  115. view.removeConstraint(constraint)
  116. }
  117. messagesCollectionView.snp.remakeConstraints { make in
  118. make.leading.trailing.bottom.equalTo(0)
  119. make.top.equalTo(k_Nav_Height)
  120. }
  121. }
  122. // MARK: - Helpers
  123. var lastIndexPath:IndexPath{
  124. let section = messagesCollectionView.numberOfSections - 1
  125. if messagesCollectionView.numberOfItems(inSection: section) > 0 {
  126. let item = messagesCollectionView.numberOfItems(inSection: section) - 1
  127. return IndexPath(item: item, section: section)
  128. }
  129. return IndexPath(item: 0, section: 0)
  130. }
  131. func insertMessage(_ message: TSChatMessage,indexPath:IndexPath? = nil) {
  132. // messageList.append(message)
  133. let isReplace = replaceOrAppend(message, indexPath: indexPath)
  134. var cellIndexPaht = lastIndexPath
  135. if let indexPath = indexPath{
  136. cellIndexPaht = indexPath
  137. }
  138. messagesCollectionView.performBatchUpdates({
  139. if isReplace == false {
  140. messagesCollectionView.insertItems(at: [cellIndexPaht])
  141. }
  142. if messageList.count >= 2 {
  143. messagesCollectionView.reloadItems(at: [cellIndexPaht])
  144. }else{
  145. messagesCollectionView.reloadData()
  146. }
  147. }, completion: { [weak self] _ in
  148. if self?.isLastSectionVisible() == true {
  149. self?.messagesCollectionView.scrollToLastItem(animated: true)
  150. }
  151. })
  152. }
  153. /// 返回值,代表是否替换
  154. func replaceOrAppend(_ message: TSChatMessage,indexPath:IndexPath?) ->Bool {
  155. if let indexPath = indexPath {
  156. let index = indexPath.item
  157. if index >= 0 && index < messageList.count {
  158. messageList[index] = message// 下标存在,替换该位置的元素
  159. return true
  160. }
  161. }
  162. messageList.append(message)// 插入新元素
  163. return false
  164. }
  165. func isLastSectionVisible() -> Bool {
  166. guard !messageList.isEmpty else { return false }
  167. return messagesCollectionView.indexPathsForVisibleItems.contains(lastIndexPath)
  168. }
  169. override func viewWillAppear(_ animated: Bool) {
  170. }
  171. }