MapAnnotationView.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. // spacing between text and image
  19. let spacing: CGFloat = 6
  20. var annotationSelected: Bool {
  21. return (marker?.isSelected ?? false) || self.isSelected
  22. }
  23. override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
  24. super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
  25. self.marker = annotation as? ATMapMarker
  26. self.isEnabled = marker?.markerType.supportSelected ?? false
  27. self.isHidden = false
  28. }
  29. required init?(coder aDecoder: NSCoder) {
  30. fatalError("init(coder:) has not been implemented")
  31. }
  32. // MARK: - 视图更新与重用
  33. override func prepareForReuse() {
  34. super.prepareForReuse()
  35. // 清除当前所有子视图
  36. for subview in subviews {
  37. subview.removeFromSuperview()
  38. }
  39. markerImageView = nil
  40. textView = nil
  41. }
  42. private func updateView() {
  43. // 确保在更新视图前移除所有子视图
  44. for subview in subviews {
  45. subview.removeFromSuperview()
  46. }
  47. guard let marker = marker else { return }
  48. // 创建并添加图像视图
  49. markerImageView = MapAnnotationMarkerImageView(markerType: marker.markerType)
  50. if let url = marker.customAvatarUrl, !url.isEmpty {
  51. markerImageView?.loadNetworkImage(imageUrl: url)
  52. }
  53. if let markerImageView = markerImageView {
  54. markerImageView.isSelected = annotationSelected
  55. addSubview(markerImageView)
  56. }
  57. // 如果有标题,添加文本视图
  58. if let markerName = marker.markerName, !markerName.isEmpty {
  59. textView = MapAnnotationMarkerTextView(text: markerName)
  60. if let textView = textView {
  61. addSubview(textView)
  62. }
  63. }
  64. // 设置AutoLayout
  65. setupConstraints()
  66. }
  67. // MARK: - 布局
  68. private func setupConstraints() {
  69. // 禁用自动约束转换
  70. translatesAutoresizingMaskIntoConstraints = false
  71. guard let markerImageView = markerImageView else { return }
  72. markerImageView.translatesAutoresizingMaskIntoConstraints = false
  73. var markerSize = marker?.markerType.size ?? CGSize(width: 30, height: 30)
  74. if let url = marker?.customAvatarUrl, !url.isEmpty {
  75. markerSize = CGSize(width: 52, height: 52)
  76. markerImageView.layer.cornerRadius = 52 / 2
  77. markerImageView.layer.masksToBounds = true // 确保超出部分被裁剪
  78. }
  79. // 设置图像视图约束 - 居中显示在标记视图中
  80. NSLayoutConstraint.activate([
  81. markerImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
  82. markerImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
  83. markerImageView.widthAnchor.constraint(equalToConstant: markerSize.width),
  84. markerImageView.heightAnchor.constraint(equalToConstant: markerSize.height)
  85. ])
  86. // 设置文本视图约束 - 位于图像上方,居中对齐
  87. if let textView = textView {
  88. textView.translatesAutoresizingMaskIntoConstraints = false
  89. NSLayoutConstraint.activate([
  90. textView.centerXAnchor.constraint(equalTo: centerXAnchor),
  91. textView.bottomAnchor.constraint(equalTo: markerImageView.topAnchor, constant: -spacing)
  92. ])
  93. }
  94. // 设置标记视图整体尺寸
  95. let textHeight = textView?.intrinsicContentSize.height ?? 0
  96. let totalHeight = textView != nil ?
  97. markerSize.height + textHeight + spacing :
  98. markerSize.height
  99. // 宽度至少和markerImageView一样宽,高度根据是否有文本决定
  100. NSLayoutConstraint.activate([
  101. widthAnchor.constraint(greaterThanOrEqualTo: markerImageView.widthAnchor),
  102. heightAnchor.constraint(equalToConstant: totalHeight)
  103. ])
  104. // 更新偏移量
  105. updateCenterOffset()
  106. }
  107. // MARK: - 更新视图
  108. override func layoutSubviews() {
  109. super.layoutSubviews()
  110. updateCenterOffset()
  111. }
  112. private func updateCenterOffset() {
  113. let markerSize = marker?.markerType.size ?? CGSize(width: 30, height: 30)
  114. if let textView = textView {
  115. let textHeight = textView.frame.height
  116. let totalHeight = markerSize.height + textHeight + spacing
  117. // 有文本时,调整偏移让图片底部对准坐标点
  118. centerOffset = CGPoint(x: 0, y: -(totalHeight - markerSize.height)/2)
  119. } else {
  120. // 没有文本时,仅需要将图片底部对准坐标点
  121. centerOffset = CGPoint(x: 0, y: -markerSize.height/2)
  122. }
  123. }
  124. override func prepareForDisplay() {
  125. super.prepareForDisplay()
  126. // 确保视图已经设置好
  127. if markerImageView == nil {
  128. updateView()
  129. }
  130. // 更新选中状态
  131. markerImageView?.isSelected = annotationSelected
  132. }
  133. override func setSelected(_ selected: Bool, animated: Bool) {
  134. super.setSelected(selected, animated: animated)
  135. marker?.isSelected = selected
  136. markerImageView?.isSelected = annotationSelected
  137. }
  138. }