QSLHomeFriendView.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. //
  2. // QSLHomeFriendView.swift
  3. // QuickSearchLocation
  4. //
  5. // Created by mac on 2024/4/12.
  6. //
  7. import UIKit
  8. enum FriendViewLocation {
  9. case ScrollTop
  10. case ScrollCenter
  11. case ScrollBottom
  12. }
  13. protocol QSLHomeFriendViewDelegate: NSObjectProtocol {
  14. func refreshBtnAction()
  15. func homeFriPhoneViewClick()
  16. func routeBtnAction(model: QSLUserModel)
  17. func locateBtnAction(model: QSLUserModel)
  18. func addFriendAction(isSmall: Bool)
  19. func viewDidScroll()
  20. }
  21. class QSLHomeFriendView: UIView {
  22. weak var delegate: QSLHomeFriendViewDelegate?
  23. var viewModel:QSLHomeViewModel? {
  24. didSet {
  25. friTableView.reloadData()
  26. }
  27. }
  28. /// 位置
  29. var scrollLocation: FriendViewLocation?
  30. /// 滑动到最后停下来的位置
  31. var scrollStopY: CGFloat?
  32. /// 滑动顶部距离顶部的距离
  33. var scrollTopHeight: CGFloat?
  34. /// 滑动中间距离顶部的距离
  35. var scrollCenterHeight: CGFloat?
  36. /// 滑动底部距离顶部的距离
  37. var scrollBottomHeight: CGFloat?
  38. lazy var friMapLogoImageView: UIImageView = {
  39. let imageView = UIImageView()
  40. imageView.isHidden = true
  41. imageView.image = UIImage(named: "home_friends_map_logo")
  42. return imageView
  43. }()
  44. lazy var friExpandButton: UIButton = {
  45. let button = UIButton(type: .custom)
  46. button.backgroundColor = UIColor.hexStringColor(hexString: "#333333", alpha: 0.6)
  47. button.addRadius(radius: 2)
  48. return button
  49. }()
  50. lazy var refreshButton: UIButton = {
  51. let button = UIButton(type: .custom)
  52. button.setBackgroundImage(UIImage(named: "home_refresh_btn"), for: .normal)
  53. button.addTarget(self, action: #selector(refreshBtnAction), for: .touchUpInside)
  54. return button
  55. }()
  56. lazy var friBgView: UIView = {
  57. let view = UIView()
  58. view.clipsToBounds = true
  59. view.backgroundColor = QSLColor.backGroundColor
  60. view.addRadius(radius: 12.rpx)
  61. return view
  62. }()
  63. lazy var friHeaderView: UIView = {
  64. let view = UIView(frame: CGRect(x: 0, y: 0, width: QSLConst.qsl_kScreenW, height: 61.rpx))
  65. if let image = UIImage.gradient([UIColor.hexStringColor(hexString: "#FFFFFF", alpha: 1), UIColor.hexStringColor(hexString: "#FFFFFF", alpha: 0)], size: CGSize(width: qsl_kScreenW, height: 61.rpx), locations: [0, 1], direction: .vertical) {
  66. view.backgroundColor = UIColor(patternImage: image)
  67. }
  68. view.addFourCorner(topLeft: 12.rpx, topRight: 12.rpx, bottomLeft: 0, bottomRight: 0)
  69. return view
  70. }()
  71. lazy var friHeaderTitleIcon: UIImageView = {
  72. let imageView = UIImageView()
  73. imageView.image = UIImage(named: "home_friends_header_title")
  74. return imageView
  75. }()
  76. lazy var friHeaderAddBtn: UIButton = {
  77. let btn = UIButton()
  78. btn.setBackgroundImage(UIImage(named: "home_friends_header_add_btn_bg"), for: .normal)
  79. btn.image(UIImage(named: "home_friends_header_add_icon"))
  80. btn.title("添加好友")
  81. btn.mediumFont(15)
  82. btn.textColor(.white)
  83. btn.setImageTitleLayout(.imgLeft, spacing: 0)
  84. btn.addTarget(self, action: #selector(topAddButtonAction), for: .touchUpInside)
  85. return btn
  86. }()
  87. lazy var friTableView: UITableView = {
  88. let tableView = UITableView(frame: .zero, style: .grouped)
  89. tableView.backgroundColor = .clear
  90. tableView.separatorStyle = .none
  91. tableView.showsVerticalScrollIndicator = false
  92. tableView.delegate = self
  93. tableView.dataSource = self
  94. tableView.tableViewNeverAdjustContentInset()
  95. tableView.bounces = false
  96. tableView.isUserInteractionEnabled = true
  97. tableView.isScrollEnabled = false
  98. tableView.contentInsetAdjustmentBehavior = .never
  99. tableView.register(cellClass: QSLHomeFriendTableViewCell.self)
  100. return tableView
  101. }()
  102. override init(frame: CGRect) {
  103. super.init(frame: frame)
  104. self.scrollCenterHeight = self.qsl_top
  105. self.scrollBottomHeight = qsl_kScreenH - qsl_kTabbarFrameH - 56 - 40
  106. self.setupUI()
  107. self.addPanAction()
  108. }
  109. required init?(coder: NSCoder) {
  110. fatalError("init(coder:) has not been implemented")
  111. }
  112. @objc func refreshBtnAction() {
  113. delegate?.refreshBtnAction()
  114. }
  115. }
  116. // MARK: - 设置UI
  117. extension QSLHomeFriendView {
  118. func setupUI() {
  119. addSubview(friBgView)
  120. addSubview(friTableView)
  121. addSubview(friMapLogoImageView)
  122. addSubview(friExpandButton)
  123. addSubview(refreshButton)
  124. addSubview(friHeaderView)
  125. friHeaderView.addSubview(friHeaderTitleIcon)
  126. friHeaderView.addSubview(friHeaderAddBtn)
  127. friMapLogoImageView.snp.makeConstraints { make in
  128. make.size.equalTo(CGSize(width: 62, height: 20))
  129. make.top.equalTo(0)
  130. make.left.equalTo(12)
  131. }
  132. friExpandButton.snp.makeConstraints { make in
  133. make.size.equalTo(CGSize(width: 80, height: 4))
  134. make.top.equalTo(friMapLogoImageView.snp.bottom).offset(8)
  135. make.centerX.equalTo(snp.centerX)
  136. }
  137. friBgView.snp.makeConstraints { make in
  138. make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx)
  139. make.left.bottom.right.equalTo(0)
  140. }
  141. refreshButton.snp.makeConstraints { make in
  142. make.size.equalTo(CGSize(width: 40.rpx, height: 40.rpx))
  143. make.right.equalTo(-8.rpx)
  144. make.bottom.equalTo(friBgView.snp.top).offset(-12.rpx)
  145. }
  146. friHeaderView.snp.makeConstraints { make in
  147. make.left.right.equalTo(0)
  148. make.height.equalTo(61.rpx)
  149. make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx)
  150. }
  151. friHeaderTitleIcon.snp.makeConstraints { make in
  152. make.size.equalTo(CGSize(width: 82.rpx, height: 26.5.rpx))
  153. make.left.equalTo(16.rpx)
  154. make.top.equalTo(16.rpx)
  155. }
  156. friHeaderAddBtn.snp.makeConstraints { make in
  157. make.size.equalTo(CGSize(width: 106.rpx, height: 32.rpx))
  158. make.right.equalTo(-8.rpx)
  159. make.centerY.equalTo(friHeaderTitleIcon.snp.centerY)
  160. }
  161. friTableView.snp.makeConstraints { make in
  162. make.left.equalTo(0)
  163. make.right.equalTo(0)
  164. make.top.equalTo(friBgView.snp.top).offset(50.rpx)
  165. make.bottom.equalTo(-12.rpx)
  166. }
  167. }
  168. }
  169. // MARK: - 点击事件
  170. extension QSLHomeFriendView {
  171. @objc func homeFriPhoneViewAction() {
  172. delegate?.homeFriPhoneViewClick()
  173. }
  174. @objc func topAddButtonAction() {
  175. delegate?.addFriendAction(isSmall: true)
  176. }
  177. }
  178. extension QSLHomeFriendView: QSLHomeFriendTableViewCellDelegate {
  179. func routeBtnAction(model: QSLUserModel) {
  180. delegate?.routeBtnAction(model: model)
  181. }
  182. func locateBtnAction(model: QSLUserModel) {
  183. delegate?.locateBtnAction(model: model)
  184. }
  185. }
  186. // MARK: - 添加滑动逻辑
  187. extension QSLHomeFriendView: UIGestureRecognizerDelegate {
  188. func addPanAction() {
  189. let pan = UIPanGestureRecognizer(target: self, action: #selector(panAction))
  190. pan.delegate = self
  191. self.addGestureRecognizer(pan)
  192. }
  193. @objc func panAction(pan: UIPanGestureRecognizer) {
  194. let point = pan.translation(in: self)
  195. if let scrollStopY = self.scrollStopY, scrollStopY > 0 {
  196. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  197. return
  198. }
  199. self.qsl_top += point.y
  200. if let scrollTopHeight = self.scrollTopHeight, self.qsl_top < scrollTopHeight {
  201. self.qsl_top = scrollTopHeight
  202. }
  203. if let scrollBottomHeight = self.scrollBottomHeight, self.qsl_top > scrollBottomHeight {
  204. self.qsl_top = scrollBottomHeight
  205. }
  206. if pan.state == .ended || pan.state == .cancelled {
  207. let velocity = pan.velocity(in: self)
  208. let speed = 350.0
  209. if let scrollLocation = self.scrollLocation {
  210. switch scrollLocation {
  211. case .ScrollTop:
  212. if velocity.y < -speed {
  213. self.scrollToLocation(type: .ScrollTop)
  214. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  215. return
  216. } else if velocity.y > speed {
  217. self.scrollToLocation(type: .ScrollCenter)
  218. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  219. return
  220. }
  221. break
  222. case .ScrollCenter:
  223. if velocity.y < -speed {
  224. self.scrollToLocation(type: .ScrollTop)
  225. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  226. return
  227. } else if velocity.y > speed {
  228. self.scrollToLocation(type: .ScrollBottom)
  229. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  230. return
  231. }
  232. break
  233. case .ScrollBottom:
  234. if velocity.y < -speed {
  235. self.scrollToLocation(type: .ScrollCenter)
  236. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  237. return
  238. } else if velocity.y > speed {
  239. self.scrollToLocation(type: .ScrollBottom)
  240. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  241. return
  242. }
  243. break
  244. }
  245. }
  246. if self.qsl_top < (qsl_kScreenH - qsl_kTabbarBottom) / 4 {
  247. self.scrollToLocation(type: .ScrollTop)
  248. } else if self.qsl_top < (qsl_kScreenH - qsl_kTabbarBottom) / 4 * 3 && self.qsl_top > (qsl_kScreenH - qsl_kTabbarBottom) / 4 {
  249. self.scrollToLocation(type: .ScrollCenter)
  250. } else {
  251. self.scrollToLocation(type: .ScrollBottom)
  252. }
  253. }
  254. pan.setTranslation(CGPoint(x: 0, y: 0), in: self)
  255. }
  256. func scrollToLocation(type: FriendViewLocation) {
  257. let animation = CASpringAnimation(keyPath: "position.y")
  258. animation.damping = 10
  259. animation.stiffness = 200
  260. animation.mass = 1
  261. animation.initialVelocity = 10
  262. animation.duration = animation.settlingDuration
  263. // animation.fromValue = self.friPhoneView.layer.position.y
  264. // animation.toValue = self.friPhoneView.layer.position.y + 2
  265. animation.isRemovedOnCompletion = false
  266. animation.fillMode = .forwards
  267. let animation1 = CASpringAnimation(keyPath: "position.y")
  268. animation1.damping = 10
  269. animation1.stiffness = 200
  270. animation1.mass = 1
  271. animation1.initialVelocity = 10
  272. animation1.duration = animation.settlingDuration
  273. animation1.fromValue = self.friTableView.layer.position.y
  274. animation1.toValue = self.friTableView.layer.position.y + 2
  275. animation1.isRemovedOnCompletion = false
  276. animation1.fillMode = .forwards
  277. self.scrollLocation = type
  278. if let topHeight = self.scrollTopHeight, let centerHeight = self.scrollCenterHeight, let bottomHeight = self.scrollBottomHeight {
  279. switch type {
  280. case .ScrollTop:
  281. UIView.animate(withDuration: 0.2) {
  282. self.qsl_top = topHeight
  283. } completion: { finished in
  284. self.friTableView.isScrollEnabled = true
  285. // self.friPhoneView.layer.add(animation, forKey: "animation")
  286. self.friTableView.layer.add(animation1, forKey: "animation")
  287. }
  288. break
  289. case .ScrollCenter:
  290. UIView.animate(withDuration: 0.2) {
  291. self.qsl_top = centerHeight
  292. } completion: { finished in
  293. self.friTableView.isScrollEnabled = false
  294. // self.friPhoneView.layer.add(animation, forKey: "animation")
  295. self.friTableView.layer.add(animation1, forKey: "animation")
  296. }
  297. break
  298. case .ScrollBottom:
  299. UIView.animate(withDuration: 0.2) {
  300. self.qsl_top = bottomHeight
  301. } completion: { finished in
  302. self.friTableView.isScrollEnabled = false
  303. // self.friPhoneView.layer.add(animation, forKey: "animation")
  304. self.friTableView.layer.add(animation1, forKey: "animation")
  305. }
  306. break
  307. }
  308. }
  309. delegate?.viewDidScroll()
  310. }
  311. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
  312. return true
  313. }
  314. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  315. let currentPostion = scrollView.contentOffset.y
  316. self.scrollStopY = currentPostion
  317. }
  318. }
  319. extension QSLHomeFriendView: QSLHomeFriendFooterViewDelegate {
  320. func addButtonAction() {
  321. delegate?.addFriendAction(isSmall: false)
  322. }
  323. }
  324. // MARK: - 设置Tableview
  325. extension QSLHomeFriendView: UITableViewDelegate, UITableViewDataSource {
  326. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  327. guard let friendList = viewModel?.friendList else { return 0 }
  328. return friendList.count
  329. }
  330. func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  331. // if indexPath.row == 0 {
  332. //
  333. // cell.addCorner(conrners: [.topRight, .topLeft], radius: 6)
  334. // }
  335. //
  336. // if let friendList = viewModel?.friendList, indexPath.row == friendList.count - 1 {
  337. //
  338. // cell.addCorner(conrners: [.bottomLeft, .bottomRight], radius: 6)
  339. // }
  340. }
  341. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  342. let cell = tableView.dequeueReusableCell(cellType: QSLHomeFriendTableViewCell.self, cellForRowAt: indexPath)
  343. cell.selectionStyle = .none
  344. cell.delegate = self
  345. if let model = viewModel?.friendList[indexPath.row] {
  346. cell.config(model: model)
  347. }
  348. return cell
  349. }
  350. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  351. return 94.rpx
  352. }
  353. func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  354. return 0.0001
  355. }
  356. func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
  357. return 200.rpx
  358. }
  359. func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
  360. let view = QSLHomeFriendFooterView()
  361. view.delegate = self
  362. return view
  363. }
  364. }