MapAmapPopUpWindowView.swift 9.1 KB

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