// // MapAnnotationView.swift // Runner // // Created by Groot on 2025/5/9. // import UIKit import MapKit class MapAnnotationView: MKAnnotationView { static let identifier: String = "MapAnnotationView" var marker: ATMapMarker? { didSet { updateView() } } var markerImageView: MapAnnotationMarkerImageView? var textView: MapAnnotationMarkerTextView? // spacing between text and image let spacing: CGFloat = 6 var annotationSelected: Bool { return (marker?.isSelected ?? false) || self.isSelected } override init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) self.marker = annotation as? ATMapMarker self.isEnabled = marker?.markerType.supportSelected ?? false self.isHidden = false } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - 视图更新与重用 override func prepareForReuse() { super.prepareForReuse() // 清除当前所有子视图 for subview in subviews { subview.removeFromSuperview() } markerImageView = nil textView = nil } private func updateView() { // 确保在更新视图前移除所有子视图 for subview in subviews { subview.removeFromSuperview() } guard let marker = marker else { return } // 创建并添加图像视图 markerImageView = MapAnnotationMarkerImageView(markerType: marker.markerType) if let markerImageView = markerImageView { markerImageView.isSelected = annotationSelected addSubview(markerImageView) } // 如果有标题,添加文本视图 if let markerName = marker.markerName, !markerName.isEmpty { textView = MapAnnotationMarkerTextView(text: markerName) if let textView = textView { addSubview(textView) } } // 设置AutoLayout setupConstraints() } // MARK: - 布局 private func setupConstraints() { // 禁用自动约束转换 translatesAutoresizingMaskIntoConstraints = false guard let markerImageView = markerImageView else { return } markerImageView.translatesAutoresizingMaskIntoConstraints = false let markerSize = marker?.markerType.size ?? CGSize(width: 30, height: 30) // 设置图像视图约束 - 居中显示在标记视图中 NSLayoutConstraint.activate([ markerImageView.centerXAnchor.constraint(equalTo: centerXAnchor), markerImageView.centerYAnchor.constraint(equalTo: centerYAnchor), markerImageView.widthAnchor.constraint(equalToConstant: markerSize.width), markerImageView.heightAnchor.constraint(equalToConstant: markerSize.height) ]) // 设置文本视图约束 - 位于图像上方,居中对齐 if let textView = textView { textView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ textView.centerXAnchor.constraint(equalTo: centerXAnchor), textView.bottomAnchor.constraint(equalTo: markerImageView.topAnchor, constant: -spacing) ]) } // 设置标记视图整体尺寸 let textHeight = textView?.intrinsicContentSize.height ?? 0 let totalHeight = textView != nil ? markerSize.height + textHeight + spacing : markerSize.height // 宽度至少和markerImageView一样宽,高度根据是否有文本决定 NSLayoutConstraint.activate([ widthAnchor.constraint(greaterThanOrEqualTo: markerImageView.widthAnchor), heightAnchor.constraint(equalToConstant: totalHeight) ]) // 更新偏移量 updateCenterOffset() } // MARK: - 更新视图 override func layoutSubviews() { super.layoutSubviews() updateCenterOffset() } private func updateCenterOffset() { let markerSize = marker?.markerType.size ?? CGSize(width: 30, height: 30) if let textView = textView { let textHeight = textView.frame.height let totalHeight = markerSize.height + textHeight + spacing // 有文本时,调整偏移让图片底部对准坐标点 centerOffset = CGPoint(x: 0, y: -(totalHeight - markerSize.height)/2) } else { // 没有文本时,仅需要将图片底部对准坐标点 centerOffset = CGPoint(x: 0, y: -markerSize.height/2) } } override func prepareForDisplay() { super.prepareForDisplay() // 确保视图已经设置好 if markerImageView == nil { updateView() } // 更新选中状态 markerImageView?.isSelected = annotationSelected } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) marker?.isSelected = selected markerImageView?.isSelected = annotationSelected } }