How to style Google Maps in SwiftUI with JSON - ios

I am new to XCode 11 and SwiftUI and have been following a tutorial (famous last words): https://developers.google.com/maps/documentation/ios-sdk/styling, trying to create a custom Google Maps interface using the Google Maps SDK. I have a basic map view in my app already, but I am trying to use a JSON file to style the map (specifically, I am trying to highlight local roads). My problem is that the solution provided by the Google documentation is not for SwiftUI, as it uses a ViewController. How should I use a JSON file to style the maps in SwiftUI? As I said I am a total newbie to Swift, so I would greatly appreciate answers on a beginner level, if possible. My code for the Map View called in ContentView.swift looks like this (not sure if it's relevant):
import SwiftUI
import GoogleMaps
struct GoogleMapsView: UIViewRepresentable {
private let zoom: Float = 15.0
func makeUIView(context: Self.Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: 33.3969, longitude: -84.5963, zoom: 13.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Context) {
}
}
struct GoogleMapsView_Previews: PreviewProvider {
static var previews: some View {
GoogleMapsView()
}
}

I think it doesn't matter if you're using SwiftUI or not.
The important part is:
if let styleURL = Bundle.main.url(forResource: "style", withExtension: "json") {
mapView.mapStyle = try GMSMapStyle(contentsOfFileURL: styleURL)
just add it between this:
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
->
return mapView

Related

3D Pucker as user's location mapbox ios

I am using Mapbox Maps iOS to create a custom 3d pucker and use that to display the user's location.
Everything seems to be working, the camera redirects to the user's location, the pucker successfully loads. However, I am having trouble setting the pucker's location to the user's location.
You can see that I am required to set the location of the pucker in the Model instance in the loadCharacter() function. But I am unsure of how to link that to the user's current location.
Please help!!!
// ViewController.swift
import UIKit
import MapboxMaps
public class ViewController: UIViewController {
internal var mapView: MapView!
internal var cameraLocationConsumer: CameraLocationConsumer!
override public func viewDidLoad() {
super.viewDidLoad()
let options = MapInitOptions(cameraOptions: CameraOptions(zoom: 16))
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.mapboxMap.onNext(.styleLoaded) { _ in
self.loadCharacter()
}
cameraLocationConsumer = CameraLocationConsumer(mapView: mapView)
mapView.mapboxMap.onNext(.mapLoaded) { _ in
self.mapView.location.addLocationConsumer(newConsumer: self.cameraLocationConsumer)
}
}
internal func loadCharacter() {
let uri = Bundle.main.url(forResource: "race_car_model",
withExtension: "gltf")
// Instantiate the model
let myModel = Model(uri: uri,
position: [-77.150925, 39.085006],
orientation: [0, 0, 180])
// Setting an expression to scale the model based on camera zoom
let scalingExpression = Exp(.interpolate) {
Exp(.linear)
Exp(.zoom)
0
Exp(.literal) {
[256000.0, 256000.0, 256000.0]
}
4
Exp(.literal) {
[40000.0, 40000.0, 40000.0]
}
8
Exp(.literal) {
[2000.0, 2000.0, 2000.0]
}
12
Exp(.literal) {
[100.0, 100.0, 100.0]
}
16
Exp(.literal) {
[7.0, 7.0, 7.0]
}
20
Exp(.literal) {
[1.0, 1.0, 1.0]
}
}
let configuration = Puck3DConfiguration(model: myModel, modelScale: .expression(scalingExpression))
mapView.location.options.puckType = .puck3D(configuration)
}
}
// Create class which conforms to LocationConsumer, update the camera's centerCoordinate when a locationUpdate is received
public class CameraLocationConsumer: LocationConsumer {
weak var mapView: MapView?
init(mapView: MapView) {
self.mapView = mapView
}
public func locationUpdate(newLocation: Location) {
mapView?.camera.ease(
to: CameraOptions(center: newLocation.coordinate, zoom: 16, pitch: 50.0),
duration: 1.3)
}
}
Mapbox has a repo with examples of navigation use cases that you can try, including 3D puck sample.
Note that this sample uses API from Navigation SDK, which in turn uses Maps SDK API internally. Fortunately, Navigation SDK is open-sourced, you can always look at how it draws current user location here.

Use of unresolved identifier 'Map' SwiftUI

I am following the official tutorial for SwiftUI and have run into the error message 'Use of unresolved identifier 'Map'. Even when I copy and and paste the code from the tutorial, it is still giving me the error. I have looked at a few solutions for similar issues and can't seem to find anything that will work. Code below.
import SwiftUI
import MapKit
struct MapView: View {
#State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
var body: some View {
Map(coordinateRegion: $region)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}
I apologise if this is really obvious - but I'm new to Swift/SwiftUI, and can't see where the issue is coming from. Thanks in advance!
I also got into trouble in this case.
So, I make View method using UIViewRepresentable with 2 methods: 'makeUIView' and 'updateUIView'.
MapView
import SwiftUI
import MapKit
struct MapViewUI: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(
latitude: 2.6540427, longitude: 98.8932576)
let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
}
}
And, You can call your View looks like this
struct MapPageView: View {
var body: some View {
VStack {
MapViewUI()
.frame(height: 300)
}
}
}
Problem is related to import MapKit. Because Map is defined in MapKit. Please verify if you are able to import MapKit properly

Google Maps GMSPanoramaView api blocked

I'm getting this error:
This application has been blocked by the Google Maps API. This might be because of an incorrectly registered key.
When I try to set google maps panorama street view. If I display a regular map view, it works so seems like the key is registered properly.
I followed the directions for a panorama view exactly as per google docs:
import UIKit
import GoogleMaps
class ViewController: UIViewController, GMSMapViewDelegate {
override func loadView() {
let panoView = GMSPanoramaView(frame: .zero)
self.view = panoView
panoView.moveNearCoordinate(CLLocationCoordinate2D(latitude: -33.732, longitude: 150.312))
}
}
Any ideas?
For me, the solution was that I needed to link my project in Google Cloud Platform to a billing account.
Once I did that the street view started working correctly.
The standard Google map worked fine without a billing account, which confused me initially.
You need to configure the delegate for this object. Something like:
import UIKit
import GoogleMaps
class ViewController: UIViewController, GMSMapViewDelegate {
override func loadView() {
let panoView = GMSPanoramaView(frame: .zero)
panoView.delegate = self
self.view = panoView
panoView.moveNearCoordinate(CLLocationCoordinate2D(latitude: -33.732, longitude: 150.312))
}
}
extension ViewController: GMSPanoramaViewDelegate {
func panoramaView(_ view: GMSPanoramaView, error: Error, onMoveNearCoordinate coordinate: CLLocationCoordinate2D) {
print("error: \(error.localizedDescription)")
}
}

ArcGIS iOS Map does not pan or zoom

I am walking through the basic iOS guide to add a map to my App here: https://developers.arcgis.com/ios/10-2/swift/guide/develop-your-first-map-app.htm
When running the app, it displays the basemap I have added but it does not seem to respond to any actions so I can not pane/zoom.
Here is my exact Swift controller code:
import UIKit
import ArcGIS
class ViewController: UIViewController, AGSMapViewLayerDelegate {
#IBOutlet var mapView: AGSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let tiledLayer = AGSLocalTiledLayer(name: "Norfolk")
self.mapView.addMapLayer(tiledLayer, withName: "Norfolk")
mapView.locationDisplay.startDataSource()
}
}
I have tested on both an iPad and iPhone and the behavior is the same.
I am using ArcGIS 10.2.5
Where is your map url?
let url = NSURL(string: "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer")
let tiledLayer = AGSTiledMapServiceLayer(URL: url)
self.mapView.addMapLayer(tiledLayer, withName: "Basemap Tiled Layer")
And have you added Privacy - Location When In Use Usage Description into info.plist of your app? You should do that if you call
mapView.locationDisplay.startDataSource()
I am not sure what I did incorrectly the first time, but I removed the view and re added it and it worked the 2nd time.

Google Map flickering in iOS

I am using Google Maps SDK for iOS version: 1.10.17867.0 in my app via pod. But when I initialise the map at a particular position, all the titles and map starts flickering.
Example Code (swift):
import UIKit
import GoogleMaps
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor();
var camera = GMSCameraPosition.cameraWithLatitude(19.0176147, longitude: 72.8561644, zoom:18)
// even try this: 28.6469655, longitude: 77.0932634, zoom:10
var mapView = GMSMapView.mapWithFrame(CGRectZero, camera:camera)
var marker = GMSMarker()
marker.position = camera.target
marker.snippet = "Hello World"
marker.appearAnimation = kGMSMarkerAnimationPop
marker.map = mapView
self.view = mapView
}
}
I have figured out the reason.
If you are using an incorrect google maps api key, or correct key with insufficient permissions, then this will happen. It was the latter reason for us.
For further reading, documentation link, although the said behaviour is not mentioned anywhere. It should rather log an error message.

Resources