
Hallo! Mein Name ist Vanya, ich schreibe eine 2GIS-Mobilanwendung für iOS. Heute wird es eine Geschichte darüber geben, wie unser Navigator in CarPlay erschien. Ich werde Ihnen sagen, wie wir mit solchen Dokumentationen und unfertigen Tools ein funktionierendes Produkt erstellt und in den AppStore gestellt haben.
Ein paar Worte zu CarPlay

Zunächst ein wenig Material, um einige Aspekte von CarPlay und die Gründe, warum wir bestimmte Entscheidungen getroffen haben, zu verstehen.
CarPlay ist kein Betriebssystem in einem anderen Betriebssystem, da so viele Artikel darüber schreiben. Wenn ungefähr, dann ist CarPlay ein Protokoll für die Arbeit mit einer externen Anzeige des Bildschirms der Headunit. Ton von Autolautsprechern; Touchscreens, Touchscreens, Unterlegscheiben und andere Eingabegeräte.
Das heißt, der gesamte ausführbare Code befindet sich direkt in der Hauptanwendung (nicht einmal in einer separaten Erweiterung!). Dies ist sehr cool: Um neue Funktionen zu erhalten, müssen Sie weder das Radio noch den Computer aktualisieren, sondern nur iOS aktualisieren.
Auf der Keynote zur WWDC 2018 hatten wir die Gelegenheit, Navigationsanwendungen für CarPlay zu erstellen, was uns sehr freute. Unmittelbar nach der Präsentation haben wir eine Anfrage für die Erlaubnis zur Entwicklung für CarPlay gesendet. In der Anfrage musste gezeigt werden, dass unsere Anwendung navigationsfähig ist.
Während wir auf eine Antwort von Apple warteten, gab es einen Vortrag, in dem unter Verwendung der Beispielanwendung CountryRoads über die Arbeit mit CarPlay.framework gesprochen wurde. In der Vorlesung wurde nicht auf die Fallstricke und Feinheiten bei der Arbeit mit CarPlay eingegangen, sondern es wurde erwähnt, dass die Anwendung nach dem Herstellen einer Verbindung zum CarPlay-Radio im Hintergrundmodus arbeitet.
Zuerst in die Räder stecken
Die Bewerbung im Hintergrund hat uns enttäuscht. Dafür gab es zwei Gründe:
- Wir arbeiten nicht im Hintergrund. Einmal diese Einschränkung aus technischen Gründen und zur Energieeinsparung verlassen.
- Unsere Karte ist in OpenGL geschrieben (ja, veraltet, ja, nicht Metal, das wissen wir alle), und OpenGL im Hintergrund funktioniert nicht. Im besten Fall erhalten Sie eine schwarze Ansicht und im schlimmsten Fall einen Absturz.
Es war immer noch möglich, die Arbeit im Hintergrund zu bewältigen, aber die Karte musste definitiv gelöst werden. Dann kam die Idee, es durch das Standard-MKMapView zu schaffen. Bis Sie angefangen haben, Steine auf uns zu werfen, um Standard-Apple-Karten zu verwenden, erkläre ich: Wir wollten MKMapView verwenden, aber keine Apple-Karten.
Tatsache ist, dass MKMapView Kacheln von Drittanbietern laden kann. Fliesen sind spezielle rechteckige Behälter für Texturen. Wir haben uns gerade als Servochka herausgestellt, der weiß, wie man Fliesen gibt. Es gibt Implementierungscode auf GitHub.
Apple antwortet
Wir erhielten eine Antwort von Apple, in der wir neben der Erlaubnis zur Entwicklung auch die Dokumentation "für die Elite", den Code der CountryRoads-Beispielanwendung (die in der WWDC-Vorlesung gezeigt wurde) und vor allem den privaten Fähigkeitsschlüssel com.apple.developer.carplay-maps
. Dieser Schlüssel wird in die Berechtigungsdatei mit dem Wert YES geschrieben, damit das System versteht, dass Sie Ereignisse aus CarPlay verarbeiten können, wenn Ihre Anwendung gestartet wird.
Ohne auf den Sprint mit den ausgewählten Geschichten für die Entwicklung zu warten, stieg ich auf, um Xcode Beta herunterzuladen. Der erste Versuch, 2GIS zu sammeln, war ein Fehlschlag. Das CoutryRoads-Beispielanwendungsprojekt konnte jedoch für den Simulator zusammengestellt werden.
Vor jedem Öffnen des CarPlay-Simulatorfensters musste letzteres über ein solches Fenster angepasst werden:

Dazu mussten Sie eine Zeile in das Terminal defaults write com.apple.iphonesimulator CarPlayExtraOptions -bool YES
Aus irgendeinem Grund funktionierte dies nicht - ich musste es auf fast dem kleinsten Simulator mit einer Auflösung von 800 × 480 Punkten und einer × 2-Skala ausführen. Im Moment funktioniert diese Einstellung und hilft sehr.
Nachdem ich mein Beispielprojekt erstellt und mit Dokumentation ausgestattet hatte, begann ich zu verstehen, was geschah.
Als erstes wurde mir klar: Navigationsanwendungen für CarPlay bestehen aus Basisansicht und Vorlagenebenen.

Basisansicht ist Ihre Karte. Auf dieser Ebene sollte sich nur eine Karte befinden, keine anderen Ansichten und Steuerelemente.
Vorlagen sind fast nicht anpassbare obligatorische UI-Elemente zum Anzeigen von Routen, Manövern, Listen aller Art usw.
Beta-Entwicklung
Fahren wir mit dem Schreiben von Code fort. Als erstes müssen Sie einige erforderliche CPApplicationDelegate-Methoden in die ApplicationDelegate-Datei implementieren.
func application( _ application: UIApplication, didConnectCarInterfaceController controller: CPInterfaceController, to window: CPWindow ) {} func application( _ application: UIApplication, didDisconnectCarInterfaceController controller: CPInterfaceController, from window: CPWindow ) {}
Schauen wir uns die Signatur an:
Mit UIApplication ist alles klar.
CPWindow ist der Nachfolger von UIWindow, einem Fenster für die externe Anzeige der Headunit des Radios.
CPInterfaceController - so etwas wie ein Analogon von UINavigationController, nur von CarPlay.framework.
Nun fahren wir direkt mit der Implementierung der Methode fort.
func application( _ application: UIApplication, didConnectCarInterfaceController controller: CPInterfaceController, to window: CPWindow ) { let carMapViewController = CarMapViewController( interfaceController: controller ) let navigationController = UINavigationController( rootViewController: carMapViewController ) window.rootViewController = navigationController }
In didConnect müssen Sie Code schreiben, der dem in didFinishLaunching verwendeten ähnlich ist. CarMapViewController ist eine Basisansicht (der Controller ist tatsächlich, aber in Ordnung) gemäß der Dokumentation.
Hier ist das Bild, das ich endlich bekommen habe:

Irgendwann zu dieser Zeit wurde mir klar, dass das neue Xcode-Neubausystem standardmäßig aktiviert ist und 2GIS dies höchstwahrscheinlich nicht tun wird.
Ich habe Xcode geöffnet, ein älteres (oder eher stabiles, nennen wir einen Spaten einen Spaten) Build-System installiert und meine Theorie wurde bestätigt: 2GIS wurde zusammengestellt.
Nachdem ich den gleichen Funktionsschlüssel festgelegt hatte, startete ich 2GIS unter CarPlay und sah keine Protokolle über die Umschaltung der Anwendung in den Hintergrundmodus. Es wurde noch unverständlicher, weil Apple-Ingenieure aus der Szene über den Hintergrundmodus sprachen, uns aber andererseits eine ContentView von UIAlertView versprachen und UIAlertView infolgedessen veraltet war.
Nachdem ich beschlossen hatte, dass es so sein sollte, habe ich mich nicht mit MKMapView beschäftigt. Es würde uns offline berauben und uns dazu bringen, das Rendern der Routen neu zu schreiben.
Einzelkartenproblem
Ich hatte keine Zeit, mich über die Nachricht zu freuen, dass CarPlay unsere Karte haben wird, da mich das folgende Problem stellte: Aufgrund der technischen Merkmale kann es nur eine Karte geben.
Eine schnelle Lösung für dieses Problem war, wenn auch nicht sehr elegant.
Wenn Sie 2GIS auf CarPlay verwenden, ist das Telefon normalerweise gesperrt und liegt irgendwo im Regal. Daher wird die Karte in diesem Moment am Telefon nicht wirklich benötigt (die Suche wird natürlich nicht schaden). Als wir das Telefon mit CarPlay verbunden haben, haben wir uns daher entschlossen, die Karte aus der Hauptanwendung zu nehmen und auf dem CarPlay-Bildschirm des Radios anzuzeigen. Wenn die Verbindung getrennt ist, kehren Sie auf dem Telefon zur Anwendung zurück.
Ja, es ist eine Lösung für sich selbst, aber es ist schnell, es funktioniert immer noch und es mussten nicht ein paar andere Befehle ausgeführt werden, um MVP zu nieten.
Steuerelemente auf der Karte
Also haben wir unsere Karte auf dem Radiobildschirm. Jetzt war es notwendig, die ersten und offensichtlichen Dinge für jede Karte zu tun: Steuerelemente für Zoom, aktuellen Standort und Kartenbewegung.

Beginnen wir mit dem Zoom und der aktuellen Position, da sich diese Steuerelemente auf der Karte selbst befinden und keine gewöhnliche UIControl sind. Wie ich oben geschrieben habe, befindet sich nur die Karte in der Basisansicht.
Um diese Steuerelemente auf der Karte zu platzieren, musste ich erneut in die Dokumentation und die Beispielanwendung gehen. Dort habe ich über die erste Vorlage gelesen - CPMapTemplate.

CPMapTemplate - eine transparente Vorlage zum Anzeigen einiger Steuerelemente auf der Karte und analog zur Navigationsleiste. Es wird wie folgt erstellt und eingerichtet:
let mapTemplate = CPMapTemplate() self.interfaceController.setRootTemplate(mapTemplate, animated: false)
Als nächstes müssen Sie diese Steuerelemente erstellen und auf die Karte legen.
let zoomInButton = CPMapButton(…) let zoomOutButton = CPMapButton(…) let myLocationButton = CPMapButton(…) self.mapTemplate.mapButtons = [ zoomInButton, zoomOutButton, myLocationButton ]
Das mapButtons-Array erwies sich jedoch als witzig, da unabhängig von der Anzahl der Elemente nur die ersten drei Elemente auf dem Bildschirm angezeigt werden. Sie erhalten keine Fehler im Protokoll oder in den Zusicherungen.
Dann habe ich gesehen, wie ich die Karte bewegen kann, und ich habe dies in der Dokumentation gefunden:
Navigation apps are designed to work with a variety of car input devices, and CarPlay does not support direct user interaction in the base view (apps do not directly receive tap or drag events).
Seltsam, dachte ich und musste sehen, wie das in der CountryRoads-Beispielanwendung gemacht wird. Die Antwort ist über diese Schnittstelle:

Nicht sehr praktisch, aber auf andere Weise wird die Dokumentation nicht lügen, oder?
Da der Platz für Steuerelemente auf der Karte abgelaufen war, musste in diesem Analogon der Navigationsleiste eine Schaltfläche erstellt werden, um die Karte in den "Drag" -Modus zu versetzen.
let panButton = CPBarButton(…) self.mapTemplate.leadingNavigationBarButtons = [panButton] self.mapTemplate.trailingNavigationBarButtons = []
Aber auch die Arrays der führenden NavigationBarButtons und der nachfolgendenNavigationBarButtons waren nicht ohne Witz: Wie viele Elemente in ihnen schieben, nehmen sie nur die ersten beiden. Auch ohne Fehler im Protokoll und Zusicherungen.
Um den Drag & Drop-Modus für Karten zu aktivieren und zu deaktivieren, müssen Sie Folgendes schreiben:
self.mapTemplate.showPanningInterface(animated: true) self.mapTemplate.dismissPanningInterface(animated: true)
Erstellen und Anzeigen von Routen auf einer Karte
Als Nächstes begann ich, unsere vorhandene API zum Erstellen von Routen wiederzuverwenden.
Nur für eine Demo und um zu verstehen, was und wie zu tun ist, habe ich beschlossen, zwei Punkte zu nehmen und eine Route zwischen ihnen zu erstellen. Punkt A war der Standort des Benutzers und Punkt B war unser Hauptbüro in Nowosibirsk.
Code let choice0 = CPRouteChoice( summaryVariants: ["46 "], additionalInformationVariants: [" "], selectionSummaryVariants: ["1 7 "] ) let choice1 = CPRouteChoice( summaryVariants: ["46 "], additionalInformationVariants: [" "], selectionSummaryVariants: [“1 11 "] ) let startItem = MKMapItem(…) let endItem = MKMapItem(…) endItem.name = ", ” let trip = CPTrip( origin: startItem, destination: endItem, routeChoices: [choice0, choice1] ) let tripPreviewTextConfiguration = CPTripPreviewTextConfiguration( startButtonTitle: " ”, additionalRoutesButtonTitle: “”, overviewButtonTitle: "" ) self.mapTemplate.showTripPreviews( [trip], textConfiguration: tripPreviewTextConfiguration )
Auf dem Bildschirm haben wir ein Steuerelement mit einer Beschreibung der Route:

Navigationsmodus
Routen sind gut, aber das Hauptmerkmal des Navigators ist die Navigation. Damit es angezeigt wird, müssen Sie Folgendes schreiben:
func mapTemplate( _ mapTemplate: CPMapTemplate, startedTrip trip: CPTrip, using routeChoice: CPRouteChoice ) { self.navigationSession = self.mapTemplate.startNavigationSession(for: trip) }
CPNavigationSession - eine Klasse, mit der Sie einige UI-Elemente anzeigen können, die nur im Navigationsmodus erforderlich sind.
Um das Manöver anzuzeigen, müssen Sie:
let maneuver = CPManeuver() maneuver.symbolSet = CPImageSet( lightContentImage: icon, darkContentImage: darkIcon ) maneuver.instructionVariants = [". "] maneuver.initialTravelEstimates = CPTravelEstimates(…) self.navigationSession?.upcomingManeuvers = [maneuver]
Dann erhalten wir auf dem Bildschirm des Radios Folgendes:

Um das Filmmaterial zum Manövrieren zu aktualisieren, müssen Sie:
let estimates = CPTravelEstimates(…) self.navigationSession?.updateEstimates(estimates, for: maneuver)
Es funktioniert einfach!
Als die Grundfunktionalität für den Navigator fertig war, beschloss ich, dieses Handwerk in einer internen Präsentation zu zeigen. Die Präsentation war ein Erfolg: Jeder hatte die Idee, den Navigator so schnell wie möglich fertigzustellen, zu testen und zu starten.
Zunächst bestellten wir eine echte Headunit mit CarPlay-Unterstützung. Und dann, wie sie sagen, begann die Hitze.

Bereitstellungsprofile
Aufgrund des Hinzufügens eines neuen Funktionsschlüssels müssen Profile neu generiert werden. In der normalen Entwicklung denken wir nicht darüber nach, da Xcode alles selbst erledigt. Aber nicht bei einem privaten Schlüssel.
Code Signing Error: Automatic signing is unable to resolve an issue with the "v4ios" target's entitlements. Automatic signing can't add the com.apple.developer.carplay-maps entitlement to your provisioning profile. Switch to manual signing and resolve the issue by downloading a matching provisioning profile from the developer website.
Außerdem wurde unser CI beschädigt, da wir für die lokale Verteilung von Anwendungsversionen ein Unternehmenskonto verwenden, in dem wir keine Erlaubnis zur Entwicklung der Anwendung für CarPlay angefordert haben. Aber das ist eine ganz andere Geschichte.
Debuggen
Sie können über Bluetooth oder Lightning eine Verbindung zu CarPlay herstellen. Die Praxis zeigt, dass die zweite Methode viel beliebter ist. Unser Bluetooth-Radio wusste nicht wie, daher musste ich während der Entwicklung das Wi-Fi-Debug verwenden. Wenn Sie es bei Projekten versucht haben, die schwieriger sind als Hallo Welt, dann wissen Sie, was zum Teufel es ist.
Und für diejenigen, die es nicht versucht haben, sage ich:Ich habe die Anwendung per Kabel zum Telefon gesammelt und erst dann, als ich das Telefon über WLAN mit CarPlay verband, auf das Telefon hochgeladen und einige Minuten lang ausgeführt.
Das Kopieren der Anwendung auf das Telefon dauerte ungefähr 3 Minuten, das Starten der Anwendung für ungefähr eine Minute und erst dann, nachdem der Stopp an den Haltepunkten gestartet wurde, nur 15 Sekunden später.
Und dann wurde es für mich sehr interessant, warum Apple kein DevKit hergestellt hat (so dass es nach Apple-Art einfach funktioniert und das ist alles). Es war nicht sehr bequem, einen Prüfstand ohne ihn zu montieren. Bis jetzt fällt alle paar Wochen etwas ab - man muss sich auf den Bildern merken, worauf man sich einlassen soll. Es ist gut, dass der Administrator bei der Montage dieses Standes gesagt hat, was und warum.
Der beste Rahmen, den wir je gemacht haben
Am Ende, als alles auf einem realen Gerät zusammengebaut wurde, wurde klar, dass das Feature „2GIS for CarPlay“ definitiv sein würde. Es ist Zeit, Schönheit zu tun.
Viewport-Probleme
Das Kartenansichtsfenster musste so konfiguriert werden, dass Routen in dem Gebiet ohne unnötige Steuerelemente und nicht nur in der Mitte gezeichnet werden. Kurz gesagt, damit es anders aussieht:

Und so:

Ich hatte gehofft, eine Art Layout-Leitfaden mit dem aktuell sichtbaren Bereich zu erhalten. Damit berücksichtigt er die Navigationsleiste, die Ansicht mit der Route und die Steuerelemente auf der Karte. Tatsächlich habe ich nichts bekommen. Es ist immer noch unklar, wie das Ansichtsfenster konfiguriert werden soll, daher haben wir einen Hardcode wie:
let routeControlsWidth = self.view.frame.width * 0.48 let zoomControlWidth = self.view.frame.width * 0.15
Konstruktion des Durchgangs nicht nur zwischen zwei Punkten
In der ersten Version haben wir beschlossen, unseren über CPGridTemplate erstellten Rubrikator zu verwenden:

Favoriten und Home / Work über CPListTemplate.

Und Tastatursuche über CPSearchTemplate:

Ich werde den Code über Vorlagen nicht anzeigen, da er einfach ist und die Dokumentation dazu gut geschrieben ist (zumindest über etwas).
Es ist jedoch erwähnenswert, welche Probleme bei der Arbeit mit ihnen entdeckt wurden.CPInterfaceController kann in der Navigation ähnlich wie UIKit. d.h.
self.interfaceController.pushTemplate(listTemplate, animated: true) self.interfaceController.presentTemplate(alertTemplate, animated: true)
Wenn Sie jedoch versuchen, beispielsweise CPAlertTemplate auszuführen, erhalten Sie in den Protokollen die Bestätigung, dass CPAlertTemplate nur modal dargestellt werden kann.
Es ist nicht klar, warum Apple die Logik der Tranchen nicht unter der Haube versteckt hat, ohne eine Schnittstelle wie die folgende erstellt zu haben:
self.interfaceController.showTemplate(listTemplate, animated: true)
Es brach auch die Fähigkeit, die Erben von CPTemplate wie Controller in UIKit zu verwenden.
Wenn Sie beispielsweise versuchen, Ihren Erben auf den Vorlagenstapel zu legen, erhalten Sie Folgendes:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported object <YourAwesomeGridTemplate: 0x60000060dce0> <identifier: 6CAC7E3B-FE70-43FC-A8B1-8FC39334A61D, userInfo: (null)> passed to pushTemplate:animated:. Allowed classes: {( CPListTemplate, CPGridTemplate, CPSearchTemplate, CPMapTemplate )}'
Tests und Fehler
Getestet von artemenko-aa . Einer der ersten Fehler, den er gefunden hat, können wir immer noch nicht beheben.
Tatsache ist, dass Watchdog uns sporadisch festnagelt, wenn Sie das Telefon vom CarPlay-Radio trennen - ohne den Grund zu erklären. Selbst wenn Syslogs geöffnet wurden, ist nichts klar. Wenn Sie also eine Idee haben, wie Sie den Grund beheben oder verstehen können, können Sie dies gerne kommentieren.
Der nächste Fehler war am selben Ort, aber mit einem besonderen Verhalten. Ich habe oben geschrieben, dass die didDisconnect-Methode von CPApplicationDelegate aufgerufen wird, wenn das Telefon von CarPlay getrennt wird. Bei dieser Methode kehren wir die Karte vom Radiobildschirm zurück zur Hauptanwendung zurück. Stellen Sie sich vor, wie viele Probleme wir bekommen würden, wenn diese Methode nicht mindestens einmal von fünf aufgerufen würde.
Es wurde deutlich, dass dies ein Problem von iOS und nicht speziell unserer Anwendung ist, da das gesamte System glaubte, dass es mit CarPlay verbunden war.

Ich habe es sogar als Radar gemeldet (wie alle anderen Bugs). Ich wurde gebeten, Protokolle mit einem solchen Profil zu löschen, aber ich konnte einige Zeit nicht auf die Unterstützung antworten, sodass das Radar geschlossen wurde.
Da Apple nichts vorhatte, musste das Problem selbst umgangen werden, da es ziemlich oft reproduziert wurde.
Und dann erinnerte ich mich, dass der Löwenanteil der Verbindungen zu CarPlay über Lightning erfolgt. Dies bedeutet, dass das Telefon zum Zeitpunkt der Verbindung aufgeladen wird und zum Zeitpunkt der Trennung der Ladevorgang beendet wird. In diesem Fall können Sie den Akkustatus abonnieren und genau herausfinden, wann das Telefon nicht mehr aufgeladen und von CarPlay getrennt wurde.
Das Schema ist gebrechlich, aber wir hatten keine Wahl. Wir sind diesen Weg gegangen und es hat funktioniert!

Glücklicherweise wurde diese Krücke längst aus dem Code entfernt: Apple-Entwickler haben alles in einer der iOS-Versionen behoben.
Die Geschichte zweier Redakteure
Die erste Weiterleitung bezog sich auf Metadaten. Der Text des Editorials besagt, dass unsere Beschreibung (keine Versionshinweise) nicht besagt, dass wir CarPlay unterstützen. Wie Sie sich vorstellen können, hatten dies weder die Überprüfungsrichtlinie noch Google Maps. Wir haben uns nicht gestritten (weil es normalerweise länger ist als das Bearbeiten der Metadaten), wir haben die Zeile von den Versionshinweisen in die Beschreibung kopiert und auf eine neue Überprüfung gewartet.
Das zweite Redject geschah aufgrund der Liste der Städte. 2GIS hat eine sehr coole Funktion - den vollständigen Offline-Betriebsmodus. Diese Funktion hat uns ins Bein geschossen.
Wenn Sie eine Anwendung ohne etablierte Stadt mit CarPlay verbinden, wird die Karte nicht angezeigt, da nichts angezeigt werden kann. Und dafür waren wir geplant. Die Lösung war einfach: eine Warnung ohne Schaltflächen, die besagt, dass Sie die Stadt herunterladen müssen.

Worüber Sie nicht sprechen können
Bewegung der Gestenkarte
Etwa zur gleichen Zeit kam der Navigator unter CarPlay von Google Maps heraus - und dort konnten Sie die Karte mit Gesten über den Bildschirm bewegen. Private APIs, dachte ich, das ist offensichtlich! Die Jungs von Google kamen gerade aus einem nahe gelegenen Gebäude und sagten, was sie brauchten. Immerhin heißt es in der Dokumentation:
Navigation apps are designed to work with a variety of car input devices, and CarPlay does not support direct user interaction in the base view (apps do not directly receive tap or drag events).
Trotzdem habe ich mich entschlossen, mich zu vergewissern und wurde gegoogelt, obwohl es fast sinnlos war, da es keine technischen Artikel über CarPlay Navigation Apps gab. Es gelang mir jedoch, etwas Nützliches und PLÖTZLICH auf der Apple-Website zu finden .
In den Richtlinien habe ich ein Video gefunden, das besagt, dass die Dokumentation unverschämt lügt. Das Video zeigt, wie Sie die Karte immer noch mit Gesten ziehen können. Mir wurde klar, dass ich nichts verstand, und ich musste nur noch CarPlay.framework öffnen und alle .h-Dateien überprüfen.
Und siehe da! Ich finde in CPMapTemplate seinen Delegaten CPMapTemplateDelegate, in dem es drei Methoden gibt, die zu schreien scheinen, dass Sie, wenn Sie sie implementieren, die Kontrolle über die Gesten der Karte erhalten können.
3 Methoden/ * Wird aufgerufen, wenn eine Schwenkgeste beginnt. Wird möglicherweise nicht aufgerufen, wenn eine Verbindung zu einigen CarPlay-Systemen besteht.
/.
optionale öffentliche Funktion mapTemplateDidBeginPanGesture (_ mapTemplate: CPMapTemplate)
/ * Wird aufgerufen, wenn sich eine Schwenkgeste ändert. Wird möglicherweise nicht aufgerufen, wenn eine Verbindung zu einigen CarPlay-Systemen besteht.
/.
optionale öffentliche Funktion mapTemplate (_ mapTemplate: CPMapTemplate, didUpdatePanGestureWith Übersetzung Übersetzung: CGPoint, Geschwindigkeit: CGPoint)
/ * Wird aufgerufen, wenn eine Schwenkgeste endet. Wird möglicherweise nicht aufgerufen, wenn eine Verbindung zu einigen CarPlay-Systemen besteht.
/.
optional public func mapTemplate (_ mapTemplate: CPMapTemplate, didEndPanGestureWithVelocity Geschwindigkeit: CGPoint
)
Ich habe sie implementiert und die Anwendung auf einem Simulator ausgeführt - nichts hat funktioniert. Da ich keine Zeit hatte, mich aufzuregen, erkannte ich, dass der Simulator die gleiche Qualität wie die Dokumentation haben kann, und legte ihn auf das Gerät. Alles begann, das Glück kannte keine Grenzen!
Unterhaltsame Tatsache: Ein CarPlay-Radio benötigt ein Viertel des Bildschirms, um zu verstehen, dass eine Schwenkgeste begonnen hat. Ich möchte darauf hinweisen, dass UIPanGestureRecognizer nur 10 Punkte benötigt.
Einheitlichkeit der Benutzeroberfläche auf verschiedenen Radiorecordern
Wir haben einen Aufruf zur Unterstützung erhalten: Der Benutzer hat nur einen Sajest, der in der Suche herauskriecht, obwohl es mehr hätte geben können. Es ist seltsam, dachte ich, denn auf allen Bildschirmen passt nur eine Zeile. Habe einen Screenshot angefordert:

Und das ist völlig anders als die CPSearchTemplate-Benutzeroberfläche, die ich oben gezeigt habe. Und dies muss bei der Entwicklung berücksichtigt werden, obwohl es immer noch unmöglich ist zu verstehen, wie viele Zellen in der Platte unten auf den Bildschirm passen können.
Geschwindigkeitsbegrenzungssteuerung
Wir haben uns die Statistiken angesehen und festgestellt, dass sie den Navigator für CarPlay verwenden, und wir müssen ihn mindestens auf die Ebene des Navigators in der Hauptanwendung bringen. Zunächst haben wir uns für eine Geschwindigkeitsbegrenzung entschieden. Natürlich gab es einige Probleme.
Frage Nummer eins: Wo platzieren?
Beim erneuten Stöbern in den .h-Dateien in CPWindow fand ich einen merkwürdigen layoutGuide:
var mapButtonSafeAreaLayoutGuide: UILayoutGuide
Und das stellte sich als das heraus, was wir brauchten. Unsere Steuerung passt perfekt dazu:


Frage Nummer zwei: Ist das generell legal?
Tatsache ist, dass sich die Steuerung technisch in der Basisansicht befindet. Und die Basisansicht gemäß der Dokumentation darf nur eine Karte enthalten:
The base view is where the map is drawn. The base view must be used exclusively to draw a map, and may not be used to display other UI elements. Instead, navigation apps overlay UI elements such as the navigation bar and map buttons using the provided templates.
Die Rezensenten haben uns jedoch im AppStore vermisst, sodass Steuerelemente, die sich auf die Navigation beziehen, weiterhin integriert werden können.
Sprachsuche


In guter Weise musste diese Funktion zuerst ausgeführt werden, aber wir haben mehrere Aufgaben aus der technischen Verschuldung angesammelt, die die Implementierung der Sprachsuche für CarPlay verhinderte. Und diese Aufgabe war nicht so einfach, wie es schien.
Das erste Problem: Animationen. Tatsache ist, dass es in CPVoiceControlTemplate keine Möglichkeit gibt, Standardanimationen zu erstellen. Animationen für die Spracherkennung und -suche mussten Frame für Frame aus Bildern gesammelt werden und angeben, wie viel Zeit sie vergangen sind.
for i in 1...12 { if let image = UIImage(named: "carplay_searching_\(i)") { images.append(image) } } let image = UIImage.animatedImage(with: images, duration: 0.96)
Es sieht, wie Sie vielleicht vermuten, nicht wirklich aus, aber ich möchte die Größe der Anwendung nicht erhöhen.
Das zweite Problem: Zugriffe. Auf dem Display des Telefons werden Warnungen für den Mikrofonzugriff und die Spracherkennung angezeigt. Ich musste auf das Radio-Display schreiben, dass der Benutzer das Telefon abheben, die Erlaubnis geben und erst dann den Navigator im Radio verwenden muss. Sehr bequem!
Autos mit Rechtslenkung.
Wir erhielten einen Screenshot, in dem die Benutzeroberfläche der gesamten Anwendung auf den Kopf gestellt wurde!

Und natürlich blieb das Karten-Ansichtsfenster so, wie wir es fest codiert hatten, da niemand damit gerechnet hatte, dass es eine separate Einstellung für Autos mit Rechtslenkung gab. Ich habe nicht herausgefunden, wie ich das richtig umgehen kann, aber ich habe festgestellt, dass unsere Geschwindigkeitsbegrenzungssteuerung, da sie im layoutGuide für Kartensteuerungen liegt, nach links verschoben wurde.
Ultrafix ließ nicht lange auf sich warten. Sie haben es grob gemacht, aber es funktioniert.
let isLeftWheelCar = self.speedControlViewController.view.frame.origin.x > self.view.frame.size.width / 2.0
Ich hoffe wirklich, dass es eine richtige Lösung gibt, und ich habe sie einfach nicht gelesen.
Das ist alles für mich. Wenn Sie plötzlich vorhaben, unter CarPlay einen eigenen Navigator zu erstellen, beachten Sie, dass die Dokumentation und das Framework nicht perfekt sind. Die Plattform ist völlig neu, niemand weiß etwas und Apple hat es nicht eilig, Wissen zu teilen.