MapAmapPopUpWindowView.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. //
  2. // MapAmapPopUpWindowView.swift
  3. // map_amap_ios
  4. //
  5. // Created by 诺诺诺的言 on 2025/7/22.
  6. //
  7. import Foundation
  8. import MapKit
  9. class MapAmapPopUpWindowView : MKAnnotationView {
  10. static let identifier: String = "MapAnotationBubbleView"
  11. var marker: ATMapMarker? {
  12. didSet {
  13. updateView()
  14. }
  15. }
  16. private var allContentView : UIView!
  17. private var markerContentView : UIView!
  18. private var bgImageView : UIImageView!
  19. private var titleLabelView: UILabel!
  20. private var bottomView : UIView!
  21. private var triangleLayer: CAShapeLayer!
  22. private var bubbleHeight: CGFloat = 24
  23. private var triangleHeight: CGFloat = 6
  24. // spacing between text and image
  25. let spacing: CGFloat = 6
  26. var annotationSelected: Bool {
  27. return (marker?.isSelected ?? false) || self.isSelected
  28. }
  29. override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
  30. super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
  31. self.marker = annotation as? ATMapMarker
  32. self.isEnabled = marker?.markerType.supportSelected ?? false
  33. self.isHidden = false
  34. }
  35. required init?(coder aDecoder: NSCoder) {
  36. fatalError("init(coder:) has not been implemented")
  37. }
  38. // MARK: - 视图更新与重用
  39. override func prepareForReuse() {
  40. super.prepareForReuse()
  41. // 清除当前所有子视图
  42. for subview in subviews {
  43. subview.removeFromSuperview()
  44. }
  45. markerContentView = nil
  46. markerContentView = nil
  47. }
  48. private func updateView() {
  49. // 确保在更新视图前移除所有子视图
  50. for subview in subviews {
  51. subview.removeFromSuperview()
  52. }
  53. guard let marker = marker else { return }
  54. let tags = marker.tags
  55. let mainTiel : String = tags?["title"] ?? "";
  56. let mainDesc : String = tags?["desc"] ?? "";
  57. let markerContentViewW = 170
  58. markerContentView = UIView()
  59. markerContentView.backgroundColor = UIColor(hex: "#FFFFFF")
  60. markerContentView.layer.cornerRadius = 12
  61. markerContentView.layer.masksToBounds = true
  62. markerContentView.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.08).cgColor
  63. markerContentView.layer.shadowOpacity = 1
  64. markerContentView.layer.shadowRadius = 4
  65. markerContentView.layer.shadowOffset = CGSize(width: 0, height: 4)
  66. addSubview(markerContentView)
  67. titleLabelView = UILabel()
  68. titleLabelView.translatesAutoresizingMaskIntoConstraints = false // 关键
  69. titleLabelView.text = mainTiel
  70. titleLabelView.font = UIFont.systemFont(ofSize: 10, weight: .bold)
  71. titleLabelView.textColor = UIColor(hex: "#333333")
  72. titleLabelView.backgroundColor = .clear
  73. titleLabelView.textAlignment = .left
  74. titleLabelView.numberOfLines = 0
  75. markerContentView.addSubview(titleLabelView)
  76. bottomView = UIView()
  77. bottomView.translatesAutoresizingMaskIntoConstraints = false // 关键
  78. markerContentView.addSubview(bottomView)
  79. let mapIconView = UIImageView(frame: CGRect(x: 0, y: 0, width: 12, height: 12))
  80. mapIconView.image = readImageContentFrom(imageName: "com.shishi.dingwei_map_pin.png")
  81. mapIconView.contentMode = .scaleAspectFit
  82. bottomView.addSubview(mapIconView)
  83. let mapLableW = markerContentViewW - 16 - 14
  84. let mapLable = UILabel(frame: CGRect(x: 14, y: 0, width: mapLableW, height: 14))
  85. mapLable.text = mainDesc
  86. mapLable.font = UIFont.systemFont(ofSize: 10, weight: .regular)
  87. mapLable.textColor = UIColor(hex: "#666666")
  88. mapLable.backgroundColor = .clear
  89. mapLable.textAlignment = .left
  90. mapLable.lineBreakMode = .byTruncatingTail
  91. mapLable.numberOfLines = 1 // 限制为单行
  92. bottomView.addSubview(mapLable)
  93. // 创建气泡三角形
  94. triangleLayer = CAShapeLayer()
  95. triangleLayer.fillColor = UIColor.white.cgColor
  96. let trianglePath = UIBezierPath()
  97. trianglePath.move(to: CGPoint(x: 0, y: 0)) // 左点
  98. trianglePath.addLine(to: CGPoint(x: 5, y: 6)) // 底点
  99. trianglePath.addLine(to: CGPoint(x: 10, y: 0)) // 右点
  100. trianglePath.close()
  101. triangleLayer.path = trianglePath.cgPath
  102. layer.addSublayer(triangleLayer)
  103. // 设置AutoLayout
  104. setupConstraints()
  105. }
  106. // MARK: - 布局
  107. private func setupConstraints() {
  108. // 禁用自动约束转换
  109. translatesAutoresizingMaskIntoConstraints = false
  110. guard let marker = marker else { return }
  111. let tags = marker.tags
  112. let mainTiel : String = tags?["title"] ?? "";
  113. let markerContentViewW : CGFloat = 170
  114. let mainTitleH : CGFloat = mainTiel.height(withConstrainedWidth: CGFloat(markerContentViewW - 16), font: UIFont.systemFont(ofSize: 10, weight: .bold))
  115. let markerContentViewH : CGFloat = CGFloat(mainTitleH) + 8 + 14 + 5 + 8
  116. bubbleHeight = markerContentViewH;
  117. //let markerSize = CGSize(width: markerContentViewW, height: markerContentViewH)
  118. // 设置图像视图约束 - 居中显示在标记视图中
  119. guard let markerContentView = markerContentView else { return }
  120. markerContentView.translatesAutoresizingMaskIntoConstraints = false
  121. NSLayoutConstraint.activate([
  122. markerContentView.centerXAnchor.constraint(equalTo: centerXAnchor),
  123. markerContentView.centerYAnchor.constraint(equalTo: centerYAnchor),
  124. markerContentView.widthAnchor.constraint(equalToConstant: markerContentViewW),
  125. markerContentView.heightAnchor.constraint(equalToConstant: markerContentViewH)
  126. ])
  127. // 标题(动态高度)
  128. NSLayoutConstraint.activate([
  129. titleLabelView.topAnchor.constraint(equalTo: markerContentView.topAnchor, constant: 8),
  130. titleLabelView.leadingAnchor.constraint(equalTo: markerContentView.leadingAnchor, constant: 8),
  131. titleLabelView.trailingAnchor.constraint(equalTo: markerContentView.trailingAnchor, constant: -8),
  132. titleLabelView.heightAnchor.constraint(equalToConstant: mainTitleH)
  133. // 移除固定高度约束,让标题根据内容自动调整
  134. ])
  135. // 底部区域(动态高度)
  136. NSLayoutConstraint.activate([
  137. bottomView.topAnchor.constraint(equalTo: titleLabelView.bottomAnchor, constant: 5),
  138. bottomView.leadingAnchor.constraint(equalTo: markerContentView.leadingAnchor, constant: 8),
  139. bottomView.trailingAnchor.constraint(equalTo: markerContentView.trailingAnchor, constant: -8),
  140. bottomView.bottomAnchor.constraint(equalTo: markerContentView.bottomAnchor, constant: -8)
  141. // 移除固定高度约束,让底部视图根据内容自动调整
  142. ])
  143. // 设置整个视图的高度(包含三角形)和最小宽度
  144. NSLayoutConstraint.activate([
  145. heightAnchor.constraint(equalToConstant: bubbleHeight + 6),
  146. widthAnchor.constraint(greaterThanOrEqualToConstant: markerContentViewW)
  147. ])
  148. layoutIfNeeded()
  149. updateTrianglePosition()
  150. // 更新偏移量
  151. //updateCenterOffset()
  152. }
  153. private func readImageContentFrom(imageName : String) -> UIImage {
  154. if let image = UIImage(named: imageName) {
  155. return image
  156. }
  157. // 尝试从插件资源束加载
  158. let bundleURL = Bundle(for: MapAnnotationView.self).url(forResource: "map_mapkit_ios_resources", withExtension: "bundle")
  159. if let bundleURL = bundleURL, let resourceBundle = Bundle(url: bundleURL) {
  160. return UIImage(named: imageName, in: resourceBundle, compatibleWith: nil) ?? UIImage()
  161. }
  162. return UIImage()
  163. }
  164. // MARK: - 更新视图
  165. private func updateCenterOffset() {
  166. let markerSize = CGSize(width: bubbleHeight + 6, height: 30)
  167. // 没有文本时,仅需要将图片底部对准坐标点
  168. centerOffset = CGPoint(x: 0, y: -markerSize.height/2)
  169. }
  170. override func prepareForDisplay() {
  171. super.prepareForDisplay()
  172. // 确保视图已经设置好
  173. }
  174. override func setSelected(_ selected: Bool, animated: Bool) {
  175. super.setSelected(selected, animated: animated)
  176. }
  177. override func layoutSubviews() {
  178. super.layoutSubviews()
  179. updateTrianglePosition()
  180. updateCenterOffset()
  181. }
  182. private func updateTrianglePosition() {
  183. // 更新三角形位置到底部中心
  184. let width = bounds.width
  185. triangleLayer.frame = CGRect(x: (width - 10) / 2, y: bubbleHeight + 3, width: 10, height: triangleHeight)
  186. }
  187. }