web-dev-qa-db-ja.com

MapKit(MKMapView):iOS11ではzPositionが機能しなくなりました

オン iOS11zPositionはannotationView.layerの動作を停止しました。マップ領域が変更されるたび。

enter image description here

  • 元のソリューションでは運がありません:layer.zPosition = X;
  • bringViewToFront/SendViewToBackメソッドでは運が悪い

Xcode 8.3/9

UPDATE(ソリューションは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ではもう機能しません!

18
Yaro

ZPosition doesは機能しますが、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;
    }
}

利益

11
Elias Aalto

MKMapViewMKAnnotationViewレイヤーの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
    }
}

更新:

重複する注釈をタップするときに最上位の注釈ビューを選択するための非常に洗練されていない方法が1つあります。

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)
    }
}
5
bteapot