Почему calloutAccessoryControlTapped также вызывается для просмотра аннотации?

У меня есть карта на моем контроллере представления, и я не знаю почему, но делегат calloutAccessoryControlTapped также вызывается, когда я просто нажимаю на представление аннотации, а не только когда я нажимаю на закрытие деталей. Так почему это поведение?

import UIKit
import MapKit

extension MapVC: MKMapViewDelegate, CLLocationManagerDelegate
{    
    func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
    {
        ...
    }

}

1 ответ

Massimo Polimeni, кажется, прав. Похоже, проблема связана с rightCalloutAccessoryView, но не с leftCalloutAccessoryView.

Приведенный ниже код (с leftCalloutAccessoryView) работает должным образом. Если вы коснетесь левого аксессуара, он напечатает "левый аксессуар выбран". Если вы коснетесь заголовка выноски, он ничего не напечатает.

Если вы используете rightCalloutAccessoryView (закомментировано ниже) и коснетесь нужного аксессуара, он напечатает "выбран правильный аксессуар". Если вы нажмете на заголовок выноски, он также напечатает "выбран правильный аксессуар". Это не кажется правильным.

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate {

    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let annotation = MKPointAnnotation()
        annotation.coordinate = CLLocationCoordinate2D(latitude: 50.29, longitude: -107.79)
        annotation.title = "Swift Current"

        mapView.addAnnotation(annotation)
        mapView.mapType = .standard
        mapView.delegate = self
        mapView.region = MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: annotation.coordinate.latitude,
                                           longitude: annotation.coordinate.longitude),
            span: MKCoordinateSpan(latitudeDelta: 1.0, longitudeDelta: 1.0)
        )
    }

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let reuseId = "Annotation"
        var view = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
        if view == nil {
            view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
            view?.canShowCallout = true
            view?.leftCalloutAccessoryView = UIButton(type: .detailDisclosure)
            //view?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
        } else {
            view?.annotation = annotation
        }
        return view
    }

    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
        if control == view.leftCalloutAccessoryView {
            print("left accessory selected")
        } else if control == view.rightCalloutAccessoryView {
            print("right accessory selected")
        }
    }
}

Согласно документам Apple для разработчиков:

Вспомогательные виды содержат пользовательский контент и расположены по обе стороны от текста заголовка аннотации. Если указанное вами представление является потомком класса UIControl, представление карты вызывает этот метод для удобства всякий раз, когда пользователь касается вашего представления. Вы можете использовать этот метод, чтобы реагировать на нажатия и выполнять любые действия, связанные с этим элементом управления. Например, если ваш элемент управления отображает дополнительную информацию о аннотации, вы можете использовать этот метод для представления модальной панели с этой информацией.

Если ваши пользовательские дополнительные представления не являются потомками класса UIControl, представление карты не вызывает этот метод.

Так ваш вспомогательный вид унаследован от UIControl?

Ссылка: https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapViewDelegate_Protocol/:

Это зависит от того, какую кнопку вы установите на rightCalloutAccessoryView. Например, если вы используете:

[UIButton buttonWithType:UIButtonTypeDetailDisclosure];

Затем оба нажатия на выноску и нажатие кнопки приводят к calloutAccessoryControlTappedвызывается кнопкой. Но если вы используете:

[UIButton systemButtonWithImage:[UIImage systemImageNamed:@"info.circle"] target:nil action:nil];

Тогда будет работать только нажатие кнопки, нажатие на выноску будет отключено.

Если у вас есть настраиваемая кнопка и вы хотите первое поведение, вы можете создать подкласс кнопки и сделать это:

@interface UIButton2 : UIButton

@end

@implementation UIButton2

- (id)_mapkit_accessoryControlToExtendWithCallout{
    return self;
}

@end

Этот частный метод определяет, должна ли кнопка также работать для нажатия выноски (узнал об этом с помощью Hopper). Проверка реализации по умолчаниюself.buttonType принимать решение.

Более разумным способом было бы начать с кнопки раскрытия и изменить ее изображение, например

UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
UIImage *image = [UIImage systemImageNamed:@"chevron.right" withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleSmall];
[button setImage:image forState:UIControlStateNormal];

Большая часть этого странного поведения, вероятно, связана с изменениями в работе кнопок выноски, начиная с iOS 6.

Другие вопросы по тегам