TransferMasterView.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. //
  2. // TransferMasterView.swift
  3. // TUIRoomKit
  4. //
  5. // Created by janejntang on 2023/2/20.
  6. //
  7. import Foundation
  8. class TransferMasterView: UIView {
  9. let viewModel: TransferMasterViewModel
  10. var attendeeList: [UserEntity]
  11. var searchArray: [UserEntity] = []
  12. private var isSearching: Bool = false
  13. let topLabel: UILabel = {
  14. let label = UILabel()
  15. label.textColor = UIColor(0xD5E0F2)
  16. label.font = UIFont(name: "PingFangSC-Regular", size: 16)
  17. label.text = .transferMasterText
  18. return label
  19. }()
  20. let searchBar: UISearchBar = {
  21. let searchBar = UISearchBar()
  22. searchBar.placeholder = .searchMemberText
  23. searchBar.setBackgroundImage(UIColor(0x1B1E26).trans2Image(), for: .top, barMetrics: .default)
  24. if #available(iOS 13, *) {
  25. searchBar.searchTextField.textColor = UIColor(0xB2BBD1)
  26. searchBar.searchTextField.tintColor = UIColor(0xB2BBD1).withAlphaComponent(0.3)
  27. searchBar.searchTextField.layer.cornerRadius = 6
  28. searchBar.searchTextField.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  29. } else {
  30. if let textField = searchBar.value(forKey: "searchField") as? UITextField {
  31. textField.textColor = UIColor(0xB2BBD1)
  32. textField.tintColor = UIColor(0xB2BBD1).withAlphaComponent(0.3)
  33. textField.layer.cornerRadius = 6
  34. textField.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  35. }
  36. }
  37. return searchBar
  38. }()
  39. let searchControl: UIControl = {
  40. let view = UIControl()
  41. view.backgroundColor = .clear
  42. view.isHidden = true
  43. return view
  44. }()
  45. let appointMasterButton: UIButton = {
  46. let button = UIButton(type: .custom)
  47. button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
  48. button.setTitle(.appointAndLeaveRoomText, for: .normal)
  49. button.setTitleColor(UIColor(0xFFFFFF), for: .normal)
  50. button.setBackgroundImage(UIColor(0x006EFF).withAlphaComponent(0.2).trans2Image(), for: .disabled)
  51. button.setBackgroundImage(UIColor(0x006EFF).trans2Image(), for: .normal)
  52. button.layer.cornerRadius = 12
  53. button.clipsToBounds = true
  54. button.titleLabel?.adjustsFontSizeToFitWidth = true
  55. button.adjustsImageWhenHighlighted = false
  56. return button
  57. }()
  58. lazy var transferMasterTableView: UITableView = {
  59. let tableView = UITableView(frame: .zero, style: .plain)
  60. tableView.separatorStyle = .none
  61. tableView.delegate = self
  62. tableView.dataSource = self
  63. tableView.backgroundColor = UIColor(0x1B1E26)
  64. tableView.register(TransferMasterTableCell.self, forCellReuseIdentifier: "RaiseHandCell")
  65. return tableView
  66. }()
  67. init(viewModel: TransferMasterViewModel) {
  68. self.viewModel = viewModel
  69. self.attendeeList = viewModel.attendeeList
  70. super.init(frame: .zero)
  71. }
  72. required init?(coder: NSCoder) {
  73. fatalError("init(coder:) has not been implemented")
  74. }
  75. private var isViewReady: Bool = false
  76. override func didMoveToWindow() {
  77. super.didMoveToWindow()
  78. backgroundColor = UIColor(0x1B1E26)
  79. guard !isViewReady else { return }
  80. constructViewHierarchy()
  81. activateConstraints()
  82. bindInteraction()
  83. isViewReady = true
  84. }
  85. func constructViewHierarchy() {
  86. addSubview(topLabel)
  87. addSubview(searchBar)
  88. addSubview(transferMasterTableView)
  89. addSubview(appointMasterButton)
  90. addSubview(searchControl)
  91. }
  92. func activateConstraints() {
  93. topLabel.snp.makeConstraints { make in
  94. make.top.equalToSuperview().offset(10.scale375Height())
  95. make.leading.equalToSuperview().offset(16.scale375())
  96. make.trailing.equalToSuperview().offset(-16.scale375())
  97. make.height.equalTo(24.scale375Height())
  98. }
  99. searchBar.snp.makeConstraints { make in
  100. make.leading.equalToSuperview().offset(16.scale375())
  101. make.trailing.equalToSuperview().offset(-16.scale375())
  102. make.height.equalTo(34.scale375Height())
  103. make.top.equalTo(topLabel.snp.bottom).offset(23.scale375Height())
  104. }
  105. transferMasterTableView.snp.makeConstraints { make in
  106. make.leading.equalToSuperview().offset(16.scale375())
  107. make.trailing.equalToSuperview().offset(-16.scale375())
  108. make.top.equalTo(searchBar.snp.bottom).offset(10.scale375Height())
  109. make.bottom.equalToSuperview()
  110. }
  111. appointMasterButton.snp.makeConstraints { make in
  112. make.trailing.equalToSuperview().offset(-20)
  113. make.bottom.equalToSuperview().offset(-40 - kDeviceSafeBottomHeight)
  114. make.height.equalTo(50)
  115. make.leading.equalToSuperview().offset(20)
  116. }
  117. searchControl.snp.makeConstraints { make in
  118. make.edges.equalToSuperview()
  119. }
  120. }
  121. func bindInteraction() {
  122. searchBar.delegate = self
  123. viewModel.viewResponder = self
  124. appointMasterButton.addTarget(self, action: #selector(appointMasterAction(sender:)), for: .touchUpInside)
  125. let tap = UITapGestureRecognizer(target: self, action: #selector(hideSearchControl(sender:)))
  126. searchControl.addGestureRecognizer(tap)
  127. appointMasterButton.isEnabled = viewModel.userId.count > 0
  128. }
  129. @objc func appointMasterAction(sender: UIButton) {
  130. viewModel.appointMasterAction(sender: sender)
  131. }
  132. @objc func hideSearchControl(sender: UIView) {
  133. if #available(iOS 13, *) {
  134. searchBar.searchTextField.resignFirstResponder()
  135. } else {
  136. searchBar.resignFirstResponder()
  137. }
  138. searchControl.isHidden = true
  139. }
  140. deinit {
  141. debugPrint("deinit \(self)")
  142. }
  143. }
  144. extension TransferMasterView: UISearchBarDelegate {
  145. func searchBar(_ searchBar:UISearchBar,textDidChange searchText:String){
  146. let searchContentText = searchText.trimmingCharacters(in: .whitespaces)
  147. if searchContentText.count == 0 {
  148. attendeeList = viewModel.attendeeList
  149. transferMasterTableView.reloadData()
  150. isSearching = false
  151. } else {
  152. searchArray = viewModel.attendeeList.filter({ model -> Bool in
  153. return model.userName.contains(searchContentText)
  154. })
  155. attendeeList = searchArray
  156. transferMasterTableView.reloadData()
  157. isSearching = true
  158. }
  159. }
  160. func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
  161. searchControl.isHidden = false
  162. return true
  163. }
  164. }
  165. extension TransferMasterView: UITableViewDataSource {
  166. internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  167. return attendeeList.count
  168. }
  169. }
  170. extension TransferMasterView: UITableViewDelegate {
  171. internal func tableView(_ tableView: UITableView,
  172. cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  173. let attendeeModel = attendeeList[indexPath.row]
  174. let cell = TransferMasterTableCell(attendeeModel: attendeeModel, viewModel: viewModel)
  175. cell.selectionStyle = .none
  176. return cell
  177. }
  178. internal func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  179. searchBar.endEditing(true)
  180. if #available(iOS 13, *) {
  181. searchBar.searchTextField.resignFirstResponder()
  182. } else {
  183. searchBar.resignFirstResponder()
  184. }
  185. viewModel.userId = attendeeList[indexPath.row].userId
  186. appointMasterButton.isEnabled = true
  187. transferMasterTableView.reloadData()
  188. }
  189. internal func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  190. return 60.scale375Height()
  191. }
  192. }
  193. extension TransferMasterView: TransferMasterViewResponder {
  194. func makeToast(message: String) {
  195. makeToast(message)
  196. }
  197. func reloadTransferMasterTableView() {
  198. guard !isSearching else { return }
  199. attendeeList = viewModel.attendeeList
  200. transferMasterTableView.reloadData()
  201. }
  202. func searchControllerChangeActive(isActive: Bool) {
  203. searchBar.endEditing(!isActive)
  204. if #available(iOS 13, *) {
  205. searchBar.searchTextField.resignFirstResponder()
  206. } else {
  207. searchBar.resignFirstResponder()
  208. }
  209. }
  210. }
  211. class TransferMasterTableCell: UITableViewCell {
  212. let attendeeModel: UserEntity
  213. let viewModel: TransferMasterViewModel
  214. let avatarImageView: UIImageView = {
  215. let img = UIImageView()
  216. img.layer.cornerRadius = 20
  217. img.layer.masksToBounds = true
  218. return img
  219. }()
  220. let userLabel: UILabel = {
  221. let label = UILabel()
  222. label.textColor = UIColor(0xD5E0F2)
  223. label.backgroundColor = UIColor.clear
  224. label.textAlignment = isRTL ? .right : .left
  225. label.textAlignment = .left
  226. label.font = UIFont(name: "PingFangSC-Regular", size: 16)
  227. label.numberOfLines = 1
  228. return label
  229. }()
  230. let checkMarkButton: UIButton = {
  231. let button = UIButton(type: .custom)
  232. button.setImage(UIImage(named: "room_check_mark", in: tuiRoomKitBundle(), compatibleWith: nil), for: .normal)
  233. button.isHidden = true
  234. return button
  235. }()
  236. let downLineView : UIView = {
  237. let view = UIView()
  238. view.backgroundColor = UIColor(0x2A2D38)
  239. return view
  240. }()
  241. init(attendeeModel: UserEntity ,viewModel: TransferMasterViewModel) {
  242. self.attendeeModel = attendeeModel
  243. self.viewModel = viewModel
  244. super.init(style: .default, reuseIdentifier: "TransferMasterTableCell")
  245. }
  246. private var isViewReady: Bool = false
  247. override func didMoveToWindow() {
  248. super.didMoveToWindow()
  249. guard !isViewReady else { return }
  250. constructViewHierarchy()
  251. activateConstraints()
  252. bindInteraction()
  253. isViewReady = true
  254. }
  255. required init?(coder: NSCoder) {
  256. fatalError("init(coder:) has not been implemented")
  257. }
  258. func constructViewHierarchy() {
  259. contentView.addSubview(avatarImageView)
  260. contentView.addSubview(userLabel)
  261. contentView.addSubview(checkMarkButton)
  262. contentView.addSubview(downLineView)
  263. }
  264. func activateConstraints() {
  265. avatarImageView.snp.makeConstraints { make in
  266. make.width.height.equalTo(40.scale375Height())
  267. make.leading.equalToSuperview()
  268. make.centerY.equalToSuperview()
  269. }
  270. checkMarkButton.snp.makeConstraints { make in
  271. make.width.height.equalTo(22.scale375())
  272. make.trailing.equalToSuperview()
  273. make.centerY.equalTo(self.avatarImageView)
  274. }
  275. userLabel.snp.makeConstraints { make in
  276. make.centerY.equalToSuperview()
  277. make.leading.equalTo(avatarImageView.snp.trailing).offset(10.scale375())
  278. make.width.equalTo(150.scale375())
  279. make.height.equalTo(22.scale375Height())
  280. }
  281. downLineView.snp.makeConstraints { make in
  282. make.leading.equalTo(userLabel)
  283. make.trailing.equalToSuperview()
  284. make.bottom.equalToSuperview()
  285. make.height.equalTo(1.scale375Height())
  286. }
  287. }
  288. func bindInteraction() {
  289. backgroundColor = UIColor(0x1B1E26)
  290. setupViewState(item: attendeeModel)
  291. }
  292. func setupViewState(item: UserEntity) {
  293. let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil)
  294. if let url = URL(string: item.avatarUrl) {
  295. avatarImageView.sd_setImage(with: url, placeholderImage: placeholder)
  296. } else {
  297. avatarImageView.image = placeholder
  298. }
  299. userLabel.text = item.userName
  300. if viewModel.userId == attendeeModel.userId {
  301. checkMarkButton.isHidden = false
  302. }
  303. }
  304. deinit {
  305. debugPrint("deinit \(self)")
  306. }
  307. }
  308. private extension String {
  309. static var transferMasterText: String {
  310. localized("Appoint a new master")
  311. }
  312. static var searchMemberText: String {
  313. localized("Search for participants")
  314. }
  315. static var appointAndLeaveRoomText: String {
  316. localized("Appoint and leave")
  317. }
  318. }