Центр 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 для выбранной аннотации.
- Затем я отменяю выбор, а затем снова выбираю аннотацию, чтобы фактически отображалась выноска
- для того, чтобы выноска была правильно отцентрирована над аннотацией, мне нужно отменить выделение / выбор с некоторой задержкой, чтобы центрирование карты могло завершиться
Я надеюсь, что мой ответ может оказаться полезным.
Вот другое решение:
Создать новое логическое свойство
var selectFirstAnnotation = false
в вашем контроллереУстановите в значение true, прежде чем центрировать аннотацию
Добавить это в
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)
}