LNIMChatEmojiPanel.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. //
  2. // LNIMChatEmojiPanel.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/17.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. protocol LNIMChatEmojiPanelDelegate: AnyObject {
  11. func onIMChatEmojiPanelDidClickDelete(view: LNIMChatEmojiPanel)
  12. func onIMChatEmojiPanel(view: LNIMChatEmojiPanel, didSelectEmoji emoji: LNEmojiData)
  13. }
  14. class LNIMChatEmojiPanel: UIView {
  15. private var emojiGroups: [LNEmojiGroup] = []
  16. private var collectionView: UICollectionView?
  17. private let menuView = UIView()
  18. private let deleteButton = UIButton()
  19. weak var delegate: LNIMChatEmojiPanelDelegate?
  20. override init(frame: CGRect) {
  21. super.init(frame: frame)
  22. setupViews()
  23. }
  24. func reloadData() {
  25. var groups: [LNEmojiGroup] = []
  26. if let recent = LNIMEmojiManager.shared.recentGroup {
  27. groups.append(recent)
  28. }
  29. if let emojiGroup = LNIMEmojiManager.shared.emojiGroup {
  30. groups.append(emojiGroup)
  31. }
  32. emojiGroups = groups
  33. collectionView?.reloadData()
  34. }
  35. required init?(coder: NSCoder) {
  36. fatalError("init(coder:) has not been implemented")
  37. }
  38. }
  39. extension LNIMChatEmojiPanel: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
  40. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  41. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LNIMChatEmojiCell.className, for: indexPath) as! LNIMChatEmojiCell
  42. let item = emojiGroups[indexPath.section].emojis[indexPath.row]
  43. cell.update(item)
  44. return cell
  45. }
  46. func numberOfSections(in collectionView: UICollectionView) -> Int {
  47. emojiGroups.count
  48. }
  49. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  50. emojiGroups[section].emojis.count
  51. }
  52. func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
  53. let header = collectionView.dequeueReusableSupplementaryView(
  54. ofKind: UICollectionView.elementKindSectionHeader,
  55. withReuseIdentifier: "headerView", for: indexPath)
  56. header.subviews.forEach { $0.removeFromSuperview() }
  57. let titleLabel = UILabel()
  58. titleLabel.font = .systemFont(ofSize: 12)
  59. titleLabel.textColor = .init(hex: "#444444")
  60. header.addSubview(titleLabel)
  61. titleLabel.snp.makeConstraints { make in
  62. make.leading.equalToSuperview().offset(20)
  63. make.centerY.equalToSuperview()
  64. }
  65. let group = emojiGroups[indexPath.section]
  66. titleLabel.text = group.groupName
  67. return header
  68. }
  69. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
  70. let group = emojiGroups[section]
  71. return if group.groupName.isEmpty {
  72. .zero
  73. } else {
  74. .init(width: collectionView.bounds.width, height: 20)
  75. }
  76. }
  77. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  78. collectionView.deselectItem(at: indexPath, animated: false)
  79. let item = emojiGroups[indexPath.section].emojis[indexPath.row]
  80. delegate?.onIMChatEmojiPanel(view: self, didSelectEmoji: item)
  81. LNIMEmojiManager.shared.addRecentEmoji(name: item.name)
  82. }
  83. }
  84. extension LNIMChatEmojiPanel {
  85. private func setupViews() {
  86. let collectionView = buildCollectionView()
  87. addSubview(collectionView)
  88. collectionView.snp.makeConstraints { make in
  89. make.edges.equalToSuperview()
  90. }
  91. let line = UIView()
  92. line.backgroundColor = .init(hex: "#DBDBDB")
  93. addSubview(line)
  94. line.snp.makeConstraints { make in
  95. make.horizontalEdges.equalToSuperview()
  96. make.top.equalToSuperview()
  97. make.height.equalTo(0.5)
  98. }
  99. let menu = buildMenu()
  100. addSubview(menu)
  101. menu.snp.makeConstraints { make in
  102. make.trailing.equalToSuperview().offset(-16)
  103. make.bottom.equalToSuperview().offset(20)
  104. }
  105. }
  106. private func buildCollectionView() -> UIView {
  107. let layout = UICollectionViewFlowLayout()
  108. layout.scrollDirection = .vertical
  109. layout.minimumLineSpacing = 10
  110. layout.minimumInteritemSpacing = 10
  111. layout.sectionInset = .init(top: 10, left: 20, bottom: 0, right: 20)
  112. let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
  113. collectionView.register(LNIMChatEmojiCell.self, forCellWithReuseIdentifier: LNIMChatEmojiCell.className)
  114. collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerView")
  115. collectionView.isPagingEnabled = false
  116. collectionView.showsHorizontalScrollIndicator = false
  117. collectionView.showsVerticalScrollIndicator = false
  118. collectionView.backgroundColor = .clear
  119. collectionView.alwaysBounceVertical = true
  120. collectionView.delegate = self
  121. collectionView.dataSource = self
  122. collectionView.contentInset = .init(top: 10, left: 0, bottom: 0, right: 0)
  123. self.collectionView = collectionView
  124. return collectionView
  125. }
  126. private func buildMenu() -> UIView {
  127. menuView.snp.makeConstraints { make in
  128. make.height.equalTo(88)
  129. }
  130. deleteButton.setImage(.icImChatEmojiDelete, for: .normal)
  131. deleteButton.imageEdgeInsets = .init(top: 5, left: 5, bottom: 5, right: 5)
  132. deleteButton.imageView?.contentMode = .scaleAspectFit
  133. deleteButton.layer.cornerRadius = 2
  134. deleteButton.backgroundColor = .white
  135. deleteButton.addAction(UIAction(handler: { [weak self] _ in
  136. guard let self else { return }
  137. delegate?.onIMChatEmojiPanelDidClickDelete(view: self)
  138. }), for: .touchUpInside)
  139. menuView.addSubview(deleteButton)
  140. deleteButton.snp.makeConstraints { make in
  141. make.trailing.equalToSuperview()
  142. make.leading.equalToSuperview()
  143. make.top.equalToSuperview()
  144. make.width.equalTo(50)
  145. make.height.equalTo(30)
  146. }
  147. return menuView
  148. }
  149. }