I'm implementing a custom style for my google maps view for a bus transit app I'm developing in XCode 9 w/ Swift 4. Whenever I load a map view, it always takes a little less than a second to load the custom style and I'm not sure what's causing this to happen.
Here's the effect I'm describing:
As you can see, the tan background is the default style for google's mapview, and it's visible for only a small period of time.
Here's my code that implements the map view:
class StopPredictionVC: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setUpMapView()
}
private func setUpMapView() {
let stopLatitude = Double(stop!.lat)
let stopLongitude = Double(stop!.lon)
let camera = GMSCameraPosition.camera(withLatitude: stopLatitude!, longitude: stopLongitude!, zoom: 16.4)
let frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)
mapView = GMSMapView.map(withFrame: frame, camera: camera)
let nightModeEnabled = APIWrapper.sharedInstance.settings.nightMode!
if nightModeEnabled {
mapView.mapStyle(withFilename: "nightStyle", andType: "json")
} else {
mapView.mapStyle(withFilename: "mapStyle", andType: "json")
}
let marker = GMSMarker()
marker.icon = #imageLiteral(resourceName: "marker")
marker.appearAnimation = .pop
marker.position = CLLocationCoordinate2D(latitude: stopLatitude!, longitude: stopLongitude!)
marker.title = "Bus Stop Name"
marker.snippet = "Example description"
marker.map = mapView
buildRoute(routePath: routeConfig!.path)
view.addSubview(mapView)
}
}
extension GMSMapView {
func mapStyle(withFilename name: String, andType type: String) {
do {
if let styleURL = Bundle.main.url(forResource: name, withExtension: type) {
self.mapStyle = try GMSMapStyle(contentsOfFileURL: styleURL)
} else {
NSLog("Unable to find style.json")
}
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
}
}
buildRoute(routePath:) is a function that builds the blue colored path on the road if anyone was wondering.
Obviously this isn't a huge bug, but it's quite frustrating to see every time I load a map view. Anyone see anything in my code that could be causing this?
You call it inside viewWillAppear it has 2 problems , first it's called after viewDidLoad (has some delay) , second it may be called every time the VC is shown such as returning from a push / dismiss Modal
so call setUpMapView inside viewDidLoad
note this buildRoute(routePath: routeConfig!.path) should be in a background queue if it calls any web service or do any long job inside mainQueue
BTW i may also try to load
GMSMapStyle(contentsOfFileURL: styleURL)
in background thread and after load set in mainQueue and that only if using GMSMapStyle is allowed inside a back thread
Related
I got some Code from the Mapbox Tutorials for a basic Navigation App in a Turn -by- Turn View. Everything is working fine, and I've already added a Waypoint.
In the example the origin of the route is set with fixed coordinates. That's not compatible with my Use Case. The Waypoints and the Destination are also fixed by coordinates, which is good. But the origin has to be the "Location of the User", which is obviously variable.
Maybe someone could help me, would be much appreciated. :)
import Foundation
import UIKit
import MapboxCoreNavigation
import MapboxNavigation
import MapboxDirections
class PlacemarktestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let origin = CLLocationCoordinate2DMake(37.77440680146262, -122.43539772352648)
let waypoint = CLLocationCoordinate2DMake(27.76556957793795, -112.42409811526268)
let destination = CLLocationCoordinate2DMake(37.76556957793795, -122.42409811526268)
***strong text***
let options = NavigationRouteOptions(coordinates: [origin, waypoint, destination])
Directions.shared.calculate(options) { [weak self] (session, result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let response):
guard let route = response.routes?.first, let strongSelf = self else {
return
}
// For demonstration purposes, simulate locations if the Simulate Navigation option is on.
// Since first route is retrieved from response `routeIndex` is set to 0.
let navigationService = MapboxNavigationService(route: route, routeIndex: 0, routeOptions: options)
let navigationOptions = NavigationOptions(navigationService: navigationService)
let navigationViewController = NavigationViewController(for: route, routeIndex: 0, routeOptions: options, navigationOptions: navigationOptions)
navigationViewController.modalPresentationStyle = .fullScreen
// Render part of the route that has been traversed with full transparency, to give the illusion of a disappearing route.
navigationViewController.routeLineTracksTraversal = true
strongSelf.present(navigationViewController, animated: true, completion: nil)
}
}
}
}
#zeno, to set user location you can use
mapView.setUserTrackingMode(.follow, animated: true)
don't forget to add NSLocationWhenInUseUsageDescription key to info plist.
Or you can get coordinates and set them manually using CoreLocation
import CoreLocation
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestLocation() // Request a user’s location
Documentation is here requestLocation
And then implement the CLLocationManagerDelegate method locationManager(:, didUpdateLocations:) to handle the requested user location. This method will be called once after using locationManager.requestLocation().
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
if let location = locations.first {
let latitude = location.coordinate.latitude
let longitude = location.coordinate.longitude
}
}
I have got the Location function of my app working but I want to add a custom image for the Location Button. How do I go about doing that?
I created a whole new button and made it serve as a location button with this code:
#IBAction func myLocationButton(_ sender: UIButton) {
guard let lat = self.mapView.myLocation?.coordinate.latitude, let lng = self.mapView.myLocation?.coordinate.longitude else {
return
}
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: lng, zoom: 16)
self.mapView.animate(to: camera)
}
The final outcome:
I am creating a ViewController in which I want to have a somewhat small UIView in the corner of the ViewController to display the camera preview. I am using a function to do this. However when I pass in the small UIView into the function the camera preview is not showing up. The weird thing is if I tell the function to display the preview on self.view everything works fine and I can see the camera preview. For this reason I think the problem is with the way I insert the layer or something similar.
Here is the function I am using to display the preview...
func displayPreview(on view: UIView) throws {
guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer?.connection?.videoOrientation = .portrait
view.layer.insertSublayer(self.previewLayer!, at: 0)
self.previewLayer?.frame = view.frame
}
I call this function from inside another function which handles setting up the capture session and other similar things.
func configureCameraController() {
cameraController.prepare {(error) in
if let error = error {
print("ERROR")
print(error)
}else{
}
print("hello")
try! self.cameraController.displayPreview(on: self.mirrorView)
}
}
configureCameraController()
How can I get the camera preview layer to show up on the smaller UIView?
Can you try adding the following
let rootLayer: CALayer = self.yourSmallerView.layer
rootLayer.masksToBounds = true
self.previewLayer.frame = rootLayer.bounds
rootLayer.addSublayer(self.previewLayer)
in place of
view.layer.insertSublayer(self.previewLayer!, at: 0)
Also ensure, yourSmallerView.contentMode = UIViewContentMode.scaleToFill
For an AR experience I'm working on, I want to have a "camera view" that shows annotations based on the user's location. If the user is in a certain area, show the annotation.
I'm able to do something like this using below
extension ViewController: AnnotationManagerDelegate {
func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
print("camera did change tracking state: \(camera.trackingState)")
let annotationLocation = CLLocation()
let point = CGPoint(x: annotationLocation.coordinate.longitude, y: annotationLocation.coordinate.latitude)
let features = mapView.visibleFeatures(at: point);
if let score = features.first(where: { $0.attributes["score"] as! Int >= 5 }) {
// ...
But in my AR view, I want to hide the map - not show it. When I try setting mapView.isHidden = true - the query always fails.
This makes sense because the query is for visible features. How can instead hide the map, but still query tiles for features?
Go into Mapbox Studio https://www.mapbox.com/studio/ and create a new map style and remove all of the layers. You can style a Mapbox map to be a single solid color (land, water, roads, etc.), which is what you need. If you need to toggle between a map and a blank map, simply toggle between styles.
let basicMap = URL(string: "mapbox://styles/mapbox/outdoors-v9")
let blankMap = URL(string: "yourCustomURLFromMapboxStudio")
let mapView = MGLMapView(frame: view.bounds, styleURL: blankMap)
well, guess this one is kinda big...
I'm making an app in which there will be a MapKit and a button. When the user clicks the button, he/she will open a new ViewController with several options of destinations to go. After confirming the one he wants to go, the MapKit view will reopen with directions for the specific place. To do this, I created a function on the ViewController linked to the MapKit view:
func createmap(latit: Double, longit: Double){
//set what's going to show up in the Map
MapView.delegate = self
MapView.showsScale = true
MapView.showsPointsOfInterest = true
MapView.showsUserLocation = true
//request authorization for user location data storage
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
//if authorization is given, use the user location
if CLLocationManager.locationServicesEnabled(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
//set source and destination coordinates of direction recommendation
let sourceCoordinates = locationManager.location?.coordinate
let destinationCoords = CLLocationCoordinate2D(latitude: latit, longitude: longit)
//set placemarks with source and destination coordinates
let sourcePlacemark = MKPlacemark(coordinate: sourceCoordinates!)
let destPlacemark = MKPlacemark(coordinate: destinationCoords)
//put placemarks on maps with the source and destination coordinates
let sourceItem = MKMapItem(placemark: sourcePlacemark)
let destItem = MKMapItem(placemark: destPlacemark)
//set direction request, source and destination request coordinates and transport type
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceItem
directionRequest.destination = destItem
directionRequest.transportType = .automobile
//set response if sucess or error
let directions = MKDirections(request: directionRequest)
directions.calculate(completionHandler: {
response, error in
guard let response = response else {
if let error = error {
print("Something went wrong.")
}
return
}
//set format of route line
let route = response.routes[0]
self.MapView.add(route.polyline, level: .aboveRoads)
//set map framing
let rekt = route.polyline.boundingMapRect
self.MapView.setRegion(MKCoordinateRegionForMapRect(rekt), animated: true)
})
}
And then, to make this function run, I did this:
override func viewDidLoad() {
super.viewDidLoad()
createmap(latit: lat, longit: long)
}
lat and long are variables declared publicly.
In the other view, I have a button called "go back to the Map" in which I try to make a simple segue. However, there is a problem with this code: if I get directions to some place, reopen the other view and then press "go back to the Map", the directions that were set disappear. I don't know what to do to make them stay, can someone please help?
just a bit more information: the "go back to the Map" button is linked to the MapKit view as a simple segue (i clicked, dragged and dropped to the MapKit view, didn't write any line of code)
I think problem is your Map VC is already added into the navigation stack so ViewDidLoad method is not called again you should move your code from viewdidload to viewWill appear like
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
createmap(latit: lat, longit: long)
}
Did you used this Function to add a polyline? you added poly line correctly . but I think you had not provided values to poly line for drawing path
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 4.0
return renderer
}