Hide title and subtitle fields in MKAnnotationView
By default all you can do is add a detail view and left and right accessory views to the callout. I found a post online that goes into detail about creating a completely custom callout accessory view. It tells the system not to display a callout view, and then adds the custom callout view as a subview of the annotation view.
See this link: How To Completely Customise Your Map Annotations Callout Views
Custom font for MKAnnotationView Callout
If all you need is a custom font, you need to subclass MKAnnotationView
, but you don't have to recreate all the behavior that you get for free with a standard MKAnnotationView
. It's actually pretty easy.
- Subclass
MKAnnotationView
- Override -layoutSubviews
- When an
MKAnnotationView
is selected, the callout is added as a subview. Therefore, we can recursively loop through our subclass' subviews and find theUILabel
we wish to modify. - That's it!
The only drawback with this method is that you can see the callout adjust it's size if your font is smaller or larger than the standard system font it was expecting. It'd be great if all the adjustments were made before being presented to the user.
// elsewhere, in a category on UIView.
// thanks to this answer: http://stackoverflow.com/a/25877372/607876
//
typedef void(^ViewBlock)(UIView *view, BOOL *stop);
@interface UIView (Helpers)
- (void)loopViewHierarchy:(ViewBlock)block;
@end
@implementation UIView (Helpers)
- (void)loopViewHierarchy:(ViewBlock)block {
BOOL stop = false;
if (block) {
block(self, &stop);
}
if (!stop) {
for (UIView* subview in self.subviews) {
[subview loopViewHierarchy:block];
}
}
}
@end
// then, in your MKAnnotationView subclass
//
@implementation CustomFontAnnotationView
- (void)layoutSubviews
{
// MKAnnotationViews only have subviews if they've been selected.
// short-circuit if there's nothing to loop over
if (!self.selected) {
return;
}
[self loopViewHierarchy:^(UIView *view, BOOL *stop) {
if ([view isKindOfClass:[UILabel class]]) {
*stop = true;
((UILabel *)view).font = {custom_font_name};
}
}];
}
@end
I inspected the view tree by first creating my MKAnnotationView
subclass and setting a breakpoint in my overridden -layoutSubviews
. In the debugger, I then issued po [self recursiveDescription]
. Make sure to turn the breakpoint off when your map first loads, because as mentioned up above, MKAnnotationView
s don't have any subviews until their selected. Before you make a selection, enable the breakpoint, tap your pin, break, and print out the view tree. You'll see a UILabel
at the very bottom of the tree.
How can I display a MKPinAnnotationView data (e.g. title, subtitle) in a fixed-size component below the map instead of the popup above the pin?
You could try to add a UITapGestureRecognizer to your MKMapView. Then for the action, create a function that will display the View at the bottom.
GestureRecognizer added in viewDidLoad():
let showTap : UIGestureRecognizer = UITapGestureRecognizer(target: self, action: "tapShowView:")
myMapView.addGestureRecognizer(showTap)
Then your function could be something like this.
func tapShowView(sender:UIGestureRecognizer) {
//Animate UIView in......
//Or just add your UIView.
let viewHeight = CGFloat(80)
let bottomView = UIView(frame: CGRect(x: 0, y: self.view.frame.height - viewHeight, width: self.view.frame.width, height: viewHeight))
bottomView.backgroundColor = .whiteColor()
//add buttons, navbar etc....
self.myMapView.addSubview(bottomView)
//OR if you add it in viewDidload and set to bottomView.hidden = true
bottomView.hidden = false
}
please ignore any syntax errors, I am not sitting at comp with Xcode on it. But this should give you a push. If you have any questions ill be back on when I wake up in a few hours(1:31 am here).. happy coding.
EDIT:
I think I miss read your question.....If you want to show the "BottomView" when user touches an annotation. You can call the "tapShowView" method from the MapViewDelegate method.
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
if view.annotation!.isKindOfClass(FBAnnotationCluster) {
print("cluster selected")
} else {
print("single pin selected")
//Call method here to display BottomView.
self.tapShowView() //Edit tapShowView to remove sender
}
}
Then to prevent any callout showing when you touch an annotation in this delegate method..
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
ADD:
annotationView?.canShowCallout = true
EDIT 2:
If you need to toggle the view when the user scrolls map etc. You can do that in the region did change delegate method.
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool){
//Just toggle the hidden property of the view.
BottomView.hidden = true
}
I would suggest you have a good look at the MKMapView delegate functions. There is a whole bunch of cool stuff you can do with them. Anytime you add something like a UITableView/UICollectionView/MKMapView etc... Take a few minutes to scroll through the delegate functions. Its really amazing how powerful some of the delegate functions are. If you need anything else, Ill be around the rest of the day.
How to create Custom MKAnnotationView and custom annotation title and subtitle
To create a custom annotation view (your replacement for the standard pin), you can just set the image
property of the MKAnnotationView
in the viewForAnnotation
method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
else if ([annotation isKindOfClass:[YourAnnotationClassHere class]]) // use whatever annotation class you used when creating the annotation
{
static NSString * const identifier = @"MyCustomAnnotation";
MKAnnotationView* annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView)
{
annotationView.annotation = annotation;
}
else
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:identifier];
}
annotationView.canShowCallout = NO; // set to YES if using customized rendition of standard callout; set to NO if creating your own callout from scratch
annotationView.image = [UIImage imageNamed:@"your-image-here.png"];
return annotationView;
}
return nil;
}
You might also want to adjust the centerOffset
property to get the pin to line up precisely the way you want.
Regarding the customization of the callout, the easiest approach is to specify leftCalloutAccessoryView
, rightCalloutAccessoryView
and/or detailCalloutAccessoryView
. This gives you a surprising degree of control, adding all sorts of images, labels, etc.
If you want to do a radical redesign of the callout, you can have viewForAnnotation
set canShowCallout
to NO
and then respond to setSelected
in your custom annotation view to show your own callout. While in Swift, see Customize MKAnnotation Callout View? for a few options for customizing the callouts.
Related Topics
Zposition of Sknode Relative to Its Parent
How to Skip Iterations of a For-In Loop (Swift 3)
Swift - Exit Outer Function from Closure
Modifying an Array Passed as an Argument to a Function in Swift
Swift - Associated Types in Protocol with Where Clause
Showing Notification Banner on MAC with Swift
How to Cast [Int8] to [Uint8] in Swift
Non-Modular Headers of Openssl Library When Using Modulemap for Swift Framework
C-Style Uninitialized Pointer Passing in Apple Swift
Differences Between Filtering Realm with Nspredicate and Block
Swiftui Share Sheet Crashes iPad
Swiftui - How to Get Coordinate/Position of Clicked Button
How to Apply a Grace Time Using Rx
Why Can't I Use Self in a Func Swift
Swift- How to Display Image Over Button
Applewatch Messages Url Works Hard Coded But Not with Variables