如何在 MKMapView 中将图钉和地图保持在移动叠加层上方的中心

Posted

技术标签:

【中文标题】如何在 MKMapView 中将图钉和地图保持在移动叠加层上方的中心【英文标题】:How to keep pin and map centered above moving overlay in an MKMapView 【发布时间】:2015-05-17 14:49:55 【问题描述】:

当我(通过平移手势)在地图上垂直移动另一个视图时,如何使图钉保持在地图的中心,以使图钉保持在叠加层上方(不是实际的 MapKit 叠加层)。

有关第一个和最后一个状态,请参阅随附的屏幕截图。

当用户向上/向下平移时,我得到了覆盖层和屏幕顶部之间空间的 CGRect。但是,我如何使用它来移动地图并在用户向上平移时放大地图并在用户向下平移时再次缩小,到目前为止,我一直无法理解。

我尝试了不同的方法,从尝试调整可见矩形到调整地图视图的框架。答案可能在于一些 MKMapRect / Region 诡计..

(FreepikCC BY 3.0 的手形图标)

【问题讨论】:

你试过setCenterCoordinate的方法吗? 平移叠加层时是否应该放大和缩小地图? @keithbhunter 是的,向上平移时向内,向下平移时向外.. 【参考方案1】:

实际上,keithbhunter 的代码很慢,因为除了更新区域的速度超过了加载速度之外,地图的高度也在变化,这会导致额外的开销!

我更新了代码,使其运行顺畅。

使用此代码,我所做的是保持地图视图的大小相同,但我移动中心点以补偿滑动视图的高度。

要使这段代码正常工作,您必须修改 keithbhunter 的设置,以便将 mapView 的底部约束完全固定到超级视图的底部(而不是滑动视图(以便 mapView 始终与超级视图大小相同)。其余的设置都是一样的。

还可以使用变量maxMetersDistance自定义缩放量

我在这里,始终以埃菲尔铁塔为中心

import UIKit
import MapKit

class ViewController: UIViewController 

    @IBOutlet weak var mapView: MKMapView!
    @IBOutlet weak var slidingView: UIView!
    @IBOutlet weak var slidingViewHeight: NSLayoutConstraint!
    var maxMetersDistance:CGFloat = 10000.0; // customize this to set how far the map zooms out of the interest area

    override func viewDidLoad() 
        super.viewDidLoad()
        let pan = UIPanGestureRecognizer(target: self, action: "viewDidPan:")
        self.slidingView.addGestureRecognizer(pan)
        firstTimeCenter()
    

    func firstTimeCenter()
         var coordinate = CLLocationCoordinate2D(latitude: 48.8582, longitude: 2.2945)
        let region = MKCoordinateRegionMakeWithDistance(coordinate, Double(maxMetersDistance), Double(maxMetersDistance))
        self.mapView.setRegion(region, animated: true)
    

    func reloadMap() 
        let height = CGFloat(self.slidingViewHeight.constant)
        var regionDistance = (maxMetersDistance / self.view.frame.height) * height
        regionDistance = maxMetersDistance - regionDistance
        var coordinate = CLLocationCoordinate2D(latitude: 48.8582, longitude: 2.2945)
        var mapRect = mapView.visibleMapRect;
        var metersPerMapPoint = MKMetersPerMapPointAtLatitude(coordinate.latitude);
        var metersPerPixel = CGFloat(metersPerMapPoint) * CGFloat(mapRect.size.width) / CGFloat(mapView.bounds.size.width);
        var totalMeters = Double(metersPerPixel) * Double(height/2)

        coordinate = self.translateCoord(coordinate, MetersLat: -totalMeters, MetersLong: 0.0)

        let region = MKCoordinateRegionMakeWithDistance(coordinate, Double(regionDistance), Double(regionDistance))
        self.mapView.setRegion(region, animated: false)
    

    func viewDidPan(panGesture: UIPanGestureRecognizer) 
        let location = panGesture.locationInView(self.view)
        self.slidingViewHeight.constant = self.view.frame.size.height - location.y
        self.reloadMap()
    

    func translateCoord(coord:CLLocationCoordinate2D, MetersLat:Double,  MetersLong:Double)->CLLocationCoordinate2D
        var tempCoord = CLLocationCoordinate2D()
        var tempRegion = MKCoordinateRegionMakeWithDistance(coord, MetersLat, MetersLong);
        var tempSpan = tempRegion.span;
        tempCoord.latitude = coord.latitude + tempSpan.latitudeDelta;
        tempCoord.longitude = coord.longitude + tempSpan.longitudeDelta;
        return tempCoord;
    


【讨论】:

感谢胡安和基思! Juan 的解决方案有一些跳跃性,我必须弄清楚,但这与我正在寻找的非常接近 :)【参考方案2】:
class ViewController: UIViewController 

    @IBOutlet weak var mapView: MKMapView!
    @IBOutlet weak var slidingView: UIView!
    @IBOutlet weak var slidingViewHeight: NSLayoutConstraint!


    override func viewDidLoad() 
        super.viewDidLoad()
        self.reloadMap()
        let pan = UIPanGestureRecognizer(target: self, action: "viewDidPan:")
        self.slidingView.addGestureRecognizer(pan)
    

    func reloadMap() 
        let coordinate = CLLocationCoordinate2D(latitude: 37.332363, longitude: -122.030805)
        let height = Double(self.mapView.frame.size.height)
        let regionDistance = 0.3 * height * height  // random multiplier and exponential equation for scaling
        let region = MKCoordinateRegionMakeWithDistance(coordinate, regionDistance, regionDistance)
        self.mapView.setRegion(region, animated: false)
    

    func viewDidPan(panGesture: UIPanGestureRecognizer) 
        let location = panGesture.locationInView(self.view)
        self.slidingViewHeight.constant = self.view.frame.size.height - location.y
        self.reloadMap()
    


要设置视图,请在视图控制器中放置一个地图视图和一个UIView。使用自动布局将地图视图的左侧、顶部和右侧固定到超级视图。然后将地图视图的底部固定到UIView 的顶部。接下来,将UIView 的左侧、底部和右侧固定到超级视图。最后,将UIView 的高度约束设置为您希望它初始化的任何内容。这个高度值将随着用户拖动视图而改变。这使得UIView 可以随心所欲地增长,同时满足自动布局。

为地图视图、UIViewUIView 的高度约束添加一个@IBOutlet 到您的视图控制器,就像上面的代码一样。 regionDistance 属性在这里发挥了缩放的魔力。这是一个指数方程(我随机组成),它将根据地图视图的高度计算更大或更小的区域。 reloadMap() 使用它来更新地图的缩放。将它们捆绑在一起的是UIView 上的UIPanGestureRecognizer,它控制UIView 的高度,从而导致地图上的缩放动作。

陷阱:这会迫使地图更新区域的速度快于加载区域的速度,使其看起来非常跳跃。可能有办法解决这个问题。发挥创意。

我在示例中使用的坐标是 Apple 的总部。

【讨论】:

以上是关于如何在 MKMapView 中将图钉和地图保持在移动叠加层上方的中心的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 的新地图视图中制作可点击的图钉?

隐藏地图图钉注释图像

在地图视图上拖动注释图钉

iPhone MKMapView 注解聚类

从给定位置拖动图钉

地图注释显示不正确的自定义图钉图像