MapKit (MKMapView):zPosition 在 iOS11 上不再工作

Posted

技术标签:

【中文标题】MapKit (MKMapView):zPosition 在 iOS11 上不再工作【英文标题】:MapKit (MKMapView): zPosition does not work anymore on iOS11 【发布时间】:2018-03-13 02:58:25 【问题描述】:

ios11 上,zPosition 停止为 annotationView.layer 工作。每次地图区域变化时。

原始解决方案不走运:layer.zPosition = X; bringViewToFront/SendViewToBack 方法不走运

Xcode 8.3/9

更新(解决方案感谢 Elias Aalto):

创建MKAnnotationView时:

annotationView.layer.zPosition = 50;
if (IS_OS_11_OR_LATER) 
    annotationView.layer.name = @"50";
    [annotationView.layer addObserver:MeLikeSingleton forKeyPath:@"zPosition" options:0 context:NULL];


在 MeLikeSingleton 或您拥有的任何观察者对象中:

- (void)observeValueForKeyPath:(NSString *)keyPath
                         ofObject:(id)object
                           change:(NSDictionary *)change
                          context:(void *)context 

       if (IS_OS_11_OR_LATER) 

           if ([keyPath isEqualToString:@"zPosition"]) 
               CALayer *layer = object;
               int zPosition = FLT_MAX;
               if (layer.name) 
                   zPosition = layer.name.intValue;
               
               layer.zPosition = zPosition;
               //DDLogInfo(@"Name:%@",layer.name);
           

        

此解决方案使用 layer.name 值来跟踪 zOrder。如果您有多个级别的 zPosition(用户位置、集群、引脚、标注);) 没有 for 循环,只有 KVO 我使用了一个单例对象来观察层值的变化。如果您在整个应用中使用了多个 MKMapView。

在 IOS11 之前它是如何工作的

..是使用

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

并在此处设置 zPosition

..但是(对于我们中的一些人来说,仍然不知道为什么)在 iOS11 中不再起作用了!

【问题讨论】:

您找到解决方案了吗?我正在处理同样的事情。 是的,见原帖 对所有同舟共济的人:zPosition 在 iOS 11 中显然被破坏了。请花点时间提交错误报告:developer.apple.com/bug-reporting 我们中的更多人会提交,这个错误将在 Apple 获得更多关注. 你必须爱苹果,只是完全打破了每个主要版本的东西,就像它不是没有thang 【参考方案1】:

zPosition 确实 工作,只是 MKMapView 在内部根据有点损坏和无用的 MKFeatureDisplayPriority 覆盖它。如果您只需要在“其他所有内容”之上保留少量注释,则可以使用 KVO 干净地做到这一点semi。只需将观察者添加到注释视图层的 zPosition 并覆盖它,因为 MKMapView 试图摆弄它。

(请原谅我的 ObjC)

添加观察者:

        [self.annotationView.layer addObserver:self forKeyPath:@"zPosition" options:0 context:nil];

否决 MKMapView

 - (void)observeValueForKeyPath:(NSString *)keyPath
                  ofObject:(id)object
                    change:(NSDictionary *)change
                   context:(void *)context

    if(object == self.annotationView.layer)
    
        self.annotationView.layer.zPosition = FLT_MAX;
    

利润

【讨论】:

这对我来说比使用相当奇怪的[self.annotationView setDisplayPriority:MKFeatureDisplayPriorityDefaultLow/High] 效果要好得多,因为这种方法实际上隐藏了出现在高优先级视图下方的低优先级视图,而不仅仅是重新排序它们的 z-indexes - 如果它纯色元素,但如果它是半透明的,那就不好了。谢谢! 这个解决方案开始让我的应用程序崩溃。 crashes.to/s/0f3a2ba5fef【参考方案2】:

我们可以完全忽略MKMapView 修改MKAnnotationView 层的zPosition 的尝试。由于MKAnnotationView 使用标准CALayer 作为其层而不是某个私有类,因此我们可以对其进行子类化并覆盖其zPosition。要实际设置zPosition,我们可以提供自己的访问器。

它会比 KVO 快得多。

class ResistantLayer: CALayer 

    override var zPosition: CGFloat 
        get  return super.zPosition 
        set 
    
    var resistantZPosition: CGFloat 
        get  return super.zPosition 
        set  super.zPosition = newValue 
    


class ResistantAnnotationView: MKAnnotationView 

    override class var layerClass: AnyClass 
        return ResistantLayer.self
    
    var resistantLayer: ResistantLayer 
        return self.layer as! ResistantLayer
    

更新:

当点击重叠注释时,我有一种非常不雅的方法来选择最顶部的注释视图。

class MyMapView: MKMapView 

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 

        // annotation views whose bounds contains touch point, sorted by visibility order
        let views =
            self.annotations(in: self.visibleMapRect)
                .flatMap  $0 as? MKAnnotation 
                .flatMap  self.view(for: $0) 
                .filter  $0.bounds.contains(self.convert(point, to: $0)) 
                .sorted(by: 
                    view0, view1 in

                    let layer0  = view0.layer
                    let layer1  = view1.layer
                    let z0      = layer0.zPosition
                    let z1      = layer1.zPosition

                    if z0 == z1 
                        if  let subviews = view0.superview?.subviews,
                            let index0 = subviews.index(where:  $0 === view0 ),
                            let index1 = subviews.index(where:  $0 === view1 )
                        
                            return index0 > index1
                         else 
                            return false
                        
                     else 
                        return z0 > z1
                    
                )

        // disable every annotation view except topmost one
        for item in views.enumerated() 
            if item.offset > 0 
                item.element.isEnabled = false
            
        

        // re-enable annotation views after some time
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) 
            for item in views.enumerated() 
                if item.offset > 0 
                    item.element.isEnabled = true
                
            
        

        // ok, let the map view handle tap
        return super.hitTest(point, with: event)
    

【讨论】:

是的,它正在生产中。你确定你从mapView(MKMapView, viewFor: MKAnnotation)返回子类——而不是MKAnnotationView 是的,我返回了正确的子类。我在ResistantLayer 中的空set 中放了一个print 语句,它被称为 稍后——例如,在mapView(_:regionDidChangeAnimated:) 的某个地方——你检查zPositions 的mapView.annotations 这对集群注释不起作用,你会崩溃。 (如果您不需要集群注释,也可以工作!) @nolanw 为什么?我正在为集群注释返回 MKMarkerAnnotationView 的子类,在空的 zPosition.set 上命中并且没有发生崩溃。

以上是关于MapKit (MKMapView):zPosition 在 iOS11 上不再工作的主要内容,如果未能解决你的问题,请参考以下文章

IOS 11 - 当另一个引脚落后时,MKMapView引脚对话框无法点击

IOS-MapKit

MKMapView 不读取给定坐标

MKMapView 缩放和区域

iOS地图的显示(大头针)

:使用MapKit开发地图服务