Центр MKMapView ДО отображения выноски

Я пытаюсь центрировать MKMapView после того, как аннотация была выбрана. Я также включил canShowCallout но кажется, что iOS сначала отображает выноску (которая смещается, когда она не помещается на экране), а затем карта перемещается, в результате чего выноска не полностью видна на экране.

неправильная позиция выноски

Как я могу центрировать карту ДО того, как позиция выноски отображается и отображается?

3 ответа

Я хотел сделать то же самое и в итоге сделал следующее.

Предостережение перед тем, как я начну: я знаю, что решение довольно уродливо!... но эй, оно работает.

Примечание: я нацеливаюсь на iOS 9, но она должна работать на предыдущих версиях iOS:

Хорошо, здесь мы идем:

  • во-первых, создайте новое свойство в вашем контроллере представления, например: @property(nonatomic, assign, getter=isPinCenteringOngoing) BOOL pinCenteringOngoing;
  • в mapView:viewForAnnotation: задавать canShowCallout в NO для ваших аннотаций Views
  • в mapView:didSelectAnnotationView: сделайте следующее:

    - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
    {
        if([view isKindOfClass:$YOURANNOTATIONVIEWCLASS$.class])
        {
            if(!self.isPinCenteringOngoing)
            {
                self.pinCenteringOngoing = YES;
                [self centerMapOnSelectedAnnotationView:($YOURANNOTATIONVIEWCLASS$ *)view];
            }
            else
            {
                self.pinCenteringOngoing = NO;
            }
        }
    }
    
  • в mapView:didDeselectAnnotationView: сделайте следующее:

    - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
    {
        if([view isKindOfClass:$YOURANNOTATIONVIEWCLASS$.class])
        {
            if(!self.isPinCenteringOngoing)
            {
                view.canShowCallout = NO;
            }
        }
    }
    
  • и, наконец, создайте новый метод, который выполняет фактическую работу:

    - (void)centerMapOnSelectedAnnotationView:($YOURANNOTATIONVIEWCLASS$ *)view
    {
        // Center map
        CGPoint annotationCenter = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view.frame));
        CLLocationCoordinate2D newCenter = [self.mapView convertPoint:annotationCenter toCoordinateFromView:view.superview];
        [self.mapView setCenterCoordinate:newCenter animated:YES];
    
        // Allow callout to be shown
        view.canShowCallout = YES;
    
        // Deselect and then select the annotation so the callout is actually displayed
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void)
        {
            [self.mapView deselectAnnotation:view.annotation animated:NO];
    
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void)
            {
                [self.mapView selectAnnotation:view.annotation animated:NO];
            });
        });
    }
    

Чтобы завершить мой ответ, вот текстовое объяснение того, что я делаю в приведенном выше коде и почему я делаю это:

  • Я хочу, чтобы аннотация была центрирована на экране, а выноска была центрирована над ней.
  • Что я получаю по умолчанию:
    • При выборе аннотации карта открывает выноску и при необходимости корректирует карту так, чтобы выноска помещалась на экране. Эта стандартная реализация ни в коем случае не гарантирует, что выноска "центрирована" над аннотацией.
    • Центрируя карту с помощью setCenterCoordinate:, вид аннотации центрируется на карте.
    • Теперь две предыдущие точки вместе могут привести к тому, что сноска будет "обрезана", так как аннотация центрирована на карте, но сноска не центрирована над аннотацией.
  • Чтобы это исправить, я делаю следующее:
    • сначала я отключаю выноску, которая будет отображаться по умолчанию, устанавливая для canShowCallout значение NO для каждого annotationView
    • когда пользователь выбирает аннотацию, я сначала центрирую карту
    • Затем я позволяю отображать выноску, устанавливая для canShowCallout значение YES для выбранной аннотации.
    • Затем я отменяю выбор, а затем снова выбираю аннотацию, чтобы фактически отображалась выноска
    • для того, чтобы выноска была правильно отцентрирована над аннотацией, мне нужно отменить выделение / выбор с некоторой задержкой, чтобы центрирование карты могло завершиться

Я надеюсь, что мой ответ может оказаться полезным.

Вот другое решение:

  1. Создать новое логическое свойство var selectFirstAnnotation = false в вашем контроллере

  2. Установите в значение true, прежде чем центрировать аннотацию

  3. Добавить это в regionDidChangeAnimated,

    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
            if selectFirstAnnotation == true {
              if let annotation = mapView.annotations.first(where: { !($0 is MKUserLocation) }) {
                mapView.selectAnnotation(annotation, animated: true)
                selectFirstAnnotation = false
        }}}
    

Отлично работает для моего поведения

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

      UIView.animate(withDuration: 0.8) {
     self.mapView.setCenter(CLLocationCoordinate2D(latitude: newCenter.latitude, longitude: newCenter.longitude), animated: true)
}

Затем я получил неприемлемый сигнал от разделения вызовов отмены выбора и выбора на разные отправки с разным временем и обнаружил, что они оба могут идти в одной отправке. Добавление анимированного: true к вызову select также добавляет приятный штрих.

      DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
     mapView.deselectAnnotation(view.annotation, animated: false)
     mapView.selectAnnotation(view.annotation!, animated: true)
}
Другие вопросы по тегам