[ios] Refined custom callout view support
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.
This commit is contained in:
committed by
Minh Nguyễn
parent
b70fd7ae69
commit
11d99bd890
@@ -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',
|
||||
|
||||
67
include/mbgl/ios/MGLCalloutView.h
Normal file
67
include/mbgl/ios/MGLCalloutView.h
Normal file
@@ -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
|
||||
@@ -1,35 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "MGLTypes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol MGLCalloutViewDelegate;
|
||||
|
||||
@protocol MGLCalloutViewProtocol <NSObject>
|
||||
|
||||
@property (nonatomic, copy) NSString *title, *subtitle;
|
||||
@property (nonatomic, strong) UIView *leftAccessoryView, *rightAccessoryView;
|
||||
@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;
|
||||
|
||||
- (void)dismissCalloutAnimated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@protocol MGLCalloutViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
// Controls whether the callout "highlights" when pressed. default YES. You must also respond to `-calloutViewClicked` below.
|
||||
- (BOOL)calloutViewShouldHighlight:(UIView<MGLCalloutViewProtocol> *)calloutView;
|
||||
|
||||
// Called when the callout view is clicked.
|
||||
- (void)calloutViewClicked:(UIView<MGLCalloutViewProtocol> *)calloutView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#import "MBXCustomCalloutView.h"
|
||||
|
||||
#import <mbgl/darwin/MGLAnnotation.h>
|
||||
|
||||
static CGFloat const tipHeight = 10.0;
|
||||
static CGFloat const tipWidth = 10.0;
|
||||
|
||||
@@ -9,7 +11,17 @@ static CGFloat const tipWidth = 10.0;
|
||||
|
||||
@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
|
||||
{
|
||||
@@ -32,8 +44,11 @@ static CGFloat const tipWidth = 10.0;
|
||||
{
|
||||
[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;
|
||||
|
||||
@@ -153,7 +153,7 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
|
||||
@"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];
|
||||
@@ -599,13 +599,13 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
|
||||
}];
|
||||
}
|
||||
|
||||
- (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;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#import "SMCalloutView.h"
|
||||
#import "MGLCalloutViewProtocol.h"
|
||||
|
||||
@interface MGLCalloutView : SMCalloutView <MGLCalloutViewProtocol>
|
||||
|
||||
@end
|
||||
@@ -1,5 +0,0 @@
|
||||
#import "MGLCalloutView.h"
|
||||
|
||||
@implementation MGLCalloutView
|
||||
|
||||
@end
|
||||
11
platform/ios/src/MGLCompactCalloutView.h
Normal file
11
platform/ios/src/MGLCompactCalloutView.h
Normal file
@@ -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
platform/ios/src/MGLCompactCalloutView.m
Normal file
31
platform/ios/src/MGLCompactCalloutView.m
Normal file
@@ -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
|
||||
@@ -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>
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
/// 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;
|
||||
@@ -1341,12 +1341,12 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
|
||||
}
|
||||
}
|
||||
|
||||
- (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
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)])
|
||||
{
|
||||
@@ -2514,9 +2514,9 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
|
||||
[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)
|
||||
{
|
||||
@@ -2576,13 +2576,10 @@ std::chrono::steady_clock::duration MGLDurationInSeconds(float duration)
|
||||
}
|
||||
}
|
||||
|
||||
- (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;
|
||||
|
||||
Reference in New Issue
Block a user