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

Move callout customization away from MGLMapViewDelegate towards custom views #4392

Closed
1ec5 opened this issue Mar 18, 2016 · 9 comments
Closed
Labels
annotations Annotations on iOS and macOS or markers on Android archived Archived because of inactivity iOS Mapbox Maps SDK for iOS refactor
Milestone

Comments

@1ec5
Copy link
Contributor

1ec5 commented Mar 18, 2016

The current annotation callout API is implemented as a series of delegate methods in MGLMapViewDelegate that configure SMCalloutView. SMCalloutView provides a lot of customization options, but we’re only exposing a few basic ones.

SMCalloutView is great for callout views that need a certain layout, but many developers need more flexibility and don’t mind setting up the layout and drawing themselves. The current API for custom callout views can be very confusing for a developer who wants something more freeform – “Why do I have to implement accessory views?” And you’re basically on your own in terms of styling as soon as you go the custom callout route.

Let’s drop the dependency on SMCalloutView and deprecate the current callout API – both the delegate methods and the protocol – in favor of a more deliberate, coherent callout API. The proposal below is aimed at aligning the iOS SDK’s annotations API with that of the OS X SDK, which manages callouts with NSPopoverControllers and allows you to specify your own.


You should be able to drag a custom view into a storyboard, turn it into an MGLMapView, and design the custom callout views directly inside the storyboard. You’d drag a custom view into the scene dock above the view controller and optionally set the custom class to MGLCompactCalloutView, which provides the standard bubble look and feel (surfaced in the storyboard via a designable). You’d specify a reuse identifier for the callout view, then drag a connection from the callout view to the map view’s prototypeCalloutViews outlet collection. The callout view can be customized extensively without any work on our part:

  • To set the callout view’s background color, you’d use the Background Color control available in the Attributes inspector for any view. If you want to get fancy, you can insert a UIVisualEffectView for a vibrant blur effect.
  • You’d use the Size inspector to make the callout view taller, or even use Auto Layout constraints or a UIScrollView to ensure that the callout view fits its contents. The callout view’s size and contents could vary by size class.
  • If tapping on the right side of the callout view should take the user to another view controller, you’d drag a UIButton into the right side of the callout view and drag a segue from the button to the destination view controller. Optionally, you’d connect the button to the callout view’s rightAccessoryView outlet so that -[MGLMapViewDelegate mapView:annotation:calloutAccessoryControlTapped:] would get called.

When configuring an MGLAnnotationView (#1784) or MGLAnnotationImage programmatically, you’d choose which callout view to use with the annotation, identifying one of the prototype callout views either by indexing into the outlet collection or by specifying a reuse identifier. This part differs from how MapKit’s MKAnnotationView API works, but I think it’s worth diverging to offer better storyboard support and more flexibility. Notably, setting up the annotation model object is the only part of the workflow that absolutely requires code.


If possible, we’d provide a compatibility shim for the old API that could get removed in a future major version. But if you want to work with SMCalloutView specifically, then you’d be able to pull in SMCalloutView and customize it to your liking without depending on SDK changes.

The entire API would still be usable programmatically for more advanced use cases. But the goal is that the “First steps” guide, which is intended for beginners, would have only a few lines of code and the rest would be in Interface Builder. Some future directions, somewhat out of scope for this issue, would further reduce the need for code:

  • You could define an annotation view itself by dragging a UIImageView into the scene dock, connecting it to the map view’s prototypeAnnotationViews outlet collection, and optionally connecting a callout view to the annotation view’s calloutView outlet.
  • You could specify a path to a local GeoJSON file from which the annotations would be generated.

A lot of the code in this custom callout view example can be reused to implement a snazzy-looking default callout view.

/cc @friedbunny @boundsj @tmcw

@1ec5 1ec5 added iOS Mapbox Maps SDK for iOS refactor labels Mar 18, 2016
@1ec5 1ec5 added this to the ios-v3.3.0 milestone Mar 22, 2016
@1ec5
Copy link
Contributor Author

1ec5 commented Mar 29, 2016

Some addenda:

You’d drag a custom view into the scene dock above the view controller and optionally set the custom class to MGLCompactCalloutView… You’d specify a reuse identifier for the callout view

Providing a reuse identifier inspectable for ordinary UIViews would be tricky. We can’t add a stored property to UIView via a category. However, we could have the category-defined getter and setter work with an associated object on the view.

If the developer wants the standard bubble look but forgets to set MGLCompactCalloutView as the custom class, should the callout really be transparent and borderless?

You could define an annotation view itself by dragging a UIImageView into the scene dock, connecting it to the map view’s prototypeAnnotationViews outlet collection, and optionally connecting a callout view to the annotation view’s calloutView outlet.

There are two likely models here:

  • The map view has a prototypeAnnotationViews outlet collection, and each prototype annotation view has a calloutView outlet. This option is simpler and reminiscent of MKAnnotationView’s various callout-related properties, but it’s more difficult to mix and match annotation views and callout views.
  • The map view has prototypeAnnotationViews and prototypeCalloutViews outlet collections, and a prototype annotation view can designate a callout view by its reuse identifier. This option is more powerful, but the way to link an annotation view with a callout view is less discoverable.

What happens when we add support for showing callouts on shape and line overlays? It may make sense to have prototype callout views be independent of any prototype annotation view.

Where should the callout’s anchor position or anchor edge be exposed? As a property of the callout view or the annotation view?

@friedbunny
Copy link
Contributor

Working in the fb-callouts-4392 branch. So far, the only commits are the removal of SMCalloutView and the interim conversion of MGLCompactCalloutView to the demo custom callout.

I’m currently exploring how to deprecate the old delegate-based callouts without removing them outright.

@1ec5
Copy link
Contributor Author

1ec5 commented Jun 5, 2016

Noting also that the callout view’s title and subtitle labels should update (and the callout view itself should resize) when the represented annotation object’s title and subtitle properties change. It’s currently trivial to observe property changes using KVO, as we do with the coordinate property, but SMCalloutView lacks support for updating its contents dynamically.

It also doesn’t look straightforward to add dynamic content updates to SMCalloutView, because it doesn’t expose its labels publicly and lays itself out manually. SMCalloutView is written to support iOS 5.0 and above, while Auto Layout was introduced in iOS 6.0. We only support iOS 7.0 and above, so any replacement for SMCalloutView should use constraints to avoid a similar mess.

@incanus
Copy link
Contributor

incanus commented Jul 29, 2016

I'd love to discuss this more, especially if we are anticipating having MapKit parity with some sort of similar callout view as part of this more abstract solution. SMCalloutView is mature, we have a good relationship with the developer (👋 @nfarina), and it'd take a lot of work to replicate.

@1ec5
Copy link
Contributor Author

1ec5 commented Jul 30, 2016

SMCalloutView is a great library, but the current situation is unfortunate: we only make use of a tiny fraction of SMCalloutView, so there’s plenty of dead code around iOS 6 support and various customization options. Due to the way we’ve integrated SMCalloutView, developers can’t even bring in their own copy of SMCalloutView that they can use more fully (#1757).

Meanwhile, many developers are eschewing the built-in callout API for custom callout views, either because they want more flexibility with the bubble callout design (#3950) or because they want to imitate the bottom panel affordance found in Google Maps and (now in iOS 10) Apple Maps. We need the ability to dynamically update the callout’s title and subtitle through KVO, for parity with the Android SDK (#5353) and macOS SDK, but that’s currently impossible because of how SMCalloutView is laid out. Instead of hacking around that limitation, I think we should move to an implementation that uses Auto Layout.

The existing custom callout API requires a lot of code, because it’s trying to fit within the paradigm of SMCalloutView. I think we can greatly simplify the API in a way that allows developers to bring in SMCalloutView if that’s what they want but get a reasonable default that matches MapKit if they don’t bring in SMCalloutView. The implementation in #4948 already replicates much of the SMCalloutView functionality we’ve been using, but with far less code. I think we can also consider it fairly mature for what it does, given that it’s based on the custom callout example.

@friedbunny
Copy link
Contributor

My plan so far in #4948 is to minimally reproduce SMCalloutView’s features, but do so in a way that’s as efficient as possible for this project. Primarily, I want to reduce the number of delegate methods and more closely align to MapKit’s property-based callout↔annotation connections. Our current custom callout API is entirely too complicated and this effort is about improving that.

SMCalloutView itself is great and the changes we’re discussing here should actually make it easier for developers to more fully use it. For those developers who don’t need SMCalloutView, unbundling it from our SDK will make their projects marginally lighter weight.

(I do think, though, that iOS 10’s Maps.app move away from bubble-style callouts is the beginning of the end for SMCalloutView — the bubble is still a useful paradigm, but separate floating views will probably at least enjoy some faddish popularity.)

@boundsj boundsj added the annotations Annotations on iOS and macOS or markers on Android label Aug 15, 2016
@friedbunny friedbunny modified the milestones: ios-v3.4.0, ios-future Aug 19, 2016
@ghost
Copy link

ghost commented Oct 11, 2017

I think this is an excellent idea.

@stale
Copy link

stale bot commented Dec 3, 2018

This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.

@stale stale bot closed this as completed Dec 3, 2018
@julianrex julianrex reopened this Dec 3, 2018
@stale stale bot removed the archived Archived because of inactivity label Dec 3, 2018
@stale stale bot added the archived Archived because of inactivity label Jun 1, 2019
@stale
Copy link

stale bot commented Jun 2, 2019

This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.

@stale stale bot closed this as completed Jun 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
annotations Annotations on iOS and macOS or markers on Android archived Archived because of inactivity iOS Mapbox Maps SDK for iOS refactor
Projects
None yet
Development

No branches or pull requests

5 participants