2017-10-02 7 views
12

Sur iOS11, le zPosition a cessé de fonctionner pour le fichier annotationView. Chaque fois que la région de la carte change.MapKit (MKMapView): zPosition ne fonctionne plus sur iOS11

enter image description here

  • Pas de chance avec une solution originale: layer.zPosition = X;
  • Pas de chance avec bringViewToFront/SendViewToBack méthodes

Xcode 8.3/9

MISE A JOUR (SOLUTION merci Elias Aalto):

Lors de la création 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]; 

} 

En MeLikeSingleton ou quoi que objet d'observateur que vous avez là:

- (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); 
      } 

     } 
} 
  • Cette solution utilise la valeur layer.name de garder une trace de Zorder. Si vous avez plusieurs niveaux de zPosition (emplacement de l'utilisateur, cluster, broche, légende);)
  • Non pour les boucles, uniquement KVO
  • J'ai utilisé un objet Singleton qui observe les changements de valeur de la couche. Dans le cas où vous avez plusieurs MKMapViews utilisés à travers l'application.

COMMENT IL EST AVANT LE TRAVAIL IOS11

..est d'utiliser le

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

et réglez ici les zPosition.

..mais que (pour certains d'entre nous, encore dunny pourquoi) ne fonctionne plus dans iOS11!

+2

Avez-vous trouvé une solution? Je fais face à la même chose. – d3020

+0

oui, voir le message original – Jaro

+0

Pour tout le monde dans le même bateau: zPosition est apparemment cassé dans iOS 11. S'il vous plaît prendre votre temps pour déposer un rapport de bogue: https://developer.apple.com/bug-reporting/ Plus d'entre nous fichier, plus d'attention ce bug va obtenir à Apple. – Stack

Répondre

6

Le zPosition fait de travail, c'est juste que MKMapView l'écrase en interne basé sur le MKFeatureDisplayPriority quelque peu cassé et inutile. Si vous avez juste besoin d'une poignée d'annotations pour persister au-dessus de "tout le reste", vous pouvez le faire correctement en utilisant KVO. Il suffit d'ajouter un observateur à la zPosition de la couche d'affichage de l'annotation et de l'écraser lorsque MKMapView essaie de la manipuler.

(S'il vous plaît excuser mon ObjC)

Ajouter l'observateur:

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

overrule 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; 
    } 
} 

Bénéfice

+0

Cela a fonctionné beaucoup mieux pour moi que d'utiliser le plutôt étrange '[self.annotationView setDisplayPriority: MKFeatureDisplayPriorityDefaultLow/High]' car cette approche cache réellement les vues de priorité inférieure qui apparaissent sous une vue haute priorité, plutôt que de simplement réorganiser leurs index z - bien si c'est un élément de couleur solide, mais pas bon si c'est semi transparent. Merci! – Breeno

4

On peut complètement ignorer les tentatives de MKMapView pour modifier MKAnnotationView couche zPosition .Puisque MKAnnotationView utilise la norme CALayer comme couche et non comme une classe privée, nous pouvons la sous-classer et la remplacer par zPosition. Pour réellement définir zPosition nous pouvons fournir notre propre accesseur.

Cela fonctionnera beaucoup plus vite que 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 
    } 
} 

MISE À JOUR:

J'ai une méthode très inélégante pour la sélection de la vue d'annotation lorsque vous tapez sur le plus élevé des annotations qui se chevauchent.

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) 
    } 
} 
+0

Avez-vous testé? Ne semble pas fonctionner pour moi – onmyway133

+0

Oui, c'est en production. Êtes-vous certain que vous renvoyez la sous-classe - pas 'MKAnnotationView' - from mapView (MKMapView, viewFor: MKAnnotation)'? – bteapot

+0

Oui, j'ai retourné la sous-classe correcte. J'ai mis une instruction 'print' à l'intérieur du' set' vide dans 'ResistantLayer' et il a été appelé – onmyway133