【问题标题】:Z-index of iOS MapKit user location annotationiOS MapKit 用户位置标注的 Z-index
【发布时间】:2011-08-22 01:30:29
【问题描述】:

我需要在所有其他注释之上绘制当前用户注释(蓝点)。现在它被绘制在我的其他注释下方并被隐藏。我想调整这个注释视图(蓝点)的 z-index 并将其置于顶部,有人知道怎么做吗?

更新: 所以我现在可以处理 MKUserLocationView,但我该如何将其提前?

- (void) mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views {
    for (MKAnnotationView *view in views) {
        if ([[view annotation] isKindOfClass:[MKUserLocation class]]) {
            // How do I bring this view to the front of all other annotations?
            // [???? bringSubviewToFront:????];
        }
    }
}

【问题讨论】:

    标签: objective-c ios mapkit


    【解决方案1】:

    感谢Paul Tiarks 的帮助,终于使用下面列出的代码让它工作了。我遇到的问题是 MKUserLocation 注释在任何其他注释之前首先添加到地图中,因此当您添加其他注释时,它们的顺序似乎是随机的,并且最终仍会位于 MKUserLocation 注释之上。为了解决这个问题,我必须将所有其他注释移到后面,并将 MKUserLocation 注释移到前面。

    - (void) mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views 
    {
        for (MKAnnotationView *view in views) 
        {
            if ([[view annotation] isKindOfClass:[MKUserLocation class]]) 
            {
                [[view superview] bringSubviewToFront:view];
            } 
            else 
            {
                [[view superview] sendSubviewToBack:view];
            }
        }
    }
    

    更新:您可能需要添加下面的代码,以确保在将蓝点滚动到地图的可视区域之外时将其绘制在顶部。

    - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
    {        
      for (NSObject *annotation in [mapView annotations]) 
      {
        if ([annotation isKindOfClass:[MKUserLocation class]]) 
        {
          NSLog(@"Bring blue location dot to front");
          MKAnnotationView *view = [mapView viewForAnnotation:(MKUserLocation *)annotation];
          [[view superview] bringSubviewToFront:view];
        }
      }
    }
    

    【讨论】:

    • 这个解决方案对我来说似乎并不可靠。如果我将地图拖到屏幕外并返回到它,蓝色位置点最终会出现在我的自定义注释下方,这可能是因为地图视图使用其原始布局重绘了视图。
    • @DanJ 然后在- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated里面使用同样的逻辑,循环遍历mapView.annotations找到[mapView viewForAnnotation:annotation]
    • 好点,这确实解决了问题,尽管我发现将代码放入 regionDidChangeAnimated 比放入 regionWillChangeAnimated 更可靠。我会更新您的答案以包含额外的代码。 FTR,当使用大量注解作为 region*ChangeAnimated 方法时,会有轻微的性能问题。
    【解决方案2】:

    另一种解决方案: 设置注释视图层的 zPosition (annotationView.layer.zPosition) in:

    - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views;

    【讨论】:

    • 这个解决方案对我来说是最可靠的。
    • 这比管理子视图的顺序要容易/干净得多。
    • 这使得引脚覆盖其他引脚的标注
    • 我通过切换 z 位置解决了覆盖标注问题 - mapView:didSelectAnnotationView: 和 mapView:didDeselectAnnotationView:
    • yuf,见下方评论
    【解决方案3】:

    该线程的官方答案是错误的...使用 zPosition 确实是最好的方法,并且与使用 regionDidChangeAnimated 相比最快...

    否则,您会因地图上的许多注释而遭受巨大的性能影响(因为每次更改帧都会重新扫描所有注释)。并一直在测试它......

    所以在创建注解视图时(或在 didAddAnnotationViews 中)设置: self.layer.zPosition = -1; (低于所有其他人)

    正如 yuf 所指出的: 这使得引脚覆盖其他引脚的标注 – yuf 2013 年 12 月 5 日 20:25

    即注释视图将出现在其他引脚下方。

    要修复,只需在选择时将 zPosition 设置为 0

    -(void) mapView:(MKMapView*)mapView didSelectAnnotationView:(MKAnnotationView*)view {
        if ([view isKindOfClass:[MyCustomAnnotationView class]])
            view.layer.zPosition = 0;
       ...
    }
    
    -(void) mapView:(MKMapView*)mapView didDeselectAnnotationView:(MKAnnotationView*)view {
        if ([view isKindOfClass:[MyCustomAnnotationView class]])
            view.layer.zPosition = -1;
       ...
    }
    

    【讨论】:

    • 当您只需要管理视觉效果时,此解决方案很不错,但如果您还需要处理点击/触摸优先级,则它不起作用(您点击似乎在顶部的图钉其他的(因为 zPosition 为 0),但您实际上点击了下面的图钉)。
    • 这是苹果的标准引脚行为...更改 zlocation 并不能阻止这一点...但这是另一个问题。为了不弹出,您应该在自定义注释中使用 self.canShowCallout = NO
    【解决方案4】:

    iOS 14 更新

    我知道这是一篇旧帖子,但这个问题仍然适用,当你在你最喜欢的搜索引擎中输入它时,你最终会出现在这里。

    从 iOS 14 开始,Apple introduced a zPriority property to MKAnnotationView。您可以使用它来使用 predefined constants 或浮点数为您的注释设置 z-index。 此外,Apple 最终让我们自己为用户位置创建视图成为可能,并提供了MKUserLocationView 作为MKAnnotationView 的子类。

    From the documentation for MKUserLocationView:

    如果要指定附加配置,如zPriority, 直接创建此注释视图。要显示注释视图, 从mapView(_:viewFor:) 返回实例。

    以下代码 sn-p 显示了如何做到这一点(将代码添加到您的 MKMapViewDelegate):

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        
        // Alter the MKUserLocationView (iOS 14+)
        if #available(iOS 14.0, *), annotation is MKUserLocation {
            
            // Try to reuse the existing view that we create below
            let reuseIdentifier = "userLocation"
            if let existingView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) {
                return existingView
            }
            let view = MKUserLocationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
            
            view.zPriority = .max   // Show user location above other annotations
            view.isEnabled = false  // Ignore touch events and do not show callout
            
            return view
        }
        
        // Create views for other annotations or return nil to use the default representation
        
        return nil
    }
    

    请注意,默认情况下,用户位置注释在点击时会显示标注。现在用户位置覆盖了您的其他注释,您可能想要禁用它,这在代码中通过将.isEnabled 设置为false 来完成。

    【讨论】:

      【解决方案5】:

      只需使用.layer.anchorPointZ 属性。

      例子:

       func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
              views.forEach {
                  if let _ = $0.annotation as? MKUserLocation {
                      $0.layer.anchorPointZ = 0
                  } else {
                      $0.layer.anchorPointZ = 1
                  }
              }
          }
      

      这里有参考https://developer.apple.com/documentation/quartzcore/calayer/1410796-anchorpointz

      【讨论】:

      • 太棒了!但请注意,每个人,结果顺序与 zPosition 是相反的。 IE。 layer.anchorPointZ =0 在上面,100 在下面。
      【解决方案6】:

      尝试获取对用户位置注释的引用(可能在 mapView: didAddAnnotationViews: 中),然后在添加所有注释后将该视图带到 mapView 的前面。

      【讨论】:

      • 我还没想好处理它,因为你无法测试它的类 MKUserLocationView 并且我所有的其他注释都是常规的 MKAnnotationView 对象,而 MKUserLocationView 是 MKAnnotationView 的子类...我正在考虑将我的注释切换到覆盖,以便我可以管理这些的 zindex 并将它们与用户位置注释分开。还有其他想法吗?
      • 您应该能够测试注解属性的类以查看它是否是 MKUserLocation 并将其保留为您的用户位置视图。
      • 感谢您上面的建议,我已经能够获得 MKUserLocationView,但我无法将其提出来。我用bringSubviewToFront 尝试了各种咒语:但我试过的都没有。有什么想法吗?
      • 你可能需要让你的 MKUserLocationView 的 superview 把它放到前面。我确信 MKMapView 在幕后做了一些不可预测的视图层次结构,但直接要求 MKUserLocationView 的超级视图将其置于前面应该可以解决问题,无论如何值得一试。
      • 扩展上面保罗的评论,你试过[[view superview] bringSubviewToFront:view];吗?
      【解决方案7】:

      斯威夫特 3:

      internal func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
              for annotationView in views {
                  if annotationView.annotation is MKUserLocation {
                      annotationView.bringSubview(toFront: view)
                      return
                  }
                  annotationView.sendSubview(toBack: view)
              }
       }
      

      【讨论】:

        【解决方案8】:

        这是一种使用谓词的方法。我认为它应该更快

        NSPredicate *userLocationPredicate = [NSPredicate predicateWithFormat:@"class == %@", [MKUserLocation class]];
        NSArray* userLocation = [[self.mapView annotations] filteredArrayUsingPredicate:userLocationPredicate];
        
        if([userLocation count]) {
            NSLog(@"Bring blue location dot to front");
            MKAnnotationView *view = [self.mapView viewForAnnotation:(MKUserLocation *)[userLocation objectAtIndex:0]];
            [[view superview] bringSubviewToFront:view];
        }
        

        【讨论】:

          【解决方案9】:

          使用下划线.m

          _.array(mapView.annotations).
              filter(^ BOOL (id<MKAnnotation> annotation) { return [annotation 
          isKindOfClass:[MKUserLocation class]]; })
              .each(^ (id<MKAnnotation> annotation) { [[[mapView 
          viewForAnnotation:annotation] superview] bringSubviewToFront:[mapView 
          viewForAnnotation:annotation]]; });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-03-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多