【问题标题】:MKMapView setRegion animated does not show animationMKMapView setRegion 动画不显示动画
【发布时间】:2013-04-09 02:21:55
【问题描述】:

我有一个MKMapViewannotation pins。当视图被加载时,最近的 pin 被搜索并且地图将被缩放,因此它同时显示用户的位置和最近的 pin。我用[map setRegion:region animated:YES]; 做到这一点。到目前为止一切正常。同样的方法也可以通过点击一个按钮来定位用户,然后执行我刚才描述的操作。

我还有一个搜索字段,用户可以使用它搜索地图点。当用户点击其中一个搜索结果时,地图会设置区域,因此搜索到的图钉位于中间。 现在,这有点奇怪。我还设置了这个区域动画,至少我执行与上面相同的命令。但是,如果地图点离地图的当前可见部分太远,则在更改区域时不会显示动画。 我错过了什么吗?我已经看过 Apple 的文档,他们没有提到任何关于动画最大距离的内容。

我期待任何帮助!


更新 1:
刚刚在模拟器中再次测试。一个有趣的事实是,当我第一次搜索 MapPoint 然后选择一个搜索结果时,它没有动画。如果我在第一个搜索之后执行另一个搜索并选择一个结果,它会生成动画。一旦我点击定位按钮,它会将用户带回到他的位置和最近的点,它不会为这个setRegion: 和之后的第一次搜索设置动画。但仅在模拟器中,在我的 4S 上,它完全符合我在上面原始问题中的描述。


更新 2:
在 cmets 中,我被要求提供示例坐标。
所以这里是第一步的坐标(搜索自己的位置和最近的 pin):
我的位置: 47.227131 / 8.264251
最近的 pin: 47.251347 / 8.357191
他们之间的距离约为22公里。地图的中心是两个图钉之间的中心。从中心到屏幕边框的距离是两点之间距离的 1.5 倍,在这种情况下约为 33 公里。
这里是第二步的一组坐标(搜索地图点并选择它):
搜索的图钉: 46.790680 / 9.818824
到屏幕边框的距离在这里固定为 500 米。

【问题讨论】:

  • 您是否尝试过使用MKMapViewDelegate 并查看mapView:regionDidChangeAnimated: 的调用频率以及animated 的设置?
  • @mluisbrown 是的,我正在使用委托方法,并且我已经测试过:动画属性是 NO... 似乎 iOS 在任何地方都发生了变化,但这对我来说没有意义...
  • @mluisbrown 更正:并不总是 NO,只要它没有设置动画区域...
  • 至少和你看到的一致。您是否在应用的主线程上调用 setRegion:animated
  • @mluisbrown 是的,它在主线程上。这也是我首先假设的,但我在调用 setRegion:animated 之前将当前线程打印到日志中,并且它在主线程上调用。

标签: iphone ios objective-c mkmapview


【解决方案1】:

我已经在 iOS 6 和 iOS 7 beta 上使用一个简单的演示应用程序测试了这个问题。事实证明,地图视图实际上并不总是动画区域之间的过渡。这取决于区域之间的距离。例如,从巴黎到伦敦的过渡没有动画效果。但是如果你先缩小一点,然后去伦敦,它就会变成动画。

文档说:

animated: 如果您希望地图视图为 如果您希望地图居中,则过渡到新区域或否 立即指定区域。

但正如我们所见,我们不能依赖动画。我们只能告诉地图视图应该动画。 MapKit 决定动画是否合适。它告诉代理是否将在-(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated 中设置动画。

为了在所有情况下始终如一地为区域更改设置动画,您需要首先为中间区域设置动画。设 A 为当前地图区域,B 为目标区域。如果区域之间有交集,您可以直接过渡。 (将MKCoordinateRegion 转换为MKMapRect 并使用MKMapRectIntersection 查找交集)。如果没有交叉点,则计算跨越两个区域的区域 C(使用 MKMapRectUnionMKCoordinateRegionForMapRect)。然后先去C区,在regionDidChangeAnimated去B区。

示例代码:

MKCoordinateRegion region = _destinationRegion;    
MKMapRect rect = MKMapRectForCoordinateRegion(_destinationRegion);
MKMapRect intersection = MKMapRectIntersection(rect, _mapView.visibleMapRect);
if (MKMapRectIsNull(intersection)) {
    rect = MKMapRectUnion(rect, _mapView.visibleMapRect);
    region = MKCoordinateRegionForMapRect(rect);
    _intermediateAnimation = YES;
}
[_mapView setRegion:region animated:YES];

-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    if (_intermediateAnimation) {
        _intermediateAnimation = NO;
        [_mapView setRegion:_destinationRegion animated:YES];
    }
}

这个辅助方法取自here

MKMapRect MKMapRectForCoordinateRegion(MKCoordinateRegion region)
{
    MKMapPoint a = MKMapPointForCoordinate(CLLocationCoordinate2DMake(
                                                                      region.center.latitude + region.span.latitudeDelta / 2,
                                                                      region.center.longitude - region.span.longitudeDelta / 2));
    MKMapPoint b = MKMapPointForCoordinate(CLLocationCoordinate2DMake(
                                                                      region.center.latitude - region.span.latitudeDelta / 2,
                                                                      region.center.longitude + region.span.longitudeDelta / 2));
    return MKMapRectMake(MIN(a.x,b.x), MIN(a.y,b.y), ABS(a.x-b.x), ABS(a.y-b.y));
}

WWDC 2013 session 309 Putting Map Kit in Perspective解释了如何在 iOS 7 中进行如此复杂的转换。

【讨论】:

  • 这听起来很合理!我已经发现,如果我先将区域设置为初始状态,它就会动画。但是因为这看起来很糟糕,我再次删除了它。但是首先找到(最小的)交叉区域看起来要好得多。非常感谢您的解决方案!
【解决方案2】:

致任何有相同问题并使用 Swift 并使用 tableView 的人:

我在关闭 tableView 后调用了 setRegion,但它没有显示动画。这是我编辑前的代码:

dismiss(animated: true, completion: nil)
a function that calls setRegion

然后我改成:

dismiss(animated: true, completion: {
        a function that calls setRegion
    })

这次成功了。

【讨论】:

    【解决方案3】:

    以下是@Felix 重写为 Swift 4 的函数:

    // MARK: - MapView help properties
    var destinationRegion: MKCoordinateRegion?
    var intermediateAnimation = false
    
    func center() {
        // Center map
        var initialCoordinates = CLLocationCoordinate2D(latitude: 49.195061, longitude: 16.606836)
        var regionRadius: CLLocationDistance = 5000000
    
        destinationRegion = MKCoordinateRegionMakeWithDistance(initialCoordinates, regionRadius * 2.0, regionRadius * 2.0)
        centreMap(on: destinationRegion!)
    }
    
    
    private func mapRect(forCoordinateRegion region: MKCoordinateRegion) -> MKMapRect {
        let topLeft = CLLocationCoordinate2D(latitude: region.center.latitude + (region.span.latitudeDelta/2), longitude: region.center.longitude - (region.span.longitudeDelta/2))
        let bottomRight = CLLocationCoordinate2D(latitude: region.center.latitude - (region.span.latitudeDelta/2), longitude: region.center.longitude + (region.span.longitudeDelta/2))
    
        let a = MKMapPointForCoordinate(topLeft)
        let b = MKMapPointForCoordinate(bottomRight)
    
        return MKMapRect(origin: MKMapPoint(x:min(a.x,b.x), y:min(a.y,b.y)), size: MKMapSize(width: abs(a.x-b.x), height: abs(a.y-b.y)))
    }
    
    
    func centreMap(on region: MKCoordinateRegion) {
        var region = region
        var rect = mapRect(forCoordinateRegion: region)
        let intersection = MKMapRectIntersection(rect, mapView.visibleMapRect)
    
        if MKMapRectIsNull(intersection) {
            rect = MKMapRectUnion(rect, mapView.visibleMapRect)
            region = MKCoordinateRegionForMapRect(rect)
            intermediateAnimation = true
        }
    
        mapView.setRegion(region, animated: true)
    }
    
    // MARK: - MKMapViewDelegate
    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        if intermediateAnimation, let region = destinationRegion {
            intermediateAnimation = false
            mapView.setRegion(region, animated: true)
        }
    }
    

    【讨论】:

      【解决方案4】:

      我也有这个问题,它并不总是动画,有时它只是跳跃和溶解。但是,我注意到如果您为 camera 而不是 region 设置动画,它会始终保持动画。

      但是使用相机,您必须设置眼睛距离/高度而不是纬度/经度跨度。对于下面的粗略计算,我有一个简单的计算,它基本上只是将高度(以米为单位)设置为与该地区的经度跨度相同的米数。如果您想要精确的精度,您必须计算出该地区纬度每度的米数,由于地球不是一个完美的球体,因此略有变化。当然,您可以乘以该值来扩大或缩小视野。

      Swift 4.1 示例代码:

      /* -------------------------------------------------------------------------- */
      func animateToMapRegion(_ region: MKCoordinateRegion) {
          // Quick and dirty calculation of altitude necessary to show region.
          // 111 kilometers per degree longitude.
          let metersPerDegree: Double = 111 * 1_000
          let altitude = region.span.longitudeDelta * metersPerDegree
      
          let camera = MKMapCamera(lookingAtCenter: region.center, fromEyeCoordinate: region.center, eyeAltitude: altitude)
      
          self.mapView.setCamera(camera, animated: true)
      }
      

      【讨论】:

      • 不使用经度,而是使用纬度,其每度的距离值不会改变。对于彼此相对较近的位置,纬度/经度比不会发生显着变化。我猜这个位置和经度一样可能会改变纬度,所以这应该一样好。
      • 更好的是,将这两个点转换为 CLLocations,这样您就可以使用 distance(from:) 方法获取它们的距离(以米为单位)。
      【解决方案5】:

      您只需获取当前位置,然后调用此函数:

      import MapKit

      var appleMapView = MKMapView()

      var currentCoordinate: CLLocationCoordinate2D?

      currentCoordinate 必须是您当前的位置坐标:

       ◙ if let currentLoc = self.currentCoordinate {
              let center = CLLocationCoordinate2D(latitude: currentLoc.latitude, longitude: currentLoc.longitude)
              let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
              appleMapView.setRegion(region, animated: true)
          }
      

      【讨论】:

      • 解释得很好。
      猜你喜欢
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-15
      • 2018-06-12
      • 2015-09-16
      相关资源
      最近更新 更多