Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[ios] Refined custom callout view support
Browse files Browse the repository at this point in the history
Renamed MGLCalloutView to MGLCompactCalloutView and MGLCalloutViewProtocol to MGLCalloutView to avoid an awkward workaround for a Swift name collision. Replaced individual title and subtitle properties with a single representedObject property that lets you use custom annotation properties in the custom callout view. Overrode a problematic SMCalloutView method. Added lots more documentation.
  • Loading branch information
1ec5 committed Jan 7, 2016
1 parent b70fd7a commit 11d99bd
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 82 deletions.
6 changes: 3 additions & 3 deletions gyp/platform-ios.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@
'../include/mbgl/ios/MGLAnnotationImage.h',
'../platform/ios/src/MGLAnnotationImage_Private.h',
'../platform/ios/src/MGLAnnotationImage.m',
'../include/mbgl/ios/MGLCalloutViewProtocol.h',
'../platform/ios/src/MGLCalloutView.h',
'../platform/ios/src/MGLCalloutView.m',
'../include/mbgl/ios/MGLCalloutView.h',
'../platform/ios/src/MGLCompactCalloutView.h',
'../platform/ios/src/MGLCompactCalloutView.m',
'../platform/ios/src/MGLCategoryLoader.h',
'../platform/ios/src/MGLCategoryLoader.m',
'../platform/ios/src/NSBundle+MGLAdditions.h',
Expand Down
67 changes: 67 additions & 0 deletions include/mbgl/ios/MGLCalloutView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#import <Foundation/Foundation.h>

#import "MGLTypes.h"

NS_ASSUME_NONNULL_BEGIN

@protocol MGLCalloutViewDelegate;
@protocol MGLAnnotation;

/**
A protocol for a `UIView` subclass that displays information about a selected annotation near that annotation.
*/
@protocol MGLCalloutView <NSObject>

/**
An object conforming to the `MGLAnnotation` protocol whose details this callout view displays.
*/
@property (nonatomic, strong) id <MGLAnnotation> representedObject;

/**
A view that the user may tap to perform an action. This view is conventionally positioned on the left side of the callout view.
*/
@property (nonatomic, strong) UIView *leftAccessoryView;

/**
A view that the user may tap to perform an action. This view is conventionally positioned on the right side of the callout view.
*/
@property (nonatomic, strong) UIView *rightAccessoryView;

/**
An object conforming to the `MGLCalloutViewDelegate` method that receives messages related to the callout view’s interactive subviews.
*/
@property (nonatomic, weak) id<MGLCalloutViewDelegate> delegate;

/**
Presents a callout view by adding it to `inView` and pointing at the given rect of `inView`’s bounds. Constrains the callout to the bounds of the given view.
*/
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;

/**
Dismisses the callout view.
*/
- (void)dismissCalloutAnimated:(BOOL)animated;

@end

/**
The MGLCalloutViewDelegate protocol defines a set of optional methods that you can use to receive messages from an object that conforms to the MGLCalloutView protocol. The callout view uses these methods to inform the delegate that the user has interacted with the the callout view.
*/
@protocol MGLCalloutViewDelegate <NSObject>

@optional
/**
Returns a Boolean value indicating whether the entire callout view “highlights” when tapped. The default value is `YES`, which means the callout view highlights when tapped.
The return value of this method is ignored unless the delegate also responds to the `-calloutViewTapped` method.
*/
- (BOOL)calloutViewShouldHighlight:(UIView<MGLCalloutView> *)calloutView;

/**
Tells the delegate that the callout view has been tapped.
*/
- (void)calloutViewTapped:(UIView<MGLCalloutView> *)calloutView;

@end

NS_ASSUME_NONNULL_END
35 changes: 0 additions & 35 deletions include/mbgl/ios/MGLCalloutViewProtocol.h

This file was deleted.

10 changes: 5 additions & 5 deletions include/mbgl/ios/MGLMapView.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLMapViewDelegate;
@protocol MGLAnnotation;
@protocol MGLOverlay;
@protocol MGLCalloutViewProtocol;
@protocol MGLCalloutView;

/**
An interactive, customizable map view with an interface similar to the one
Expand Down Expand Up @@ -963,15 +963,15 @@ IB_DESIGNABLE
- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;

/**
Returns a custom callout view to display for the specified annotation.
Returns a callout view to display for the specified annotation.
If the method is present in the delegate, it must return a new instance of a view dedicated to display the callout bubble. It will be configured by the map view.
If this method is present in the delegate, it must return a new instance of a view dedicated to display the callout bubble. It will be configured by the map view. If this method is not present, or if it returns `nil`, a standard, two-line, bubble-like callout view is displayed by default.
@param mapView The map view that requested the callout view.
@param annotation The object representing the annotation.
@return A view following the MGLCalloutView protocol.
@return A view conforming to the `MGLCalloutView` protocol, or `nil` to use the default callout view.
*/
- (nullable UIView <MGLCalloutViewProtocol> *)mapView:(MGLMapView *)mapView customCalloutViewForAnnotation:(id <MGLAnnotation>)annotation;
- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation;

/**
Returns the view to display on the left side of the standard callout bubble.
Expand Down
2 changes: 1 addition & 1 deletion include/mbgl/ios/Mapbox.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#import "MGLAccountManager.h"
#import "MGLAnnotation.h"
#import "MGLAnnotationImage.h"
#import "MGLCalloutViewProtocol.h"
#import "MGLCalloutView.h"
#import "MGLGeometry.h"
#import "MGLMapCamera.h"
#import "MGLMapView.h"
Expand Down
12 changes: 3 additions & 9 deletions ios/app/MBXCustomCalloutView.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
#import <UIKit/UIKit.h>
#import <mbgl/ios/MGLCalloutViewProtocol.h>
#import <mbgl/ios/MGLCalloutView.h>

/*
/**
* Basic custom callout view to demonstrate how to
* add your own on your app. Will only show the
* callout title for demonstration purpose.
*/

@interface MBXCustomCalloutView : UIView <MGLCalloutViewProtocol>

@property (nonatomic, copy) NSString *title, *subtitle;
@property (nonatomic, strong) UIView *leftAccessoryView, *rightAccessoryView;
@property (nonatomic, weak) id<MGLCalloutViewDelegate> delegate;

@interface MBXCustomCalloutView : UIView <MGLCalloutView>
@end
21 changes: 18 additions & 3 deletions ios/app/MBXCustomCalloutView.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#import "MBXCustomCalloutView.h"

#import <mbgl/darwin/MGLAnnotation.h>

static CGFloat const tipHeight = 10.0;
static CGFloat const tipWidth = 10.0;

Expand All @@ -9,7 +11,17 @@ @interface MBXCustomCalloutView ()

@end

@implementation MBXCustomCalloutView
@implementation MBXCustomCalloutView {
id <MGLAnnotation> _representedObject;
UIView *_leftAccessoryView;
UIView *_rightAccessoryView;
__weak id <MGLCalloutViewDelegate> _delegate;
}

@synthesize representedObject = _representedObject;
@synthesize leftAccessoryView = _leftAccessoryView;
@synthesize rightAccessoryView = _rightAccessoryView;
@synthesize delegate = _delegate;

- (instancetype)initWithFrame:(CGRect)frame
{
Expand All @@ -32,8 +44,11 @@ - (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToV
{
[view addSubview:self];
// prepare title label
self.mainLabel.text = self.title;
[self.mainLabel sizeToFit];
if ([self.representedObject respondsToSelector:@selector(title)])
{
self.mainLabel.text = self.representedObject.title;
[self.mainLabel sizeToFit];
}
// prepare our frame
CGFloat frameWidth = self.mainLabel.bounds.size.width;
CGFloat frameHeight = self.mainLabel.bounds.size.height * 2.0;
Expand Down
6 changes: 3 additions & 3 deletions ios/app/MBXViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ - (void)showSettings
@"Add 10,000 Points",
@"Add Test Shapes",
@"Start World Tour",
@"Add 1 custom Point",
@"Add Custom Callout Point",
@"Remove Annotations",
@"Toggle Custom Style Layer",
nil];
Expand Down Expand Up @@ -599,13 +599,13 @@ - (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUse
}];
}

- (UIView<MGLCalloutViewProtocol> *)mapView:(__unused MGLMapView *)mapView customCalloutViewForAnnotation:(id<MGLAnnotation>)annotation
- (UIView<MGLCalloutView> *)mapView:(__unused MGLMapView *)mapView calloutViewForAnnotation:(id<MGLAnnotation>)annotation
{
if ([annotation respondsToSelector:@selector(title)]
&& [annotation.title isEqualToString:kCustomCalloutTitle])
{
MBXCustomCalloutView *calloutView = [[MBXCustomCalloutView alloc] init];
calloutView.title = annotation.title;
calloutView.representedObject = annotation;
return calloutView;
}
return nil;
Expand Down
6 changes: 0 additions & 6 deletions platform/ios/src/MGLCalloutView.h

This file was deleted.

5 changes: 0 additions & 5 deletions platform/ios/src/MGLCalloutView.m

This file was deleted.

11 changes: 11 additions & 0 deletions platform/ios/src/MGLCompactCalloutView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import "SMCalloutView.h"
#import "MGLCalloutView.h"

/**
A concrete implementation of `MGLCalloutView` based on [SMCalloutView](https://github.com/nfarina/calloutview). This callout view displays the represented annotation’s title, subtitle, and accessory views in a compact, two-line layout.
*/
@interface MGLCompactCalloutView : SMCalloutView <MGLCalloutView>

+ (instancetype)platformCalloutView;

@end
31 changes: 31 additions & 0 deletions platform/ios/src/MGLCompactCalloutView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#import "MGLCompactCalloutView.h"

#import "MGLAnnotation.h"

@implementation MGLCompactCalloutView
{
id <MGLAnnotation> _representedObject;
}

@synthesize representedObject = _representedObject;

+ (instancetype)platformCalloutView
{
return [[self alloc] init];
}

- (void)setRepresentedObject:(id <MGLAnnotation>)representedObject
{
_representedObject = representedObject;

if ([representedObject respondsToSelector:@selector(title)])
{
self.title = representedObject.title;
}
if ([representedObject respondsToSelector:@selector(subtitle)])
{
self.subtitle = representedObject.subtitle;
}
}

@end
21 changes: 9 additions & 12 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#import "MGLAccountManager_Private.h"
#import "MGLAnnotationImage_Private.h"
#import "MGLMapboxEvents.h"
#import "MGLCalloutView.h"
#import "MGLCompactCalloutView.h"

#import <algorithm>
#import <cstdlib>
Expand Down Expand Up @@ -138,7 +138,7 @@ @interface MGLMapView () <UIGestureRecognizerDelegate,
/// Mapping from reusable identifiers to annotation images.
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
/// Currently shown popover representing the selected annotation.
@property (nonatomic) UIView<MGLCalloutViewProtocol> *calloutViewForSelectedAnnotation;
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
@property (nonatomic) MGLUserLocationAnnotationView *userLocationAnnotationView;
@property (nonatomic) CLLocationManager *locationManager;
@property (nonatomic) CGPoint centerPoint;
Expand Down Expand Up @@ -1341,12 +1341,12 @@ - (void)handleCalloutAccessoryTapGesture:(UITapGestureRecognizer *)tap
}
}

- (BOOL)calloutViewShouldHighlight:(__unused MGLCalloutView *)calloutView
- (BOOL)calloutViewShouldHighlight:(__unused MGLCompactCalloutView *)calloutView
{
return [self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)];
}

- (void)calloutViewClicked:(__unused MGLCalloutView *)calloutView
- (void)calloutViewTapped:(__unused MGLCompactCalloutView *)calloutView

This comment has been minimized.

Copy link
@picciano

picciano Feb 9, 2016

This is a delegate method of SMCalloutView. It should be named calloutViewClicked. The map view no longer responds to the user clicking the callout view. (tapping the accessory views still work, but not the callout itself.)

{
if ([self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)])
{
Expand Down Expand Up @@ -2514,9 +2514,9 @@ - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
[self.delegate mapView:self annotationCanShowCallout:annotation])
{
// build the callout
if ([self.delegate respondsToSelector:@selector(mapView:customCalloutViewForAnnotation:)])
if ([self.delegate respondsToSelector:@selector(mapView:calloutViewForAnnotation:)])
{
self.calloutViewForSelectedAnnotation = [self.delegate mapView:self customCalloutViewForAnnotation:annotation];
self.calloutViewForSelectedAnnotation = [self.delegate mapView:self calloutViewForAnnotation:annotation];
}
if (!self.calloutViewForSelectedAnnotation)
{
Expand Down Expand Up @@ -2576,13 +2576,10 @@ - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
}
}

- (MGLCalloutView *)calloutViewForAnnotation:(id <MGLAnnotation>)annotation
- (MGLCompactCalloutView *)calloutViewForAnnotation:(id <MGLAnnotation>)annotation
{
MGLCalloutView *calloutView = (MGLCalloutView *)[MGLCalloutView platformCalloutView];

if ([annotation respondsToSelector:@selector(title)]) calloutView.title = annotation.title;
if ([annotation respondsToSelector:@selector(subtitle)]) calloutView.subtitle = annotation.subtitle;

MGLCompactCalloutView *calloutView = [MGLCompactCalloutView platformCalloutView];
calloutView.representedObject = annotation;
calloutView.tintColor = self.tintColor;

return calloutView;
Expand Down

0 comments on commit 11d99bd

Please sign in to comment.