MOLineSearchUserView.swift 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //
  2. // MOLineSearchUserView.swift
  3. // MiMoLive
  4. //
  5. // Created by OneeChan on 2025/9/25.
  6. //
  7. import Foundation
  8. import UIKit
  9. class MOLineSearchUserView: UIView, MOPopupViewProtocol {
  10. private let curType: LineType
  11. private var userList: [MOLivePkLinkInviteVO] = []
  12. private var nextTag = ""
  13. private var curKey = ""
  14. let container = UIView()
  15. let containerHeight: PopupViewHeight = .percent(0.7)
  16. private let noMoreDataView = MONoMoreDataView()
  17. private let tableView = UITableView()
  18. private let input = UITextField()
  19. private let clearButton = UIButton()
  20. init(type: LineType) {
  21. self.curType = type
  22. super.init(frame: .zero)
  23. setupViews()
  24. // reload()
  25. }
  26. required init?(coder: NSCoder) {
  27. fatalError("init(coder:) has not been implemented")
  28. }
  29. }
  30. extension MOLineSearchUserView {
  31. private func reload() {
  32. MOLineViewModel.loadLineMatchUsers(nil, curKey) { [weak self] list, hasMore in
  33. guard let self else { return }
  34. self.tableView.mj_header?.endRefreshing()
  35. guard let list else { return }
  36. self.noMoreDataView.isHaveData = !list.list.isEmpty
  37. self.userList = list.list
  38. self.nextTag = list.next
  39. if !hasMore {
  40. self.tableView.mj_footer?.endRefreshingWithNoMoreData()
  41. }
  42. self.tableView.reloadData()
  43. }
  44. }
  45. @objc
  46. private func loadNext() {
  47. guard !self.nextTag.isEmpty else {
  48. self.tableView.mj_footer?.endRefreshing()
  49. return
  50. }
  51. MOLineViewModel.loadLineMatchUsers(nextTag, curKey) { [weak self] list, hasMore in
  52. guard let self else { return }
  53. self.tableView.mj_footer?.endRefreshing()
  54. guard let list else { return }
  55. self.userList.append(contentsOf: list.list)
  56. self.nextTag = list.next
  57. if !hasMore {
  58. self.tableView.mj_footer?.endRefreshingWithNoMoreData()
  59. }
  60. self.tableView.reloadData()
  61. }
  62. }
  63. }
  64. extension MOLineSearchUserView {
  65. @objc
  66. private func handleBgClick() {
  67. input.resignFirstResponder()
  68. dismissH()
  69. }
  70. @objc
  71. private func handleClearClick() {
  72. input.text = ""
  73. clearButton.isHidden = true
  74. }
  75. }
  76. extension MOLineSearchUserView: UITableViewDataSource, UITableViewDelegate {
  77. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  78. 64
  79. }
  80. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  81. userList.count
  82. }
  83. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  84. let cell = tableView.dequeueReusableCell(withIdentifier: MOLineUserListCell.className())
  85. if indexPath.row < userList.count, let cell = cell as? MOLineUserListCell {
  86. cell.updateContent(MOLineUserListItemData(curType: curType, item: userList[indexPath.row]))
  87. }
  88. return cell!
  89. }
  90. }
  91. extension MOLineSearchUserView: UITextFieldDelegate {
  92. func textFieldShouldReturn(_ textField: UITextField) -> Bool {
  93. textField.resignFirstResponder()
  94. curKey = textField.text ?? ""
  95. reload()
  96. return true
  97. }
  98. @objc
  99. private func textFieldDidChanged(_ textField: UITextField) {
  100. clearButton.isHidden = textField.text?.isEmpty != false
  101. }
  102. }
  103. extension MOLineSearchUserView {
  104. private func setupViews() {
  105. let button = UIButton()
  106. button.addTarget(self, action: #selector(handleBgClick), for: .touchUpInside)
  107. addSubview(button)
  108. button.snp.makeConstraints { make in
  109. make.edges.equalToSuperview()
  110. }
  111. container.backgroundColor = .white
  112. container.layer.cornerRadius = 16
  113. container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
  114. addSubview(container)
  115. let match = buildSearch()
  116. container.addSubview(match)
  117. match.snp.makeConstraints { make in
  118. make.leading.equalToSuperview()
  119. make.trailing.equalToSuperview()
  120. make.top.equalToSuperview()
  121. }
  122. let list = buildList()
  123. container.addSubview(list)
  124. list.snp.makeConstraints { make in
  125. make.leading.equalToSuperview().offset(12)
  126. make.trailing.equalToSuperview().offset(-12)
  127. make.bottom.equalToSuperview()
  128. make.top.equalTo(match.snp.bottom)
  129. }
  130. }
  131. private func buildSearch() -> UIView {
  132. let container = UIView()
  133. let cancel = UIButton()
  134. cancel.setTitle("Cancel", for: .normal)
  135. cancel.setTitleColor(.init(hex: "#17171A"), for: .normal)
  136. cancel.titleLabel?.font = .poppinsRegularFont(14)
  137. cancel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
  138. cancel.addTarget(self, action: #selector(handleBgClick), for: .touchUpInside)
  139. container.addSubview(cancel)
  140. cancel.snp.makeConstraints { make in
  141. make.trailing.equalToSuperview().offset(-12)
  142. make.centerY.equalToSuperview()
  143. }
  144. let search = UIView()
  145. search.layer.cornerRadius = 8
  146. search.backgroundColor = .init(hex: "#F3F4FA")
  147. container.addSubview(search)
  148. search.snp.makeConstraints { make in
  149. make.leading.equalToSuperview().offset(12)
  150. make.top.equalToSuperview().offset(12)
  151. make.bottom.equalToSuperview().offset(-8)
  152. make.trailing.equalTo(cancel.snp.leading).offset(-19)
  153. }
  154. let icon = UIImageView()
  155. icon.image = UIImage(named: "icon_search")
  156. icon.setContentHuggingPriority(.defaultHigh, for: .horizontal)
  157. search.addSubview(icon)
  158. icon.snp.makeConstraints { make in
  159. make.leading.equalToSuperview().offset(8)
  160. make.centerY.equalToSuperview()
  161. }
  162. clearButton.addTarget(self, action: #selector(handleClearClick), for: .touchUpInside)
  163. clearButton.setImage(UIImage(named: "icon_textfield_clear"), for: .normal)
  164. clearButton.isHidden = true
  165. clearButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
  166. search.addSubview(clearButton)
  167. clearButton.snp.makeConstraints { make in
  168. make.centerY.equalToSuperview()
  169. make.trailing.equalToSuperview().offset(-10)
  170. }
  171. input.returnKeyType = .search
  172. let text: String = .init(key: "mimo_contact_search_hint")
  173. let placeholder = NSAttributedString(string: text,
  174. attributes: [.foregroundColor: UIColor.init(hex: "#878A99", alpha: 0.5)])
  175. input.attributedPlaceholder = placeholder
  176. input.font = .poppinsRegularFont(14)
  177. input.tintColor = .init(hex: "#17171A")
  178. input.textColor = .init(hex: "#17171A")
  179. input.delegate = self
  180. input.addTarget(self, action: #selector(textFieldDidChanged(_:)), for: .editingChanged)
  181. search.addSubview(input)
  182. input.snp.makeConstraints { make in
  183. make.leading.equalTo(icon.snp.trailing).offset(6)
  184. make.top.bottom.equalToSuperview()
  185. make.trailing.equalTo(clearButton.snp.leading).offset(-6)
  186. make.height.equalTo(36)
  187. }
  188. return container
  189. }
  190. private func buildList() -> UIView {
  191. noMoreDataView.isHaveData = true
  192. let header = MJRefreshNormalHeader { [weak self] in
  193. guard let self else { return }
  194. self.reload()
  195. }
  196. header.lastUpdatedTimeLabel?.isHidden = true
  197. header.stateLabel?.isHidden = true
  198. tableView.mj_header = header
  199. let footer = MJRefreshAutoNormalFooter(refreshingTarget: self, refreshingAction: #selector(loadNext))
  200. footer.setTitle("", for: .noMoreData)
  201. tableView.mj_footer = footer
  202. tableView.backgroundView = noMoreDataView
  203. tableView.register(MOLineUserListCell.self, forCellReuseIdentifier: MOLineUserListCell.className())
  204. tableView.separatorColor = .clear
  205. tableView.delegate = self
  206. tableView.dataSource = self
  207. tableView.allowsSelection = false
  208. return tableView
  209. }
  210. }
  211. //import SwiftUI
  212. //
  213. //struct MOLineSearchUserViewPreview: UIViewRepresentable {
  214. // func makeUIView(context: Context) -> some UIView {
  215. // let viewModel = MOLineViewModel()
  216. // let view = UIView()
  217. // view.backgroundColor = .black
  218. // let list = MOLineSearchUserView(viewModel)
  219. // list.showIn(view)
  220. //
  221. // return view
  222. // }
  223. //
  224. // func updateUIView(_ uiView: UIViewType, context: Context) { }
  225. //}
  226. //
  227. //#Preview {
  228. // MOLineSearchUserViewPreview()
  229. //}