I have created code that produces a geotargeting area represented by an annotation and MKCircle. The app notifies the user when they have entered and exited a region. Everything is working fine but I cannot figure out how to get the app to hold/display multiple regions (only one annotation/circle will show) here are a few snippets of my code:
override func viewDidLoad() {
super.viewDidLoad( )
//setup locationManager
locationManager.delegate = self
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
//setup mapView
mapView.delegate = self
mapView.showsUserLocation = true
mapView.userTrackingMode = .Follow
//setup test data will need to link coredata to pass in (LocationLabel, radius, address)
setupData("Test1", radius: 100, Address: "735 Main Rd, Clemson")
setupData("Test2", radius: 100, Address: "821 Main Rd, Clemson")
setupData("Test3", radius: 100, Address: "720 Main Rd, Clemson")
}
func setupData( Label: String, radius: Double, Address: String ) {
// check if system can monitor regions
if CLLocationManager.isMonitoringAvailableForClass(CLCircularRegion.self) {
//region data need to put in its own class to read multiple regions
let title = Label
let regionRadius = radius // in meters
let address = Address // street, city, state zip
//takes in the address of a location and converts it into 2d coordinates (lat/long)
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
if let placemarks = placemarks {
if placemarks.count != 0 {
let coordinates = placemarks.first!.location
let coordinate = coordinates?.coordinate
//setup region this will read an object with a saved coordinate and name
var region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: coordinate!.latitude,
longitude: coordinate!.longitude), radius: regionRadius, identifier: title)
self.locationManager.startMonitoringForRegion(region)
//setup annotation
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate!;
annotation.title = "\(title)";
self.mapView.addAnnotation(annotation)
//setup circle
let circle = MKCircle(centerCoordinate: coordinate!, radius: regionRadius)
self.mapView.addOverlay(circle)
}
else {
print("System can't track regions")
}
}
}
}
}
You have to implement the renderForOverlay function of MKMapViewDelegate to actually see those overlays that you have added. Try to increase your radius as well if you want it to see right away without zooming in.
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer
{
if let overlay = overlay as? MKCircle
{
let circleRenderer = MKCircleRenderer(circle: overlay)
circleRenderer.fillColor = UIColor.blueColor()
return circleRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
Related
I created my own marker with iconView for showing my own position. As you can see it is the green one. But every time I move the screen it is reloading my green view and it seems lagging. I don't have the same problem with the other marker shown, which are simple png.
How can I solve that? I can solve that problem by removing the line mapView.clear() But the result is that every loaded marker don't go away if I zoom out. So the map gets more and more crowded.
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
mapView.clear()
let fullCordinate = "\(mapView.camera.target.latitude),\(mapView.camera.target.longitude)"
networkGoogleApiConnect.fetchPoi(geoCordinate: fullCordinate)
networkGoogleApi.testVariable.forEach {
let marker1 = GMSMarker()
marker1.position = CLLocationCoordinate2D(latitude: $0.position.lat, longitude: $0.position.lng)
marker1.title = $0.title
marker1.icon = UIImage(named: "house")
var dict = [String:String]()
dict["id"] = $0.id
marker1.userData = dict
marker1.map = mapView
locationManager.stopUpdatingLocation()
}
locationManager.delegate=self
let neuLongitudeEigenerStandort = locationManager.location!.coordinate.longitude
let neuLatitudeEigenerStandort = locationManager.location!.coordinate.latitude
let fullCordinateEigenerStandort = "\(neuLatitudeEigenerStandort),\(neuLongitudeEigenerStandort)"
let ownMarker = GMSMarker()
ownMarker.iconView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
ownMarker.iconView?.backgroundColor = .green
ownMarker.position = CLLocationCoordinate2D(latitude: neuLatitudeEigenerStandort, longitude: neuLongitudeEigenerStandort)
ownMarker.title = "Eigene Position"
ownMarker.snippet = "GPS"
ownMarker.icon = GMSMarker.markerImage(with: .blue)
ownMarker.map = mapView
}
I'm trying to zoom google map on particular pin from lat long.it working fine but i want to change pin image when zoom in on pin in swift.i have doing like this but there have putting 2 image on same lat long.
func zoom(lat: Double, long : Double){
CATransaction.begin()
CATransaction.setValue(1, forKey: kCATransactionAnimationDuration)
// It will animate your camera to the specified lat and long
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: long, zoom: 15)
self.mapView!.animate(to: camera)
let position = CLLocationCoordinate2D(latitude: lat,longitude: long)
let marker = GMSMarker()
marker.map = self.mapView
marker.icon = UIImage.init(named: "pin-1")
CATransaction.commit()
}
You can put a function that detects changes in your zoom level then put a condition that will change the value of your marker icon.
Here is how my code looks like:
import UIKit
import GoogleMaps
class ViewController: UIViewController, GMSMapViewDelegate {
let marker = GMSMarker()
override func viewDidLoad() {
}
override func loadView() {
// Create a GMSCameraPosition that tells the map to display the
// coordinate -33.86,151.20 at zoom level 6.
let camera = GMSCameraPosition.camera(withLatitude: -33.86, longitude: 151.20, zoom: 6.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
self.view = mapView
mapView.delegate = self
// Creates a marker in the coordinate of the map.
marker.position = CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20)
marker.title = "Sydney"
marker.snippet = "Australia"
marker.map = mapView
marker.icon = UIImage(named: "pin_orange")
}
//This detect the changes in the cameraposition
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
let zoom = mapView.camera.zoom
print("map zoom is ",String(zoom))
//put a condition here to change the icon of your marker
if zoom > 6 {
marker.icon = UIImage(named: "icon1")
}else{
marker.icon = UIImage(named: "icon2")
}
}
}
Hope this helps!
I need to draw custom shapes like Arc, Semi-circle? I tried the below code but it's not rendering anything on the MKMapView.
Is this the right way to draw custom shapes?
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mapView.delegate = self
let center = CLLocationCoordinate2D(latitude: 15.463157486154865, longitude: 73.78846049308775)
let radius = CLLocationCoordinate2D(latitude: 15.495608080208948, longitude: 73.83418584279791)
addCircle(center: center, radius: radius)
}
private func createArcPath() -> UIBezierPath {
let center = CLLocationCoordinate2D(latitude: 15.463157486154865, longitude: 73.78846049308775)
// converting the coordinates to CGPoint with respect to MKMapView.
let centerPoint = mapView.convert(center, toPointTo: self.mapView)
// Creating bezierPath of arc.
let path = UIBezierPath(arcCenter: centerPoint, radius: 6080.205481929489, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 2, clockwise: true)
return path
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKCircle {
let arcRenderer = MKOverlayPathRenderer()
arcRenderer.path = createArcPath().cgPath
arcRenderer.strokeColor = UIColor.red
arcRenderer.fillColor = UIColor.red
arcRenderer.lineWidth = 10
arcRenderer.alpha = 1
arcRenderer.lineCap = .round
return arcRenderer
}
return MKOverlayRenderer()
}
}
extension ViewController {
private func addCircle(center ccoordinate: CLLocationCoordinate2D, radius rcoordinate: CLLocationCoordinate2D) {
let centerLocation = CLLocation.init(latitude: ccoordinate.latitude, longitude: ccoordinate.longitude)
let radiusLocation = CLLocation.init(latitude: rcoordinate.latitude, longitude: rcoordinate.longitude)
let radius = centerLocation.distance(from: radiusLocation)
let circle = MKCircle(center: ccoordinate, radius: radius)
mapView.addOverlay(circle)
}
}
Use this to add custom shape in MAPVIEW
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
if overlay is MKCircle
{
print("overlay latitude: "+String(overlay.coordinate.latitude))
print("overlay longitude: "+String(overlay.coordinate.longitude))
let circleOverlay = overlay as! MKCircle
if(circleOverlay.accessibilityPath != nil)
{
let arcRenderer = MKOverlayPathRenderer()
arcRenderer.path = circleOverlay.accessibilityPath?.cgPath
arcRenderer.strokeColor = UIColor.red
arcRenderer.lineWidth = 10
arcRenderer.alpha = 0.3
return arcRenderer
}
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.black
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
circle.alpha = 0.3
return circle
}
}
After some RND I came across a library curvyRoute & used it to draw arch on MKMapView.
// Adds arch overlay to the mapView
private func addArcOverlays() {
let pointA = CLLocationCoordinate2D(latitude: 15.463157486154865, longitude: 73.78846049308775)
let pointB = CLLocationCoordinate2D(latitude: 15.495608080208948, longitude: 73.83418584279791)
mapView.addOverlay(LineOverlay(origin: pointA, destination: pointB))
let style = LineOverlayStyle(strokeColor: .red, lineWidth: 4, alpha: 1)
let arc = ArcOverlay(origin: pointA, destination: pointB, style: style)
arc.radiusMultiplier = 0.5
mapView.addOverlay(arc)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
switch overlay {
case let lineOverlay as LineOverlay:
let linerender = MapLineOverlayRenderer(lineOverlay)
setVisibleMapRect(linerender.overlay, animated: true)
return linerender
case let polyline as MKPolyline:
let renderer = MKPolylineRenderer(overlay: polyline)
renderer.strokeColor = UIColor.yellow.withAlphaComponent(0.5)
renderer.lineWidth = 4
return renderer
default:
return MKOverlayRenderer()
}
}
I want to show mapView in UITableViewCells,
So I actually wrote this code
class DataPointTableCell: UITableViewCell {
#IBOutlet weak var mapView: MKMapView!
func addAnnotaionToMapView(_ coordinates: Coordinate) {
removePreviousCoordinate()
let viewCoorindate = CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longitude)
let annotation = MKPointAnnotation()
annotation.coordinate = viewCoorindate
mapView.addAnnotation(annotation)
// animate is turned to false on purpose.
mapView.animateToPoint(viewCoorindate, animated: false)
}
private func removePreviousCoordinate() {
let annotations = self.mapView.annotations
self.mapView.removeAnnotations(annotations)
}
}
The problem with this approach is that it animates the mapView location and then adds the marker as it deques the cell.
I went with another approach i.e. by using MKMapSnapshotter
private func setMapImage() {
let rect = self.mapImageView.bounds
let mapSnapshotOptions = MKMapSnapshotter.Options()
// Set the region of the map that is rendered.
let needleLocation = CLLocationCoordinate2DMake(15.4952364, 73.8343293)
let region = MKCoordinateRegion(center: needleLocation, latitudinalMeters: 1000, longitudinalMeters: 1000)
mapSnapshotOptions.region = region
// Set the scale of the image. We'll just use the scale of the current device, which is 2x scale on Retina screens.
mapSnapshotOptions.scale = UIScreen.main.scale
// Set the size of the image output.
mapSnapshotOptions.size = CGSize(width: 300, height: 300)
// Show buildings and Points of Interest on the snapshot
mapSnapshotOptions.showsBuildings = true
mapSnapshotOptions.showsPointsOfInterest = false
let snapshot = MKMapSnapshotter(options: mapSnapshotOptions)
snapshot.start { snapshot, error in
guard let snapshot = snapshot, error == nil else {
print("\(error!.localizedDescription)")
return
}
UIGraphicsBeginImageContextWithOptions(mapSnapshotOptions.size, true, 0)
snapshot.image.draw(at: .zero)
let pinView = MKPinAnnotationView(annotation: nil, reuseIdentifier: nil)
let pinImage = pinView.image
var point = snapshot.point(for: needleLocation)
if rect.contains(point) {
let pinCenterOffset = pinView.centerOffset
point.x -= pinView.bounds.size.width / 2
point.y -= pinView.bounds.size.height / 2
point.x += pinCenterOffset.x
point.y += pinCenterOffset.y
pinImage?.draw(at: point)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
DispatchQueue.main.async {
self.mapImageView.image = image
}
}
}
This works fine but I can't cache image based on anything.
So the help that I wanted is how to download and cache the mapImage using SDWebimage.
First add a unique key for your cell
var youUniqueCellKey = "Key"
Then in setMapImage first check if an image is already Cached or not and assign it to your mapImageView
private func setMapImage() {
if let image = SDImageCache.shared().imageFromCache(forKey: youUniqueCellKey) {
self.mapImageView.image = image
} else {
// Add your rest of your code here
// At the end
var imageToStore = UIImage() // Your mapImage
SDImageCache.shared().store(imageToStore, forKey: youUniqueCellKey, completion: nil)
}
}
Edit: I found a solution. MKMapView won't show the entire globe if you build it in Xcode's simulator, but it will if you build it on an actual device.
Existing versions of the question haven't worked - Need Swift 4, Xcode 10 solution:
I'm trying to zoom an MKMapView out to show the entire globe in my app. I can zoom out to see a map of the whole world, but not the entire globe.
Here's what I know:
-It has to be satellite, hybrid, satelliteFlyover, or hybridFlyover
-3D must be enabled.
Here is what I've tried:
-Adjusting the span of the region (no matter how big I make the span, it never zooms out enough to see the globe)
-Adjusting the altitude of the camera (no matter how high I make it, it never zooms out enough to see the globe)
I have googled and stack-overflowed, and I can't find any solutions that work. I am reduced to posting this question myself, violating my lurker principals, but I am totally stuck.
Here is some code I've tried from tutorials, etc. (several attempts commented out):
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
mapView.mapType = .standard
mapView.isPitchEnabled = true
// mapView.showsBuildings = true // displays buildings
let eiffelTowerCoordinates = CLLocationCoordinate2DMake(48.85815, 2.29452)
mapView.region = MKCoordinateRegion(center: eiffelTowerCoordinates, latitudinalMeters: 1000, longitudinalMeters: 100) // sets the visible region of the map
// create a 3D Camera
let mapCamera = MKMapCamera()
mapCamera.centerCoordinate = eiffelTowerCoordinates
mapCamera.pitch = 45
mapCamera.altitude = 50000000 // example altitude
mapCamera.heading = 45
// set the camera property
mapView.camera = mapCamera
// var ausCenter = CLLocationCoordinate2D(latitude: -25.354917, longitude: 134.347407)
// let camera = MKMapCamera(lookingAtCenter: ausCenter, fromDistance: 300, pitch: 10, heading: 0)
// mapView.camera = camera
// var ausCenter = CLLocationCoordinate2D(latitude: -25.354917, longitude: 134.347407)
// var ausSpan = MKCoordinateSpan(latitudeDelta: 5, longitudeDelta: 5)
// var region = MKCoordinateRegion(center: ausCenter, span: ausSpan)
// mapView.setRegion(region, animated: true)
// let camera = MKMapCamera()
// camera.centerCoordinate = mapView.centerCoordinate
// camera.pitch = 90.0
// camera.altitude = 30000000
// camera.heading = 0
// mapView.setCamera(camera, animated: true)
// var ausCenter = CLLocationCoordinate2D(latitude: -25.354917, longitude: 134.347407)
//
//// mapView.setCenter(ausCenter, animated: true)
//
// let span = MKCoordinateSpan(latitudeDelta: 150, longitudeDelta: 150)
// let region = MKCoordinateRegion(center: ausCenter, span: span)
// mapView.setRegion(region, animated: true)
//
// //add an annotation
// let annotation = MKPointAnnotation()
// annotation.coordinate = ausCenter
// annotation.title = "Australia"
// annotation.subtitle = "Details Forthcoming"
// mapView.addAnnotation(annotation)
}
}