Why calloutAccessoryControlTapped is called for tap on annotation view also?
Asked Answered
M

3

3

I have a map on my view controller and I don't know why but the delegate calloutAccessoryControlTapped is also called when I just tap on annotation view, not only when I tap on detail closure. So why this behavior?

import UIKit
import MapKit

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

}
Maduro answered 5/4, 2016 at 17:7 Comment(0)
S
1

As per Apple developer docs:

Accessory views contain custom content and are positioned on either side of the annotation title text. If a view you specify is a descendant of the UIControl class, the map view calls this method as a convenience whenever the user taps your view. You can use this method to respond to taps and perform any actions associated with that control. For example, if your control displayed additional information about the annotation, you could use this method to present a modal panel with that information.

If your custom accessory views are not descendants of the UIControl class, the map view does not call this method.

So is your accessory view inherited from UIControl?

Reference: https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapViewDelegate_Protocol/#//apple_ref/occ/intfm/MKMapViewDelegate/mapView:annotationView:calloutAccessoryControlTapped:

Sumerology answered 7/4, 2016 at 20:59 Comment(2)
Sorry but I can't see the point, the docs also says: Tells the delegate that the user tapped one of the ANNOTATION VIEW'S ACCESSORY BUTTONS. So I expected the delegate is called only for tap event on the buttons, not for the entire annotation view.Maduro
It says: "view you specify is a descendant of the UIControl class, the map view calls this method as a convenience whenever the user taps your view." So is your accessory view inherited from UIControl?Sumerology
R
1

Massimo Polimeni seems to be correct. There appears to be a problem with the rightCalloutAccessoryView, but not with the leftCalloutAccessoryView.

The code below (with a leftCalloutAccessoryView) works as expected. If you tap the left accessory, it prints "left accessory selected". If you tap the callout title, it doesn't print anything.

If you use a rightCalloutAccessoryView (commented out, below) and tap the right accessory, it prints "right accessory selected". If you tap the callout title, it also prints "right accessory selected". This does not seem correct.

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")
        }
    }
}
Romansh answered 26/7, 2019 at 19:46 Comment(0)
L
0

It depends what kind of button you set on the rightCalloutAccessoryView. E.g. if you use:

[UIButton buttonWithType:UIButtonTypeDetailDisclosure];

Then both tapping the callout and tapping the button both result in calloutAccessoryControlTapped being called with the button. But if you use:

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

Then only tapping the button will work, tapping the callout will be disabled.

If you have a custom button and you want the first behavior then you can make a button subclass and do this:

@interface UIButton2 : UIButton

@end

@implementation UIButton2

- (id)_mapkit_accessoryControlToExtendWithCallout{
    return self;
}

@end

This private method is how it decides wether a button should also work for tapping the callout (learned this using Hopper). The default implementation checks self.buttonType to decide.

A more sensible way would be to start with the disclosure button and change its image, e.g.

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

A lot of this weird behaviour is likely to do with changes in how callout buttons work since iOS 6.

Lemonade answered 13/6, 2020 at 16:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.