Sfoglia il codice sorgente

add annotation view

Groot 6 mesi fa
parent
commit
cc9c50bfe3

+ 77 - 0
plugins/map_mapkit_ios/example/ios/.symlinks/plugins/map_mapkit_ios/ios/Classes/MapView/Model/MarkerType.swift

@@ -0,0 +1,77 @@
+//
+//  MarkerType.swift
+//  Pods
+//
+//  Created by Groot on 2025/5/14.
+//
+
+import Foundation
+
+protocol MapMarkerSupportType: Codable {
+    var rawValue: Int { get }
+
+    var supportSelected: Bool { get }
+
+    func imageName(selected: Bool) -> String
+
+    var size: CGSize { get }
+}
+
+// 创建一个全局辅助类来处理解码/编码
+struct MarkerTypeFactory {
+    static func markerType(from rawValue: Int) -> any MapMarkerSupportType {
+        // 根据应用包类型选择正确的枚举类型
+        switch currentPackage {
+        case .traceLocation1:
+            return MarkerType(rawValue: rawValue) ?? MarkerType.mine
+        // 如果有更多包类型,可以在这里添加
+        }
+    }
+}
+
+// 为MapMarkerSupportType添加Codable实现
+extension MapMarkerSupportType {
+    // 编码为整数
+    func encode(to encoder: Encoder) throws {
+        var container = encoder.singleValueContainer()
+        try container.encode(rawValue)
+    }
+}
+
+// 第一个包的标记类型
+enum MarkerType: Int, MapMarkerSupportType {
+    case mine = 1
+    case friend = 2
+    case traceStart = 3
+    case traceEndFriend = 4
+    case traceEndMine = 5
+    
+    var supportSelected: Bool {
+        return self == .mine || self == .friend
+    }
+    func imageName(selected: Bool) -> String {
+        let prefixName: String = currentPackage.rawValue
+        let imageBaseName: String = switch self {
+            case .mine:
+                "mine"
+            case .friend:
+                "friend"
+            case .traceStart:
+                "traceStart"
+            case .traceEndFriend:
+                "traceEndFriend"
+            case .traceEndMine:
+                "traceEndMine"
+        }
+        return "\(prefixName)_\(imageBaseName)" + ((selected && supportSelected) ? "_selected" : "")
+    }
+
+    var size: CGSize {
+        switch self {
+            case .traceStart:
+                return CGSize(width: 22, height: 36)
+            default:
+                return CGSize(width: 44, height: 52)
+        }
+    }
+}

+ 1 - 1
plugins/map_mapkit_ios/example/ios/Podfile.lock

@@ -15,7 +15,7 @@ EXTERNAL SOURCES:
 
 SPEC CHECKSUMS:
   Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
-  map_mapkit_ios: fe3a12a53d3f398815c9973cd82bb0a9679bde43
+  map_mapkit_ios: ca064ca09018af4608239db3d98b46cb2233e8b9
 
 PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5
 

+ 25 - 5
plugins/map_mapkit_ios/example/lib/main.dart

@@ -76,6 +76,7 @@ class _MyHomePageState extends State<MyHomePage> {
                       "latitude": Random().nextDouble() * (40.0 - 22.0) + 22.0,
                       "longitude": Random().nextDouble() * (120.0 - 110.0) + 110.0,
                       "markerName": "Marker ${Random().nextInt(1000000)}",
+                      "markerType": 1,
                       "isSelected": false
                    });    
                   }
@@ -89,12 +90,31 @@ class _MyHomePageState extends State<MyHomePage> {
                 CupertinoButton(
                   child: Text("Add Polyline"),
                   onPressed: () {
+                    final points = List<Map<String, double>>.generate(20, (_) => {
+                      "latitude": Random().nextDouble() * (40.0 - 22.0) + 22.0,
+                      "longitude": Random().nextDouble() * (120.0 - 110.0) + 110.0
+                    });
                     _methodChannel?.invokeMethod("polyline#addPolyline", {
-                      "points": List.generate(20, (_) => {
-                        "latitude": Random().nextDouble() * (40.0 - 22.0) + 22.0,
-                        "longitude": Random().nextDouble() * (120.0 - 110.0) + 110.0
-                      })
-                  });
+                      "points": points
+                    });
+                    // start marker
+                    _methodChannel?.invokeMethod("marker#updateOrAddMarkers", {
+                      "id": "marker_${Random().nextInt(1000000)}",
+                      "latitude": points[0]["latitude"],
+                      "longitude": points[0]["longitude"],
+                      "markerName": "Marker ${Random().nextInt(1000000)}",
+                      "markerType": 3,
+                      "isSelected": false
+                    });
+                    // end marker
+                    _methodChannel?.invokeMethod("marker#updateOrAddMarkers", {
+                      "id": "marker_${Random().nextInt(1000000)}",
+                      "latitude": points[points.length - 1]["latitude"],
+                      "longitude": points[points.length - 1]["longitude"],
+                      "markerName": "Marker ${Random().nextInt(1000000)}",
+                      "markerType": 4,
+                      "isSelected": false
+                    });
                   }
                 )
               ],

+ 13 - 0
plugins/map_mapkit_ios/ios/Classes/MapMapkitIosPlugin.swift

@@ -1,6 +1,19 @@
 import Flutter
 import UIKit
 
+enum MapSupportedPackage: String {
+    case traceLocation1 = "com.trace.location"
+}
+
+var currentPackage: MapSupportedPackage {
+    if let bundleId = Bundle.main.bundleIdentifier {
+        if bundleId.hasPrefix(MapSupportedPackage.traceLocation1.rawValue) {
+            return .traceLocation1
+        }
+    }
+    return .traceLocation1
+}
+
 public class MapMapkitIosPlugin: NSObject, FlutterPlugin {
     public static func register(with registrar: FlutterPluginRegistrar) {
         let factory = MapFlutterViewFactory(messenger: registrar.messenger())

+ 2 - 1
plugins/map_mapkit_ios/ios/Classes/MapView/MapFlutterView.swift

@@ -63,7 +63,8 @@ class MapFlutterView: NSObject, FlutterPlatformView {
                 mapView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
                 mapView.topAnchor.constraint(equalTo: contentView.topAnchor),
                 mapView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
-            ])
+            ]
+        )
         
         vc.didMove(toParent: topController)
     }

+ 11 - 11
plugins/map_mapkit_ios/ios/Classes/MapView/MapViewController.swift

@@ -52,12 +52,14 @@ class MapViewController: UIViewController {
         // 监听区域变化并更新地图
         viewModel.$currentRegion
             .compactMap { $0 }
+            .receive(on: DispatchQueue.main)
             .sink { [weak self] region in
                 self?.mapView.setRegion(region, animated: true)
             }
             .store(in: &cancellables)
 
         viewModel.$markers
+            .receive(on: DispatchQueue.main)
             .sink { [weak self] markers in
                 self?.mapView.removeAnnotations(self?.mapView.annotations ?? [])
                 self?.mapView.addAnnotations(markers)
@@ -65,6 +67,7 @@ class MapViewController: UIViewController {
             .store(in: &cancellables)
 
         viewModel.$polylines
+            .receive(on: DispatchQueue.main)
             .sink { [weak self] polylines in
                 self?.mapView.removeOverlays(self?.mapView.overlays ?? [])
                 self?.mapView.addOverlays(polylines.map({ $0.polyline }))
@@ -75,16 +78,14 @@ class MapViewController: UIViewController {
 
 extension MapViewController: MKMapViewDelegate {
     func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
-        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: MapAnnotationView.identifier) as? MapAnnotationView
-        
-        // 如果没有可复用的视图,创建新的自定义标注视图
-        if annotationView == nil {
-            annotationView = MapAnnotationView(annotation: annotation, reuseIdentifier: MapAnnotationView.identifier)
-        } else {
-            // 更新现有视图的标注
-            annotationView?.annotation = annotation
+        guard let marker = annotation as? ATMapMarker else {
+            return nil
         }
         
+        let annotationView = (mapView.dequeueReusableAnnotationView(withIdentifier: marker.markerType.imageName(selected: marker.isSelected)) as? MapAnnotationView) ?? MapAnnotationView(annotation: marker, reuseIdentifier: marker.markerType.imageName(selected: marker.isSelected))
+        annotationView.marker = marker
+        annotationView.prepareForDisplay()
+        annotationView.prepareForReuse()
         return annotationView
     }
 
@@ -98,9 +99,8 @@ extension MapViewController: MKMapViewDelegate {
 
     func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
         let renderer = MKPolylineRenderer(overlay: overlay)
-        renderer.lineWidth = 5
-        renderer.strokeColor = .blue
-        renderer.alpha = 1
+        renderer.lineWidth = 3
+        renderer.strokeColor = UIColor(red: 0.123, green: 0.125, blue: 0.255, alpha: 1.0)
         renderer.lineCap = .round
         renderer.lineJoin = .round
         return renderer

+ 77 - 0
plugins/map_mapkit_ios/ios/Classes/MapView/Model/MarkerType.swift

@@ -0,0 +1,77 @@
+//
+//  MarkerType.swift
+//  Pods
+//
+//  Created by Groot on 2025/5/14.
+//
+
+import Foundation
+
+protocol MapMarkerSupportType: Codable {
+    var rawValue: Int { get }
+
+    var supportSelected: Bool { get }
+
+    func imageName(selected: Bool) -> String
+
+    var size: CGSize { get }
+}
+
+// 创建一个全局辅助类来处理解码/编码
+struct MarkerTypeFactory {
+    static func markerType(from rawValue: Int) -> any MapMarkerSupportType {
+        // 根据应用包类型选择正确的枚举类型
+        switch currentPackage {
+        case .traceLocation1:
+            return MarkerType(rawValue: rawValue) ?? MarkerType.mine
+        // 如果有更多包类型,可以在这里添加
+        }
+    }
+}
+
+// 为MapMarkerSupportType添加Codable实现
+extension MapMarkerSupportType {
+    // 编码为整数
+    func encode(to encoder: Encoder) throws {
+        var container = encoder.singleValueContainer()
+        try container.encode(rawValue)
+    }
+}
+
+// 第一个包的标记类型
+enum MarkerType: Int, MapMarkerSupportType {
+    case mine = 1
+    case friend = 2
+    case traceStart = 3
+    case traceEndFriend = 4
+    case traceEndMine = 5
+    
+    var supportSelected: Bool {
+        return self == .mine || self == .friend
+    }
+    func imageName(selected: Bool) -> String {
+        let prefixName: String = currentPackage.rawValue
+        let imageBaseName: String = switch self {
+            case .mine:
+                "mine"
+            case .friend:
+                "friend"
+            case .traceStart:
+                "traceStart"
+            case .traceEndFriend:
+                "traceEndFriend"
+            case .traceEndMine:
+                "traceEndMine"
+        }
+        return "\(prefixName)_\(imageBaseName)" + ((selected && supportSelected) ? "_selected" : "")
+    }
+
+    var size: CGSize {
+        switch self {
+            case .traceStart:
+                return CGSize(width: 22, height: 36)
+            default:
+                return CGSize(width: 44, height: 52)
+        }
+    }
+}

+ 28 - 8
plugins/map_mapkit_ios/ios/Classes/MapView/Model/Models.swift

@@ -26,14 +26,6 @@ extension Encodable {
     }
 }
 
-enum ATMapMarkerType: Int, CaseIterable {
-    case mine = 1
-    case friend = 2
-    case traceStartPoint = 3
-    case traceEndFriendPoint = 4
-    case traceEndMinePoint = 5
-}
-
 struct ATMapCameraPosition: Codable {
     var latitude: CGFloat
     var longitude: CGFloat
@@ -60,6 +52,34 @@ class ATMapMarker: NSObject, Codable {
     var latitude: CGFloat
     var longitude: CGFloat
     var isSelected: Bool
+    var markerType: any MapMarkerSupportType
+
+    enum CodingKeys: String, CodingKey {
+        case id, markerName, latitude, longitude, isSelected, markerType
+    }
+
+    required init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        id = try container.decode(String.self, forKey: .id)
+        markerName = try container.decode(String.self, forKey: .markerName)
+        latitude = try container.decode(CGFloat.self, forKey: .latitude)
+        longitude = try container.decode(CGFloat.self, forKey: .longitude)
+        isSelected = try container.decode(Bool.self, forKey: .isSelected)
+        
+        // 使用工厂方法创建正确类型的markerType
+        let rawValue = try container.decode(Int.self, forKey: .markerType)
+        markerType = MarkerTypeFactory.markerType(from: rawValue)
+    }
+
+    func encode(to encoder: Encoder) throws {
+        var container = encoder.container(keyedBy: CodingKeys.self)
+        try container.encode(id, forKey: .id)
+        try container.encode(markerName, forKey: .markerName)
+        try container.encode(latitude, forKey: .latitude)
+        try container.encode(longitude, forKey: .longitude)
+        try container.encode(isSelected, forKey: .isSelected)
+        try container.encode(markerType.rawValue, forKey: .markerType)
+    }
 }
 
 extension ATMapMarker: MKAnnotation {

+ 5 - 1
plugins/map_mapkit_ios/ios/Classes/MapView/ViewModel/MapViewModel.swift

@@ -58,6 +58,11 @@ class MapViewModel: NSObject, ObservableObject, MapCapability {
             return
         }
 
+        // 检查是否已存在相同ID的标记,如果有则先移除
+        if let index = markers.firstIndex(where: { $0.id == marker.id }) {
+            markers.remove(at: index)
+        }
+        
         markers.append(marker)
         result(nil)
     }
@@ -69,7 +74,6 @@ class MapViewModel: NSObject, ObservableObject, MapCapability {
     }
 
     // 处理地图替换所有Marker
-    // Combine 执行替换前的清除操作
     func handleMapReplaceAllMarkers(args: [String: Any]?, result: @escaping FlutterResult) {
         defer {
             result(nil)

+ 33 - 19
plugins/map_mapkit_ios/ios/Classes/MapView/Views/MapAnnotationView.swift

@@ -13,17 +13,36 @@ class MapAnnotationView: MKAnnotationView {
     static let identifier: String = "MapAnnotationView"
 
     var marker: ATMapMarker?
+    
+    var annotationSelected: Bool {
+        return (marker?.isSelected ?? false) || self.isSelected
+    }
+    
     private let imageView = UIImageView()
+    private var uiImage: UIImage? {
+        // 先尝试从主资源束加载
+        let imageName = marker?.markerType.imageName(selected: annotationSelected) ?? ""
+        print(imageName)
+        if let image = UIImage(named: imageName) {
+            return image
+        }
+        
+        // 尝试从插件资源束加载
+        let bundleURL = Bundle(for: MapAnnotationView.self).url(forResource: "map_mapkit_ios_resources", withExtension: "bundle")
+        if let bundleURL = bundleURL, let resourceBundle = Bundle(url: bundleURL) {
+            return UIImage(named: imageName, in: resourceBundle, compatibleWith: nil)
+        }
+        
+        return nil
+    }
     
     override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
-        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
         self.marker = annotation as? ATMapMarker
+        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
         
-        // 确保视图可见
-        self.canShowCallout = false
-        self.isEnabled = true
+        self.isEnabled = marker?.markerType.supportSelected ?? false
         self.isHidden = false
-        
+        // 添加图片显示
         setupImageView()
     }
 
@@ -33,15 +52,15 @@ class MapAnnotationView: MKAnnotationView {
     
     private func setupImageView() {
         // 设置默认图像
-        let defaultImage = UIImage()
-        imageView.image = defaultImage
+        imageView.image = uiImage
         imageView.contentMode = .scaleAspectFit
         
         // 添加图像视图到注释视图
         addSubview(imageView)
         
         // 设置图像视图的大小和位置
-        imageView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
+        let size = marker?.markerType.size ?? .init()
+        imageView.frame = CGRect(origin: .init(), size: size)
         
         // 设置注释视图的大小
         self.frame = imageView.frame
@@ -53,22 +72,17 @@ class MapAnnotationView: MKAnnotationView {
     override func prepareForDisplay() {
         super.prepareForDisplay()
         
-        // 根据标记状态更新图像
+        // 确保图像是最新的
         updateImage()
     }
     
     private func updateImage() {
-        // 使用已有的图像资源,而不是不存在的 marker_selected 和 marker_default
-        let defaultImage = UIImage()
-        imageView.image = defaultImage
+        let oldImage = imageView.image
+        imageView.image = uiImage
         
-        // 如果被选中,可以修改图像视图的外观
-        if let isSelected = marker?.isSelected, isSelected {
-            imageView.layer.borderWidth = 2.0
-            imageView.layer.borderColor = UIColor.blue.cgColor
-            imageView.layer.cornerRadius = imageView.frame.width / 2
-        } else {
-            imageView.layer.borderWidth = 0
+        // 如果图像发生变化或为空,重新设置图像视图
+        if imageView.image == nil || oldImage != imageView.image {
+            setupImageView()
         }
     }
 

BIN
plugins/map_mapkit_ios/ios/Resources/com.trace.location_friend.png


BIN
plugins/map_mapkit_ios/ios/Resources/com.trace.location_mine.png


BIN
plugins/map_mapkit_ios/ios/Resources/com.trace.location_mine_selected.png


BIN
plugins/map_mapkit_ios/ios/Resources/com.trace.location_traceEndFriend.png


BIN
plugins/map_mapkit_ios/ios/Resources/com.trace.location_traceEndMine.png


BIN
plugins/map_mapkit_ios/ios/Resources/com.trace.location_traceStart.png


+ 3 - 0
plugins/map_mapkit_ios/ios/map_mapkit_ios.podspec

@@ -26,4 +26,7 @@ A new Flutter plugin project.
   # plugin's privacy impact, and then uncomment this line. For more information,
   # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
   # s.resource_bundles = {'map_mapkit_ios_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
+  s.resource_bundles = {
+    'map_mapkit_ios_resources' => ['Resources/**/*']
+  }
 end