-
-
Save andrewgleave/915374 to your computer and use it in GitHub Desktop.
MKMapRect zoomRect = MKMapRectNull; | |
for (id <MKAnnotation> annotation in mapView.annotations) { | |
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate); | |
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0); | |
if (MKMapRectIsNull(zoomRect)) { | |
zoomRect = pointRect; | |
} else { | |
zoomRect = MKMapRectUnion(zoomRect, pointRect); | |
} | |
} | |
[mapView setVisibleMapRect:zoomRect animated:YES]; |
This worked perfectly for me.
Thanks a bunch.
Thank you very much for the idea (Y) You saved me some precious time
It works. Thanks
@philokezzar, as of iOS 7 you can simply use:
// Position the map such that the provided array of annotations are all visible to the fullest extent possible.
- (void)showAnnotations:(NSArray *)annotations animated:(BOOL)animated NS_AVAILABLE(10_9, 7_0);
Awesome !!!
Awsome yr :)
Here is a swift version:
func fitMapViewToAnnotaionList(annotations: [MKPointAnnotation]) -> Void {
let mapEdgePadding = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
var zoomRect:MKMapRect = MKMapRectNull
for index in 0..<annotations.count {
let annotation = annotations[index]
var aPoint:MKMapPoint = MKMapPointForCoordinate(annotation.coordinate)
var rect:MKMapRect = MKMapRectMake(aPoint.x, aPoint.y, 0.1, 0.1)
if MKMapRectIsNull(zoomRect) {
zoomRect = rect
} else {
zoomRect = MKMapRectUnion(zoomRect, rect)
}
}
mapView.setVisibleMapRect(zoomRect, edgePadding: mapEdgePadding, animated: true)
}
Could you setVisibleMapRect but don't focus center on sceen?
Hi guys, i have done following in my project not to have a function but created an extension for MapView and it works like charm! Thanks for the function Souf-R
//
// KMMapViewExtension.swift
//
import Foundation
import MapKit
extension MKMapView {
func fitMapViewToAnnotaionList() -> Void {
let mapEdgePadding = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
var zoomRect:MKMapRect = MKMapRectNull
for index in 0..<self.annotations.count {
let annotation = self.annotations[index]
let aPoint:MKMapPoint = MKMapPointForCoordinate(annotation.coordinate)
let rect:MKMapRect = MKMapRectMake(aPoint.x, aPoint.y, 0.1, 0.1)
if MKMapRectIsNull(zoomRect) {
zoomRect = rect
} else {
zoomRect = MKMapRectUnion(zoomRect, rect)
}
}
self.setVisibleMapRect(zoomRect, edgePadding: mapEdgePadding, animated: true)
}
}
What if you had multiple annotations but you only ever wanted to zoom out to the closest one to your current location?
All gists which are showed above, cannot include current location.
I just added some changes to @Souf-R's function
This version can include device's current location:
func fitMapViewToAnnotaionList(annotations: [MKPointAnnotation], userLocation: CLLocationCoordinate2D) -> Void {
let mapEdgePadding = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
var zoomRect:MKMapRect = MKMapRectNull
for index in 0..<annotations.count {
let annotation = annotations[index]
let aPoint:MKMapPoint = MKMapPointForCoordinate(annotation.coordinate)
let rect:MKMapRect = MKMapRectMake(aPoint.x, aPoint.y, 0.1, 0.1)
if MKMapRectIsNull(zoomRect) {
zoomRect = rect
} else {
zoomRect = MKMapRectUnion(zoomRect, rect)
}
}
let aPoint:MKMapPoint = MKMapPointForCoordinate(userLocation)
let rect:MKMapRect = MKMapRectMake(aPoint.x, aPoint.y, 0.1, 0.1)
if MKMapRectIsNull(zoomRect) {
zoomRect = rect
} else {
zoomRect = MKMapRectUnion(zoomRect, rect)
}
mapView.setVisibleMapRect(zoomRect, edgePadding: mapEdgePadding, animated: true)
}
Thanks
perfect solution!
Great solution!!! Thanks!! Here is the swift version of the code with padding set -
var zoomRect = MKMapRectNull
for annotation in mkMapView.annotations {
let annotationPoint = MKMapPointForCoordinate(annotation.coordinate)
let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0)
if (MKMapRectIsNull(zoomRect)) {
zoomRect = pointRect
} else {
zoomRect = MKMapRectUnion(zoomRect, pointRect)
}
}
mkMapView.setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(40, 40, 40, 40), animated: true)
Nice solution! Thanks
nice solutions
Thanks for sharing ,Saved my time :)
👍
Thanks for the Solution, works perfekt.
I needed to run
mkMapView.layoutIfNeeded()
before
mkMapView.setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(40, 40, 40, 40), animated: true)
if i did not run layoutIfNeeded() the EdgeInset is not set in ViewDidLoad or ViewDidApear
or the zoom is a bit to large.
Even easier:
static func fittingMapRect(forCoordinates coordinates: [CLLocationCoordinate2D]) -> MKMapRect {
guard !coordinates.isEmpty else {
return MKMapRectNull
}
let mapPoints = coordinates.map { MKMapPointForCoordinate($0) }
let mapRects = mapPoints.map { MKMapRect(origin: $0, size: MKMapSize(width: 1, height: 1)) }
let fittingRect = mapRects.reduce(MKMapRectNull, MKMapRectUnion)
return fittingRect
}
Unfortunately, showAnnotations
is too zoomed out and doesn't look good. Thanks for this code everyone, here's my condensed version:
mapView.setVisibleMapRect(
mapView.annotations.reduce(MKMapRectNull) { result, next in
let point = MKMapPointForCoordinate(next.coordinate)
let rect = MKMapRectMake(point.x, point.y, 0, 0)
guard !MKMapRectIsNull(result) else { return rect }
return MKMapRectUnion(result, rect)
},
edgePadding: UIEdgeInsetsMake(20, 20, 40, 20),
animated: true
)
extension MKMapView {
var MERCATOR_OFFSET : Double {
return 268435456.0
}
var MERCATOR_RADIUS : Double {
return 85445659.44705395
}
private func longitudeToPixelSpaceX(longitude: Double) -> Double {
return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * Double.pi / 180.0)
}
private func latitudeToPixelSpaceY(latitude: Double) -> Double {
return round(MERCATOR_OFFSET - MERCATOR_RADIUS * log((1 + sin(latitude * Double.pi / 180.0)) / (1 - sin(latitude * Double.pi / 180.0))) / 2.0)
}
private func pixelSpaceXToLongitude(pixelX: Double) -> Double {
return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / Double.pi;
}
private func pixelSpaceYToLatitude(pixelY: Double) -> Double {
return (Double.pi / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / Double.pi;
}
private func coordinateSpan(withMapView mapView: MKMapView, centerCoordinate: CLLocationCoordinate2D, zoomLevel: UInt) ->MKCoordinateSpan {
let centerPixelX = longitudeToPixelSpaceX(longitude: centerCoordinate.longitude)
let centerPixelY = latitudeToPixelSpaceY(latitude: centerCoordinate.latitude)
let zoomExponent = Double(20 - zoomLevel)
let zoomScale = pow(2.0, zoomExponent)
let mapSizeInPixels = mapView.bounds.size
let scaledMapWidth = Double(mapSizeInPixels.width) * zoomScale
let scaledMapHeight = Double(mapSizeInPixels.height) * zoomScale
let topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
let topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
// find delta between left and right longitudes
let minLng = pixelSpaceXToLongitude(pixelX: topLeftPixelX)
let maxLng = pixelSpaceXToLongitude(pixelX: topLeftPixelX + scaledMapWidth)
let longitudeDelta = maxLng - minLng;
let minLat = pixelSpaceYToLatitude(pixelY: topLeftPixelY)
let maxLat = pixelSpaceYToLatitude(pixelY: topLeftPixelY + scaledMapHeight)
let latitudeDelta = -1 * (maxLat - minLat);
let span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta)
return span
}
func zoom(toCenterCoordinate centerCoordinate:CLLocationCoordinate2D ,zoomLevel: UInt) {
let zoomLevel = min(zoomLevel, 20)
let span = self.coordinateSpan(withMapView: self, centerCoordinate: centerCoordinate, zoomLevel: zoomLevel)
let region = MKCoordinateRegionMake(centerCoordinate, span)
self.setRegion(region, animated: true)
}
}
Original Code Credit: http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/
Swift 2.3 Translation Credit: https://stackoverflow.com/users/2729171/apinho
Swift 4.x Swift Translation: By me. 👍
This is the easiest way:
map.layoutMargins = UIEdgeInsets(top: 10, right: 10, bottom: 10, left: 10)
map.showAnnotations(map.annotations, animated: true)
It works! I promise.
Thanks for this!
In my particular case, I had annotations and overlays for them, like the user's location blue circle overlay (userLocation.horizontalAccuracy
). To fit all the annotations and their overlays, I modified this a bit and got these two extensions.
extension MKMapView {
func setVisibleMapRectToFitAllAnnotations(animated: Bool = true,
shouldIncludeUserAccuracyRange: Bool = true,
edgePadding: UIEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)) {
var mapOverlays = overlays
if shouldIncludeUserAccuracyRange, let userLocation = userLocation.location {
let userAccuracyRangeCircle = MKCircle(center: userLocation.coordinate, radius: userLocation.horizontalAccuracy)
mapOverlays.append(MKOverlayRenderer(overlay: userAccuracyRangeCircle).overlay)
}
let zoomRect = MKMapRect(bounding: mapOverlays)
setVisibleMapRect(zoomRect, edgePadding: edgePadding, animated: animated)
}
}
extension MKMapRect {
init(bounding overlays: [MKOverlay]) {
self = .null
overlays.forEach { overlay in
let rect: MKMapRect = overlay.boundingMapRect
self = self.union(rect)
}
}
}
I just modified SimonJanevski's answer to show all annotations and overlays present on MapView.
extension MKMapView {
func setVisibleMapRectToFitAllAnnotations(animated: Bool = true,
shouldIncludeUserAccuracyRange: Bool = true,
shouldIncludeOverlays: Bool = true,
edgePadding: UIEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)) {
var mapOverlays = overlays
if shouldIncludeUserAccuracyRange, let userLocation = userLocation.location {
let userAccuracyRangeCircle = MKCircle(center: userLocation.coordinate, radius: userLocation.horizontalAccuracy)
mapOverlays.append(MKOverlayRenderer(overlay: userAccuracyRangeCircle).overlay)
}
if shouldIncludeOverlays {
let annotations = self.annotations.filter { !($0 is MKUserLocation) }
annotations.forEach { annotation in
let cirlce = MKCircle(center: annotation.coordinate, radius: 1)
mapOverlays.append(cirlce)
}
}
let zoomRect = MKMapRect(bounding: mapOverlays)
setVisibleMapRect(zoomRect, edgePadding: edgePadding, animated: animated)
}
}
extension MKMapRect {
init(bounding overlays: [MKOverlay]) {
self = .null
overlays.forEach { overlay in
let rect: MKMapRect = overlay.boundingMapRect
self = self.union(rect)
}
}
}
Thank you!!