Customise iOS8 Callout bubble (Swift)

杀马特。学长 韩版系。学妹 提交于 2019-11-28 04:35:27

calloutViewController is a part of custom callout view to handle events. You won't find it in MapKit or elsewhere.
Apples instructions are good. To create your own callout you should follow steps:

1. Create custom MKAnnotationView or MAPinAnnotationView
2. Override setSelected and hitTest methods in your annotation
3. Create your own callout view
4. Override hitTest and pointInside in you callout view
5. Implement MapView delegate methods didSelectAnnotationView, didDeselectAnnotationView

I have ended up with these solution that allows me to handle touches inside callout view without losing selection.

Annotation

class MapPin: MKAnnotationView {
    class var reuseIdentifier:String {
        return "mapPin"
    }

    private var calloutView:MapPinCallout?
    private var hitOutside:Bool = true

    var preventDeselection:Bool {
        return !hitOutside
    }


    convenience init(annotation:MKAnnotation!) {
        self.init(annotation: annotation, reuseIdentifier: MapPin.reuseIdentifier)

        canShowCallout = false;
    }

    override func setSelected(selected: Bool, animated: Bool) {
        let calloutViewAdded = calloutView?.superview != nil

        if (selected || !selected && hitOutside) {
            super.setSelected(selected, animated: animated)
        }

        self.superview?.bringSubviewToFront(self)

        if (calloutView == nil) {
            calloutView = MapPinCallout()
        }

        if (self.selected && !calloutViewAdded) {
            addSubview(calloutView!)
        }

        if (!self.selected) {
            calloutView?.removeFromSuperview()
        }
    }

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        var hitView = super.hitTest(point, withEvent: event)

        if let callout = calloutView {
            if (hitView == nil && self.selected) {
                hitView = callout.hitTest(point, withEvent: event)
            }
        }

        hitOutside = hitView == nil

        return hitView;
    }
}

Callout view

class MapPinCallout: UIView {
    override func hitTest(var point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        let viewPoint = superview?.convertPoint(point, toView: self) ?? point

        let isInsideView = pointInside(viewPoint, withEvent: event)

        var view = super.hitTest(viewPoint, withEvent: event)

        return view
    }

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        return CGRectContainsPoint(bounds, point)
    }
}

If you need something else but buttons be responsive in callout add code to handle touches in specific views before hitTest returns view

if calloutState == .Expanded && CGRectContainsPoint(tableView.frame, viewPoint) {
    view = tableView.hitTest(convertPoint(viewPoint, toView: tableView), withEvent: event)
}

Delegate methods

func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
    if let mapPin = view as? MapPin {
        updatePinPosition(mapPin)
    }
}

func mapView(mapView: MKMapView!, didDeselectAnnotationView view: MKAnnotationView!) {
    if let mapPin = view as? MapPin {
        if mapPin.preventDeselection {
            mapView.selectAnnotation(view.annotation, animated: false)
        }
    }
}

func updatePinPosition(pin:MapPin) {
    let defaultShift:CGFloat = 50
    let pinPosition = CGPointMake(pin.frame.midX, pin.frame.maxY)

    let y = pinPosition.y - defaultShift

    let controlPoint = CGPointMake(pinPosition.x, y)
    let controlPointCoordinate = mapView.convertPoint(controlPoint, toCoordinateFromView: mapView)

    mapView.setCenterCoordinate(controlPointCoordinate, animated: true)
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!