MapAmapPopUpWindowView.swift 9.2 KB

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