Как определить порядок перекрытия MKAnnotationViews?

StackOverflow https://stackoverflow.com/questions/1145238

  •  16-09-2019
  •  | 
  •  

Вопрос

У меня на карте есть несколько MKAnnotations (и соответствующих им представлений), и иногда там бывает очень тесно.Теперь аннотации в моем приложении бывают двух видов:некоторые обязаны остаться там, где они есть, в то время как другие со временем уйдут.Я бы предпочел, чтобы более стабильные объекты визуально находились на заднем плане, а движущиеся всегда проходили перед ними.

Можно было бы подумать, что аннотации, добавленные к карте последними, окажутся на переднем плане (или, по крайней мере, в самом конце), но это не похоже на правило.Насколько я могу судить, я сначала создаю и добавляю ВСЕ неподвижные аннотации, а затем добавляю несколько новых движущихся аннотаций, но многие из них (хотя и не все!) в конечном итоге оказываются под постоянно неподвижными аннотациями.

Интересно, что когда время идет, а новые движущиеся аннотации создаются, они имеют тенденцию тяготеть больше к верху, чем первые — даже если все движущиеся объекты аннотаций были созданы только после того, как на карту уже были добавлены неподвижные части.

Кто-нибудь знает способ изменить этот странный естественный порядок отображения аннотаций на карте?Я пытался выполнить поиск по API Map Kit, но, похоже, там об этом не говорится.

Это было полезно?

Решение

Хорошо, поэтому для решения используйте метод из MKMapViewDelegate.


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

В этом методе вам следует изменить порядок AnnotationView после того, как он был добавлен в представление MapKit.Итак, код может выглядеть так:


- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
   for (MKAnnotationView * annView in views) {
      TopBottomAnnotation * ann = (TopBottomAnnotation *) [annView annotation];
      if ([ann top]) {
         [[annView superview] bringSubviewToFront:annView];
      } else {
         [[annView superview] sendSubviewToBack:annView];
      }
   }

}

Это работает для меня.

Другие советы

В iOS 11 реализация displayPriority сломал все решения, которые используют bringSubviewToFront или zPosition.

Если вы переопределите CALayer представления аннотаций, вы сможете вернуть контроль над zPosition из ОС.

class AnnotationView: MKAnnotationView {

    /// Override the layer factory for this class to return a custom CALayer class
    override class var layerClass: AnyClass {
        return ZPositionableLayer.self
    }

    /// convenience accessor for setting zPosition
    var stickyZPosition: CGFloat {
        get {
            return (self.layer as! ZPositionableLayer).stickyZPosition
        }
        set {
            (self.layer as! ZPositionableLayer).stickyZPosition = newValue
        }
    }

    /// force the pin to the front of the z-ordering in the map view
   func bringViewToFront() {
        superview?.bringSubview(toFront: self)
        stickyZPosition = CGFloat(1)
    }

    /// force the pin to the back of the z-ordering in the map view
   func setViewToDefaultZOrder() {
        stickyZPosition = CGFloat(0)
    }

}

/// iOS 11 automagically manages the CALayer zPosition, which breaks manual z-ordering.
/// This subclass just throws away any values which the OS sets for zPosition, and provides
/// a specialized accessor for setting the zPosition
private class ZPositionableLayer: CALayer {

    /// no-op accessor for setting the zPosition
    override var zPosition: CGFloat {
        get {
            return super.zPosition
        }
        set {
            // do nothing
        }
    }

    /// specialized accessor for setting the zPosition
    var stickyZPosition: CGFloat {
        get {
            return super.zPosition
        }
        set {
            super.zPosition = newValue
        }
    }
}

Попробуйте настроить zPosition слоя представления аннотаций (annotationView.layer.zPosition) в:

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

Свифт 3:

Я получаю расположение контактов от API, и у меня были похожие проблемы: контакты, которые должны были быть сверху, не были.Я смог решить эту проблему следующим образом.

var count = 0 // just so we don't get the same index in bottom pins
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {  
    for view in views {
        view.layer.zPosition = CGFloat(count)
    }
    count += 1
    if count > 500 {
        count = 250 // just so we don't end up with 999999999999+ as a value for count, plus I have at least 30 pins that show at the same time and need to have lower Z-Index values than the top pins. 
    }

}

Надеюсь это поможет

Я обнаружил, что такое изменение порядка представлений аннотаций приводит к тому, что выноска, всплывающая при нажатии на нее, больше не находится поверх всех аннотаций.Я даже пытался усовершенствовать его так, чтобы вместо bringSubviewToFront и sendSubviewToBack, Я использую insertSubview:aboveSubview и insertSubview:belowSubview: где второй аргумент — это первый annotationView в списке.Казалось бы, это вызывает гораздо меньшее рассеяние вперед-назад, но выноски по-прежнему появляются под некоторыми аннотациями.

В функции делегирования вы можете выбрать пин, чтобы он оказался сверху:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?` {
    ...
    if my annotation is the special one {
        annotationView.isSelected = true
    }
    ...
}

Мне действительно нужно было это сделать, и ни один из (текущих) ответов, похоже, не обеспечил надежную реализацию.Они вроде как работали, но панорамирование карты, выбор аннотаций или увеличение масштаба могли снова испортить порядок.

Окончательное, хорошо продуманное решение оказалось не таким уж тривиальным, поэтому я просто опишу шаги, которые я здесь предпринял.Аннотация, предписывающая, что MKMapView использует не учитывает добавленный порядок или даже порядок переопределения annotations свойство.Так...


Шаги

• Создать CADisplayLink
• В каждом кадре меняйте порядок аннотаций, используя оба слой zPosition и порядок представления в суперпредставлении subviews множество.
• Если выбран вид, переместите его на передний план в схеме заказа.
• При нажатии на аннотации по-прежнему учитываются внутренние MKMapView заказываю, несмотря на уже внесенные изменения.Чтобы противостоять этому, добавьте MKMapViewDelegate
• В объекте делегата mapView:didSelect: метод, проверьте, соответствует ли выбранная аннотация тому, что вы хотите
• Вы можете определить правильную/приоритетную аннотацию, самостоятельно проведя проверку на попадание аннотаций с учетом вашего собственного порядка.
• Если выбранная аннотация верна, отлично.Если нет, вручную выберите правильную аннотацию, используя selectAnnotation:animated:


И вот оно.Вышеупомянутый метод, кажется, работает хорошо, и снижение производительности от запуска каждого кадра не так уж и плохо.Вы также можете рассмотреть возможность перехода на MapBox, который, как мне кажется, поддерживает упорядочивание аннотаций, но по разным причинам это не всегда возможно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top