-
-
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]; |
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)
}
}
}
What if you had multiple annotations but you only ever wanted to zoom out to the closest one to your current location?