MapAnnotationView.swift 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //
  2. // MapAnnotationView.swift
  3. // Runner
  4. //
  5. // Created by Groot on 2025/5/9.
  6. //
  7. import UIKit
  8. import MapKit
  9. class MapAnnotationView: MKAnnotationView {
  10. static let identifier: String = "MapAnnotationView"
  11. var marker: ATMapMarker? {
  12. didSet {
  13. updateView()
  14. }
  15. }
  16. var markerImageView: MapAnnotationMarkerImageView?
  17. var textView: MapAnnotationMarkerTextView?
  18. var batteryView : MapAnnotationBatteryView?
  19. // spacing between text and image
  20. let spacing: CGFloat = 6
  21. var annotationSelected: Bool {
  22. return (marker?.isSelected ?? false) || self.isSelected
  23. }
  24. override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
  25. super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
  26. self.marker = annotation as? ATMapMarker
  27. self.isEnabled = marker?.markerType.supportSelected ?? false
  28. self.isHidden = false
  29. }
  30. required init?(coder aDecoder: NSCoder) {
  31. fatalError("init(coder:) has not been implemented")
  32. }
  33. // MARK: - 视图更新与重用
  34. override func prepareForReuse() {
  35. super.prepareForReuse()
  36. // 清除当前所有子视图
  37. for subview in subviews {
  38. subview.removeFromSuperview()
  39. }
  40. markerImageView = nil
  41. textView = nil
  42. }
  43. private func updateView() {
  44. // 确保在更新视图前移除所有子视图
  45. for subview in subviews {
  46. subview.removeFromSuperview()
  47. }
  48. guard let marker = marker else { return }
  49. // 创建并添加图像视图
  50. markerImageView = MapAnnotationMarkerImageView(markerType: marker.markerType)
  51. if let url = marker.customAvatarUrl, !url.isEmpty {
  52. markerImageView?.loadNetworkImage(imageUrl: url)
  53. }
  54. if let markerImageView = markerImageView {
  55. markerImageView.isSelected = annotationSelected
  56. addSubview(markerImageView)
  57. }
  58. ///取出判断有没有用户的电池电量
  59. let tags = marker.tags
  60. if let electricValue = tags?["electric"],
  61. let batteryPercentage = Int(electricValue),
  62. batteryPercentage > 0 {
  63. batteryView = MapAnnotationBatteryView()
  64. batteryView?.backgroundColor = UIColor(hex:"#FFFFFF")
  65. batteryView?.frame = CGRect(x: 0, y: 0, width: 53, height: 20)
  66. batteryView?.borderWidth = 1.0
  67. batteryView?.contentPadding = 0.5
  68. batteryView?.headSpacing = 1.0
  69. batteryView?.batteryColor = UIColor(hex: "#4476FF") ?? UIColor.blue
  70. batteryView?.lowBatteryColor = UIColor(hex: "#FF5249") ?? UIColor.red
  71. batteryView?.lowBatteryThreshold = 30
  72. batteryView?.showPercentageText = true
  73. batteryView?.textFont = UIFont.boldSystemFont(ofSize: 10)
  74. batteryView?.textColor = UIColor(hex: "#666666") ?? UIColor.black
  75. batteryView?.layer.cornerRadius = 10
  76. batteryView?.layer.masksToBounds = true
  77. addSubview(batteryView ?? MapAnnotationBatteryView())
  78. batteryView?.percentage = batteryPercentage
  79. } else {
  80. // 如果有标题,添加文本视图
  81. if let markerName = marker.markerName, !markerName.isEmpty {
  82. textView = MapAnnotationMarkerTextView(text: markerName)
  83. if let textView = textView {
  84. addSubview(textView)
  85. }
  86. }
  87. }
  88. // 设置AutoLayout
  89. setupConstraints()
  90. }
  91. // MARK: - 布局
  92. private func setupConstraints() {
  93. // 禁用自动约束转换
  94. translatesAutoresizingMaskIntoConstraints = false
  95. guard let markerImageView = markerImageView else { return }
  96. markerImageView.translatesAutoresizingMaskIntoConstraints = false
  97. var markerSize = marker?.markerType.size ?? CGSize(width: 30, height: 30)
  98. if let url = marker?.customAvatarUrl, !url.isEmpty {
  99. markerSize = CGSize(width: 52, height: 52)
  100. markerImageView.layer.cornerRadius = 52 / 2
  101. markerImageView.layer.masksToBounds = true // 确保超出部分被裁剪
  102. }
  103. // 设置图像视图约束 - 居中显示在标记视图中
  104. NSLayoutConstraint.activate([
  105. markerImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
  106. markerImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
  107. markerImageView.widthAnchor.constraint(equalToConstant: markerSize.width),
  108. markerImageView.heightAnchor.constraint(equalToConstant: markerSize.height)
  109. ])
  110. // 设置文本视图约束 - 位于图像上方,居中对齐
  111. if let textView = textView {
  112. textView.translatesAutoresizingMaskIntoConstraints = false
  113. NSLayoutConstraint.activate([
  114. textView.centerXAnchor.constraint(equalTo: centerXAnchor),
  115. textView.bottomAnchor.constraint(equalTo: markerImageView.topAnchor, constant: -spacing)
  116. ])
  117. }
  118. ///电池电量
  119. if let batteryView = batteryView {
  120. batteryView.translatesAutoresizingMaskIntoConstraints = false
  121. NSLayoutConstraint.activate([
  122. batteryView.centerXAnchor.constraint(equalTo: centerXAnchor),
  123. batteryView.bottomAnchor.constraint(equalTo: markerImageView.topAnchor, constant: -spacing)
  124. ])
  125. }
  126. // 设置标记视图整体尺寸
  127. var textHeight = textView?.intrinsicContentSize.height ?? 0
  128. var totalHeight = textView != nil ?
  129. markerSize.height + textHeight + spacing :
  130. markerSize.height
  131. if let batteryView = batteryView {
  132. textHeight = batteryView.intrinsicContentSize.height
  133. totalHeight = markerSize.height + textHeight + spacing
  134. }
  135. // 宽度至少和markerImageView一样宽,高度根据是否有文本决定
  136. NSLayoutConstraint.activate([
  137. widthAnchor.constraint(greaterThanOrEqualTo: markerImageView.widthAnchor),
  138. heightAnchor.constraint(equalToConstant: totalHeight)
  139. ])
  140. // 更新偏移量
  141. updateCenterOffset()
  142. }
  143. // MARK: - 更新视图
  144. override func layoutSubviews() {
  145. super.layoutSubviews()
  146. updateCenterOffset()
  147. }
  148. private func updateCenterOffset() {
  149. let markerSize = marker?.markerType.size ?? CGSize(width: 30, height: 30)
  150. if let textView = textView {
  151. let textHeight = textView.frame.height
  152. let totalHeight = markerSize.height + textHeight + spacing
  153. // 有文本时,调整偏移让图片底部对准坐标点
  154. centerOffset = CGPoint(x: 0, y: -(totalHeight - markerSize.height)/2)
  155. } else if let batteryView = batteryView {
  156. let textHeight = batteryView.frame.height
  157. let totalHeight = markerSize.height + textHeight + spacing
  158. // 有文本时,调整偏移让图片底部对准坐标点
  159. centerOffset = CGPoint(x: 0, y: -(totalHeight - markerSize.height)/2)
  160. } else {
  161. // 没有文本时,仅需要将图片底部对准坐标点
  162. centerOffset = CGPoint(x: 0, y: -markerSize.height/2)
  163. }
  164. }
  165. override func prepareForDisplay() {
  166. super.prepareForDisplay()
  167. // 确保视图已经设置好
  168. if markerImageView == nil {
  169. updateView()
  170. }
  171. // 更新选中状态
  172. guard let marker = marker,
  173. let url = marker.customAvatarUrl,
  174. !url.isEmpty else {
  175. markerImageView?.isSelected = annotationSelected
  176. return
  177. }
  178. }
  179. override func setSelected(_ selected: Bool, animated: Bool) {
  180. super.setSelected(selected, animated: animated)
  181. marker?.isSelected = selected
  182. guard let marker = marker,
  183. let url = marker.customAvatarUrl,
  184. !url.isEmpty else {
  185. markerImageView?.isSelected = annotationSelected
  186. return
  187. }
  188. }
  189. }