LNIMChatGameMateSkillView.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. //
  2. // LNIMChatGameMateSkillView.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/11.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. import Combine
  11. class LNIMChatGameMateSkillView: UIView {
  12. private let scrollView = UIScrollView()
  13. private let stackView = UIStackView()
  14. private var skills: [LNGameMateSkillVO] = []
  15. private var itemViews: [LNIMChatGameMateSkillCell] = []
  16. private var curPage: Int = 0
  17. weak var viewModel: LNIMChatViewModel? {
  18. didSet {
  19. viewModel?.$myOrders.sink { [weak self] list in
  20. guard let self else { return }
  21. isHidden = viewModel?.userInfo?.playmate != true
  22. || viewModel?.peerSkills.isEmpty != false
  23. || !list.isEmpty
  24. }.store(in: &bag)
  25. viewModel?.$peerSkills.sink { [weak self] skills in
  26. guard let self else { return }
  27. isHidden = viewModel?.userInfo?.playmate != true
  28. || skills.isEmpty
  29. || viewModel?.myOrders.isEmpty != true
  30. self.skills = skills
  31. buildSkillViews()
  32. }.store(in: &bag)
  33. viewModel?.$userInfo.sink { [weak self] info in
  34. guard let self else { return }
  35. isHidden = info?.playmate != true
  36. || viewModel?.peerSkills.isEmpty != false
  37. || viewModel?.myOrders.isEmpty != true
  38. }.store(in: &bag)
  39. }
  40. }
  41. override init(frame: CGRect) {
  42. super.init(frame: frame)
  43. setupViews()
  44. }
  45. override func layoutSubviews() {
  46. super.layoutSubviews()
  47. let offset = CGFloat(curPage) * scrollView.bounds.width
  48. if scrollView.contentOffset.x != offset {
  49. scrollView.contentOffset.x = offset
  50. }
  51. }
  52. required init?(coder: NSCoder) {
  53. fatalError("init(coder:) has not been implemented")
  54. }
  55. }
  56. extension LNIMChatGameMateSkillView: LNIMChatGameMateSkillCellDelegate {
  57. func onIMChatGameMateSkillCell(cell: LNIMChatGameMateSkillCell, didClickOrder skill: LNGameMateSkillVO) {
  58. guard let userInfo = viewModel?.userInfo else { return }
  59. let panel = LNCreateOrderPanel()
  60. panel.update(skill, user: userInfo)
  61. panel.editable = true
  62. panel.showIn()
  63. }
  64. }
  65. extension LNIMChatGameMateSkillView: UIScrollViewDelegate {
  66. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  67. guard skills.count > 2 else { return }
  68. if scrollView.contentOffset.x < scrollView.bounds.width * 0.5 {
  69. curPage -= 1
  70. if curPage < 0 {
  71. curPage = skills.count - 1
  72. }
  73. let last = itemViews.removeLast()
  74. stackView.removeArrangedSubview(last)
  75. last.removeFromSuperview()
  76. itemViews.insert(last, at: 0)
  77. stackView.insertArrangedSubview(last, at: 0)
  78. last.snp.makeConstraints { make in
  79. make.width.equalTo(scrollView)
  80. make.height.equalToSuperview()
  81. }
  82. let skill = skills[curPage > 0 ? curPage - 1 : skills.count - 1]
  83. last.update(skill)
  84. scrollView.contentOffset.x = scrollView.contentOffset.x + scrollView.bounds.width
  85. } else if scrollView.contentOffset.x > scrollView.bounds.width * 1.5 {
  86. curPage += 1
  87. if curPage > skills.count - 1 {
  88. curPage = 0
  89. }
  90. let first = itemViews.removeFirst()
  91. stackView.removeArrangedSubview(first)
  92. first.removeFromSuperview()
  93. itemViews.append(first)
  94. stackView.addArrangedSubview(first)
  95. first.snp.makeConstraints { make in
  96. make.width.equalTo(scrollView)
  97. make.height.equalToSuperview()
  98. }
  99. let skill = skills[curPage < skills.count - 1 ? curPage + 1 : 0]
  100. first.update(skill)
  101. scrollView.contentOffset.x = scrollView.contentOffset.x - scrollView.bounds.width
  102. }
  103. }
  104. }
  105. extension LNIMChatGameMateSkillView {
  106. private func buildSkillViews() {
  107. if skills.count < 3 {
  108. scrollView.setContentOffset(.init(x: 0, y: 0), animated: false)
  109. for (index, skill) in skills.enumerated() {
  110. itemViews[index].isHidden = false
  111. itemViews[index].update(skill)
  112. }
  113. for index in skills.count..<itemViews.count {
  114. itemViews[index].isHidden = true
  115. }
  116. } else {
  117. scrollView.setContentOffset(.init(x: scrollView.bounds.width, y: 0), animated: false)
  118. for index in 0..<itemViews.count {
  119. itemViews[index].isHidden = false
  120. }
  121. itemViews[0].update(skills[skills.count - 1])
  122. itemViews[1].update(skills[0])
  123. itemViews[2].update(skills[1])
  124. }
  125. curPage = 0
  126. }
  127. private func setupViews() {
  128. clipsToBounds = true
  129. snp.makeConstraints { make in
  130. make.height.equalTo(72).priority(.medium)
  131. }
  132. scrollView.backgroundColor = .clear
  133. scrollView.clipsToBounds = false
  134. scrollView.isPagingEnabled = true
  135. scrollView.showsVerticalScrollIndicator = false
  136. scrollView.showsHorizontalScrollIndicator = false
  137. scrollView.delegate = self
  138. addSubview(scrollView)
  139. scrollView.snp.makeConstraints { make in
  140. make.directionalHorizontalEdges.equalToSuperview().inset(14)
  141. make.bottom.equalToSuperview()
  142. make.top.equalToSuperview().offset(8)
  143. }
  144. let fakeView = UIView()
  145. scrollView.addSubview(fakeView)
  146. fakeView.snp.makeConstraints { make in
  147. make.directionalVerticalEdges.equalToSuperview()
  148. make.leading.equalToSuperview()
  149. make.width.equalTo(0)
  150. }
  151. stackView.axis = .horizontal
  152. stackView.spacing = 0
  153. scrollView.addSubview(stackView)
  154. stackView.snp.makeConstraints { make in
  155. make.directionalEdges.equalToSuperview()
  156. make.height.equalToSuperview()
  157. }
  158. for _ in 0..<3 {
  159. let itemView = LNIMChatGameMateSkillCell()
  160. itemView.delegate = self
  161. stackView.addArrangedSubview(itemView)
  162. itemView.snp.makeConstraints { make in
  163. make.width.equalTo(scrollView)
  164. make.height.equalToSuperview()
  165. }
  166. itemViews.append(itemView)
  167. }
  168. }
  169. }