iosswiftflutterflutter-dependenciescarplay

How can I get the latitude and longitude bounds of the CarPlay POI template map screen?


I am using the flutter_carplay (https://pub.dev/packages/flutter_carplay) plugin in my EV charging app. I need to display the chargers on the CarPlay screen based on the map's scrolls and movements in the CPPointOfInterestTemplate. I need to know how to get the latitude and longitude bounds of the screen when it is scrolled or the position changes.

This is my Carplay FCPPointOfInterestTemplate.swift

//
//  FCPPointOfInterestTemplate.swift
//  flutter_carplay
//
//  Created by Olaf Schneider on 15.02.22.
//

import CarPlay

@available(iOS 14.0, *)
class FCPPointOfInterestTemplate {
    private(set) var _super: CPPointOfInterestTemplate?
    private(set) var elementId: String
    private var title: String
    private var poi: [FCPPointOfInterest]

    init(obj: [String: Any]) {
        self.elementId = obj["_elementId"] as! String
        self.title = obj["title"] as! String
        self.poi = (obj["poi"] as! [[String: Any]]).map {
            FCPPointOfInterest(obj: $0)
        }
    }

    var get: CPPointOfInterestTemplate {
        var pois: [CPPointOfInterest] = []

        for p in poi {
            pois.append(p.get)
        }

        let pointOfInterestTemplate = CPPointOfInterestTemplate.init(
            title: self.title, pointsOfInterest: pois, selectedIndex: NSNotFound)
        self._super = pointOfInterestTemplate
        return pointOfInterestTemplate
    }
}

@available(iOS 14.0, *)
extension FCPPointOfInterestTemplate: FCPRootTemplate {}

I need to know how to get the latitude and longitude bounds.


Solution

  • You need to assign an object that conforms to the CPPointOfInterestTemplateDelegate protocol to the pointOfInterestDelegate property of your CPPointOfInterestTemplate.

    The delegates pointOfInterestTemplate(_:didChangeMapRegion:) method will be called when the map region changes.

    In this method you can access the region argument to obtain the MKCoordinateRegion that is currently shown on the map.

    The region has a center property that gives you the latitude and longitude of the center of the map and a span property that gives you the latitude and longitude deltas.

    Since the Earth is not a perfect sphere, the top/bottom/left/right of the map won't exactly be the center plus/minus half the deltas, but it will be close enough.

    The other method you may want to implement in your delegate is pointOfInterestTemplate(_:didSelectPointOfInterest:) so that you can respond to the user selecting one of your points of interest.

    For example:

    extension FCPPointOfInterestTemplate: CPPointOfInterestTemplateDelegate {
    
        func pointOfInterestTemplate(_ pointOfInterestTemplate: CPPointOfInterestTemplate, didChangeMapRegion region: MKCoordinateRegion) {
    
          let center = region.center
          let latitudeDelta2 = region.span.latitudeDelta/2
          let longitudeDelta2 = region.span.longitudeDelta/2
    
          let topLeft = CLLocationCoordinate2D(latitude: center.latitude-latitudeDelta2, longitude:center.longitude-longitudeDelta2)
          let bottomRight = CLLocationCoordinate2D(latitude: center.latitude+latitudeDelta2, longitude:center.longitude+longitudeDelta2)
    
          // Now do something with coordinates
    
      }
    
      func pointOfInterestTemplate(_ pointOfInterestTemplate:CPPointOfInterestTemplate, didSelectPointOfInterest pointOfInterest: CPPointOfInterest) {
     
         // Do something with selected POI
      }
    
    }
    

    And then in your init you need to set the delegate:

    var get: CPPointOfInterestTemplate {
        var pois = pois.map { $0.get }
    
        let pointOfInterestTemplate = CPPointOfInterestTemplate(
                title: self.title, pointsOfInterest: pois, selectedIndex: NSNotFound)
        self._super = pointOfInterestTemplate
        pointOfInterestTemplate.pointOfInterestDelegate = self
        return pointOfInterestTemplate
    }
    

    Unfortunately it seems that the Flutter CarPlay package doesn't keep a strong reference to the FCPPointOfInterestTemplate object, so the delegate methods don't get called.

    The package probably needs to be updated to handle these delegate methods and provide callbacks to the Flutter layer via a method channel.