简体   繁体   中英

Preventing annotation deselection in MKMapView

I have a situation in my app where I want to disable annotation deselection (other than when selecting another), so when I tap anywhere that is not an annotation view, it should leave the currently selected annotation as is. If I tap on another annotation view, it should select that one and deselect the other.

I was hoping to find something along the lines of a willDeselectAnnotationView in the MKMapViewDelegate or an isDeselected in MKAnnotationView , but there's unfortunately no such thing. I also tried overriding deselectAnnotation in a custom subclass of MKMapView , but it seems the tap triggered deselect doesn't invoke that function.

Is it possible to disable annotation deselection while preserving the ability to select? Thanks!

I've found a way to do it, Make a boolean named something like "allowSelectionChanges". which I just have as global for now: Then use a subclass of MKMapView with this overriden function inside:

override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
        return allowSelectionChanges
    } 

Switch this variable to false whenever you want to prevent annotation selection and deselection. It won't affect the user's ability to move around the map!


Here's an example of how this can be used to stop a callout from getting deselected when you tap on it to interact with it. Put this in your MKAnnotationView subclass:

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        let rect = self.bounds
        var isInside = CGRectContainsPoint(rect, point)
        if !isInside {
            for view in self.subviews {
                isInside = CGRectContainsPoint(view.frame, point)
                if isInside {
                    allowSelectionChanges = false
                    return true
                }
            }
            allowSelectionChanges = true
        }

        return false
    }

Ok... I had this issue myself and while @clinton's answer pointed me in the right direction I came up with a solution that doesn't require your MKAnnotationView subclass to know about the mapView 's custom property.

Here's my solution written for Swift 3:

public class <#CustomMapViewClass#>: MKMapView {

    private var allowSelectionChanges: Bool = true

    public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return allowSelectionChanges
    }

    public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let pointInside = super.point(inside: point, with: event)
        if !pointInside {
            return pointInside
        }

        for annotation in annotations(in: visibleMapRect) where annotation is <#CustomAnnotationViewClass#> {
            guard let view = self.view(for: annotation as! MKAnnotation) else {
                continue
            }
            if view.frame.contains(point) {
                allowSelectionChanges = true
                return true
            }
        }
        allowSelectionChanges = false

        return pointInside
    }

}

I'm basing my work off of @clinton and @mihai-fratu. Both of them gave very good answers so you should up-vote them as well. What I want to add is that if the tapped annotation is in a cluster, or if it is disabled, then you will still get the deselection happening. This is my code to attempt to fix that.

public class <#CustomMapViewClass#>: MKMapView {

    private var allowSelectionChanges: Bool = true

    public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return allowSelectionChanges
    }

    public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let pointInside = super.point(inside: point, with: event)
        if pointInside {
            // Go through all annotations in the visible map rect
            for annotation in annotations(in: visibleMapRect) where annotation is MKAnnotation {
                // get the view of each annotation
                if let view: MKAnnotationView = self.view(for: annotation as! MKAnnotation) {
                    // work with the cluster view if there is one
                    let rootView = view.cluster ?? view
                    // If the frame of this view contains the selected point, then we are an annotation tap. Allow the gesture...
                    if (rootView.frame.contains(point)) {
                        allowSelectionChanges = rootView.isEnabled  // But only if the view is enabled
                        return pointInside
                    }
                }
            }
            // If you did not tap in any valid annotation, disallow the gesture
            allowSelectionChanges = false
        }
        return pointInside
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM