LNRoomGiftHeaderView.swift 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //
  2. // LNRoomGiftHeaderView.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/3/23.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNRoomGiftHeaderView: UIView {
  11. private let roomSeatsView = UIView()
  12. private let stackView = UIStackView()
  13. private let specifiedUserView = LNRoomGiftSpecifiedUserView()
  14. private weak var roomSession: LNRoomViewModel?
  15. private var headers: [LNRoomSeatNum: LNRoomGiftAvatarView] = [:]
  16. var selection: [String] {
  17. if !roomSeatsView.isHidden {
  18. headers.map { $1 }.filter { !$0.isHidden && $0.isSelected }.compactMap { $0.curSeatItem?.uid }
  19. } else if let uid = specifiedUserView.curUid {
  20. [uid]
  21. } else {
  22. []
  23. }
  24. }
  25. override init(frame: CGRect) {
  26. super.init(frame: frame)
  27. setupViews()
  28. LNEventDeliver.addObserver(self)
  29. }
  30. func update(_ room: LNRoomViewModel?, selectedUid: String?) {
  31. roomSession = room
  32. if selectedUid == nil
  33. || room?.seatsInfo.contains(where: { $0.uid == selectedUid }) == true {
  34. onRoomSeatsChanged()
  35. roomSeatsView.isHidden = false
  36. specifiedUserView.isHidden = true
  37. } else {
  38. roomSeatsView.isHidden = true
  39. specifiedUserView.isHidden = false
  40. }
  41. if let selectedUid {
  42. headers.first { $1.curSeatItem?.uid == selectedUid }?.value.isSelected = true
  43. specifiedUserView.update(selectedUid)
  44. }
  45. }
  46. required init?(coder: NSCoder) {
  47. fatalError("init(coder:) has not been implemented")
  48. }
  49. }
  50. extension LNRoomGiftHeaderView: LNRoomViewModelNotify {
  51. func onRoomSeatsChanged() {
  52. guard let seats = roomSession?.seatsInfo.sorted(by: { $0.index.rawValue < $1.index.rawValue }) else { return }
  53. if headers.count != seats.count {
  54. stackView.arrangedSubviews.forEach {
  55. stackView.removeArrangedSubview($0)
  56. $0.removeFromSuperview()
  57. }
  58. for seat in seats {
  59. let avatar = LNRoomGiftAvatarView()
  60. avatar.isSelected = false
  61. avatar.onTap { [weak avatar] in
  62. guard let avatar else { return }
  63. avatar.isSelected.toggle()
  64. }
  65. avatar.update(seat)
  66. stackView.addArrangedSubview(avatar)
  67. headers[seat.index] = avatar
  68. }
  69. } else {
  70. for seat in seats {
  71. headers[seat.index]?.update(seat)
  72. }
  73. }
  74. }
  75. }
  76. private extension LNRoomGiftHeaderView {
  77. func setupViews() {
  78. let titleLabel = UILabel()
  79. titleLabel.font = .body_l
  80. titleLabel.textColor = .text_2
  81. titleLabel.text = .init(key: "B00129")
  82. addSubview(titleLabel)
  83. titleLabel.snp.makeConstraints { make in
  84. make.leading.equalToSuperview().offset(10)
  85. make.centerY.equalToSuperview()
  86. }
  87. let roomSeatsView = buildRoomSeatsView()
  88. addSubview(roomSeatsView)
  89. roomSeatsView.snp.makeConstraints { make in
  90. make.leading.equalTo(titleLabel.snp.trailing).offset(6)
  91. make.trailing.equalToSuperview().offset(-10)
  92. make.verticalEdges.equalToSuperview()
  93. make.height.equalTo(40)
  94. }
  95. let userView = buildSpecifiedUserView()
  96. addSubview(userView)
  97. userView.snp.makeConstraints { make in
  98. make.leading.equalTo(titleLabel.snp.trailing).offset(6)
  99. make.trailing.equalToSuperview().offset(-10)
  100. make.centerY.equalToSuperview()
  101. }
  102. }
  103. private func buildRoomSeatsView() -> UIView {
  104. let scrollView = UIScrollView()
  105. scrollView.showsHorizontalScrollIndicator = false
  106. scrollView.contentInset = .init(top: 0, left: 0, bottom: 0, right: 32)
  107. roomSeatsView.addSubview(scrollView)
  108. scrollView.snp.makeConstraints { make in
  109. make.edges.equalToSuperview()
  110. }
  111. stackView.spacing = 6
  112. stackView.axis = .horizontal
  113. scrollView.addSubview(stackView)
  114. stackView.snp.makeConstraints { make in
  115. make.edges.equalToSuperview()
  116. make.height.equalToSuperview()
  117. }
  118. let gradientView = UIView.gradientView([
  119. .fill_7.withAlphaComponent(0), .fill_7
  120. ], .horizontalLTR)
  121. roomSeatsView.addSubview(gradientView)
  122. gradientView.snp.makeConstraints { make in
  123. make.verticalEdges.equalToSuperview()
  124. make.trailing.equalToSuperview()
  125. make.width.equalTo(32)
  126. }
  127. return roomSeatsView
  128. }
  129. private func buildSpecifiedUserView() -> UIView {
  130. return specifiedUserView
  131. }
  132. }
  133. private class LNRoomGiftAvatarView: UIView {
  134. private let avatarBg = UIView()
  135. private let avatarView = UIImageView()
  136. private let badgeBg = UIView()
  137. private let badge = UILabel()
  138. private(set) var curSeatItem: LNRoomSeatItem?
  139. var isSelected: Bool = false {
  140. didSet {
  141. if isSelected {
  142. avatarBg.layer.borderColor = UIColor.text_6.cgColor
  143. badge.textColor = .text_1
  144. badgeBg.backgroundColor = .text_6
  145. } else {
  146. avatarBg.layer.borderColor = UIColor.clear.cgColor
  147. badge.textColor = .text_4
  148. badgeBg.backgroundColor = .text_1
  149. }
  150. }
  151. }
  152. func update(_ seat: LNRoomSeatItem) {
  153. isHidden = seat.uid.isEmpty || seat.uid.isMyUid
  154. avatarView.sd_setImage(with: URL(string: seat.avatar))
  155. badge.text = seat.index.giftHeaderTitle
  156. curSeatItem = seat
  157. }
  158. override init(frame: CGRect) {
  159. super.init(frame: frame)
  160. snp.makeConstraints { make in
  161. make.width.height.equalTo(40)
  162. }
  163. avatarBg.backgroundColor = .clear
  164. avatarBg.layer.cornerRadius = 17
  165. avatarBg.layer.borderWidth = 1
  166. avatarBg.layer.borderColor = UIColor.primary_4.cgColor
  167. addSubview(avatarBg)
  168. avatarBg.snp.makeConstraints { make in
  169. make.width.height.equalTo(34)
  170. make.centerX.equalToSuperview()
  171. make.top.equalToSuperview().offset(1)
  172. make.bottom.equalToSuperview().offset(-5)
  173. }
  174. avatarView.layer.cornerRadius = 15
  175. avatarView.clipsToBounds = true
  176. avatarView.contentMode = .scaleAspectFill
  177. avatarBg.addSubview(avatarView)
  178. avatarView.snp.makeConstraints { make in
  179. make.center.equalToSuperview()
  180. make.width.height.equalTo(30)
  181. }
  182. badgeBg.backgroundColor = .fill
  183. badgeBg.layer.cornerRadius = 6
  184. addSubview(badgeBg)
  185. badgeBg.snp.makeConstraints { make in
  186. make.centerX.equalToSuperview()
  187. make.bottom.equalToSuperview().offset(-2)
  188. make.height.equalTo(12)
  189. make.leading.greaterThanOrEqualToSuperview()
  190. make.width.greaterThanOrEqualTo(26)
  191. }
  192. badge.font = .systemFont(ofSize: 10)
  193. badge.textColor = .text_4
  194. badge.textAlignment = .center
  195. badgeBg.addSubview(badge)
  196. badge.snp.makeConstraints { make in
  197. make.center.equalToSuperview()
  198. make.horizontalEdges.equalToSuperview().inset(4)
  199. }
  200. }
  201. required init?(coder: NSCoder) {
  202. fatalError("init(coder:) has not been implemented")
  203. }
  204. }
  205. private class LNRoomGiftSpecifiedUserView: UIView {
  206. private let nameLabel = UILabel()
  207. private let avatar = UIImageView()
  208. private(set) var curUid: String?
  209. override init(frame: CGRect) {
  210. super.init(frame: frame)
  211. let avatarBg = UIView()
  212. avatarBg.backgroundColor = .clear
  213. avatarBg.layer.cornerRadius = 17
  214. avatarBg.layer.borderWidth = 1
  215. avatarBg.layer.borderColor = UIColor.primary_4.cgColor
  216. addSubview(avatarBg)
  217. avatarBg.snp.makeConstraints { make in
  218. make.width.height.equalTo(34)
  219. make.leading.equalToSuperview()
  220. make.verticalEdges.equalToSuperview()
  221. }
  222. avatar.layer.cornerRadius = 15
  223. avatar.clipsToBounds = true
  224. avatar.contentMode = .scaleAspectFill
  225. avatarBg.addSubview(avatar)
  226. avatar.snp.makeConstraints { make in
  227. make.center.equalToSuperview()
  228. make.width.height.equalTo(30)
  229. }
  230. nameLabel.font = .body_s
  231. nameLabel.textColor = .text_1
  232. addSubview(nameLabel)
  233. nameLabel.snp.makeConstraints { make in
  234. make.centerY.equalToSuperview()
  235. make.leading.equalTo(avatarBg.snp.trailing).offset(5)
  236. make.trailing.equalToSuperview()
  237. }
  238. }
  239. func update(_ uid: String?) {
  240. curUid = uid
  241. guard let uid, !uid.isEmpty else {
  242. isHidden = true
  243. return
  244. }
  245. LNProfileManager.shared.getCachedProfileUserInfo(uid: uid, fetchIfNeeded: true)
  246. { [weak self] info in
  247. guard let self else { return }
  248. guard let info, info.uid == curUid else { return }
  249. nameLabel.text = info.name
  250. avatar.sd_setImage(with: URL(string: info.avatar))
  251. }
  252. }
  253. required init?(coder: NSCoder) {
  254. fatalError("init(coder:) has not been implemented")
  255. }
  256. }