How to change default background color of callout bubble with detailCalloutAccessoryView
I had created the code for your requirement please find the below url for download the code and review it.
Link : https://www.dropbox.com/s/o2howwqceq8rsgu/MapInformation.zip?dl=0
Environment : Xcode 8 and Swift3
Highlight the code which I had done it.
I had taken the approach to display the Popup(UIPresentationController
) instead of callout. For more information please find the below code.
A) I had used the UIButton
to display as annotation on the MapView and display the popup when user click on it.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let identifier = "pin"
var annotationView = self.mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as! AnnotationView?
if annotationView == nil {
annotationView = AnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = false
}
else {
annotationView?.annotation = annotation
}
//Take the UIButton and implement the touchupinside action for showing the popup.
let pinImage = UIImage.init(named: "customPin")
annotationView?.frame = CGRect(x: 0, y: 0, width: (pinImage?.size.width)!, height: (pinImage?.size.width)!)
annotationView?.mapPin = UIButton(frame: (annotationView?.frame)!);
annotationView?.mapPin.addTarget(self, action: #selector(ViewController.showPopup(sender:)), for: .touchUpInside)
annotationView?.addSubview((annotationView?.mapPin)!)
annotationView?.mapPin.setImage(pinImage, for: .normal)
return annotationView
}
B) Display the popup when user click on the annotation.
func showPopup(sender: UIButton!) {
let popupVC = self.storyboard?.instantiateViewController(withIdentifier: "Popup") as? Popup
popupVC?.preferredContentSize = CGSize(width: 250, height: 150)
popupVC?.modalPresentationStyle = UIModalPresentationStyle.popover
let rect = sender.superview?.convert(sender.frame, to: self.view)
popupVC?.popoverPresentationController?.delegate = self;
popupVC?.popoverPresentationController?.sourceView = self.view
popupVC?.popoverPresentationController?.sourceRect = rect!
popupVC?.popoverPresentationController?.backgroundColor = UIColor.red
self.present(popupVC!, animated: true, completion: nil)
}
Note
If you want to change the popup color from red to other different
color then you can do only single line of coding by changing the color name.popupVC?.popoverPresentationController?.backgroundColor = UIColor.red
Please look into the below screenshot.
Customise iOS8 Callout bubble (Swift)
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)
}
Custom MKPinAnnotation callout bubble similar to default callout bubble
I have developed a custom callout bubble that is nearly identical to the system callout bubble, but gives more flexibility over the height and content. It should be fairly trivial to adjust the appearance to suit your needs. See my post on the Asynchrony Solutions blog for example code and the steps required to implement a good callout replacement.
Related Topics
Create CSV File in Swift and Write to File
Swift 3/Macos: Open Window on Certain Screen
Alamofire 3 Custom Encoding to Alamofire 4 Custom Encoding
Swift Objc_Getassociatedobject Always Nil
How to Check If Airpods Are Connected to Iphone
How to Make JSON Data Persistent for Offline Use (Swift 4)
Subscript of a Struct Doesn't Set Values When Created as an Implicitly Unwrapped Optional
Workarounds for Generic Variable in Swift
Safari App Extension Crashes After a Few Seconds for Hello World Project
Updating Fetchedresultscontroller for Predicate Set by Uisearchbar
Swift Get Nsdata of a Video from Photos Library
Cloudkit Ckqueryoperation Doesn't Get All Records
Differenceand Purpose of Auto and Escaping Closure in Swift
Nstableview Inside Nspopover Looks Different as Standalone
Dynamic Uicollectionview Inside Dynamic Uitableviewcell
Drawing a Gradient Color in an Arc with a Rounded Edge
Not Getting Expected Delegate Calls When Trying to Restore In-App Purchases with Storekit
Swift Pattern Matching with Enum and Optional Tuple Associated Values