Here is my code. I want to add my own custom callout view instead of iOS default. I know there is only left callout and right callout view, but I need to add a view tooltip
I have created a library to show custom callouts.
DXCustomCallout-ObjC
You can pass any custom view for the callout! It supports events for UIControls as well.
You can use this library. It also provides a ready-to-use template for the callout view. Adds cool animations and an anchor view for your custom views.
https://github.com/okhanokbay/MapViewPlus
I just created a custom callout for my map view which avoids issues with the callout disappearing when tapped. Here is a Gist with the steps I took.
Here is Best Example of CallOut View
http://www.cocoacontrols.com/platforms/ios/controls/gikanimatedcallout
http://www.cocoacontrols.com/platforms/ios/controls/multirowcalloutannotationview
To the best answer above in Swift
This can save some minutes to rewrite itself.
!NOTE:
There is one caveat which however can be easily fixed, your custom view will ignore touch events, because of mapView working with touches differently. Here's a quick fix:
1) Subclass MKAnnotationView (or MKPinAnnotationView depends on what you need)
2) in your mapView delegate
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView?
use your subclass instead of MKAnnotationView/MKPinAnnotationView
3) in your subclass .m file override two methods as following:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, withEvent: event)
if (hitView != nil)
{
self.superview?.bringSubviewToFront(self)
}
return hitView;
}
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)
break;
}
}
return isInside
}
I had same problem and ended up doing following thing:
When I receive mapView delegate callback
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
(it's time when I want to show my custom CalloutView)
I use view received as parameter *(MKAnnotationView )view (which is a pin view) and simply add my custom view to that pin view using
[view addSubview:customView];
It will add your custom view on top of that pin view so if we want it to be above pin we have to change custom view center property like this:
CGRect customViewRect = customView.frame;
CGRect rect = CGRectMake(-customViewRect.size.width/2, -customViewRect.size.height-7, customViewRect.size.width, customViewRect.size.height);
customView.frame = rect;
[view addSubview:customView];
In my case it looks like this
!NOTE:
There is one caveat which however can be easily fixed, your custom view will ignore touch events, because of mapView working with touches differently. Here's a quick fix:
1) Subclass MKAnnotationView (or MKPinAnnotationView depends on what you need)
2) in your mapView delegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
use your subclass instead of MKAnnotationView/MKPinAnnotationView
3) in your subclass .m file override two methods as following:
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hitView = [super hitTest:point withEvent:event];
if (hitView != nil)
{
[self.superview bringSubviewToFront:self];
}
return hitView;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect rect = self.bounds;
BOOL isInside = CGRectContainsPoint(rect, point);
if(!isInside)
{
for (UIView *view in self.subviews)
{
isInside = CGRectContainsPoint(view.frame, point);
if(isInside)
break;
}
}
return isInside;
}
All done!