objective-c - swift mapkit show user location




MapKit(MKMapView):zOS 현상이 iOS11에서 더 이상 작동하지 않습니다. (2)

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를 추적합니다. 많은 레벨의 z 위치 (사용자 위치, 클러스터, 핀, 콜 아웃)가있는 경우)
  • 루프는 없습니다. KVO 만 가능합니다.
  • 레이어 값 변경을 관찰하는 Singleton Object를 사용했습니다. 여러 개의 MKMapView가 앱을 통해 사용되는 경우에 사용합니다.

IOS11 이전에 어떻게 작동했는지

.. 사용하는

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

여기서 z 위치를 설정하십시오.

.. (iOS11에서는 더 이상 작동하지 않습니다.)


z 위치 작동합니다. MKMapView는 다소 부숴지고 쓸모없는 MKFeatureDisplayPriority를 ​​기반으로 내부적으로 덮어 씁니다. '기타 모든 것'위에 계속 유지하려면 주석을 약간만 써야하는 경우 KVO를 사용하여이 세미를 깨끗하게 처리 할 수 ​​있습니다. 주석 뷰의 레이어의 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;
    }
}

이익


MKAnnotationView 레이어의 zPosition 을 수정하려는 MKMapView 의 시도를 완전히 무시할 수 있습니다. MKAnnotationView 는 프라이빗 클래스가 아닌 표준 CALayer 를 레이어로 사용하기 때문에 하위 클래스로 zPosition 클래스를 재정의 할 수 있습니다. 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)
    }
}