iOS Documentation: Animated Marker Example, Custom Annotation Example (#2539)

This commit is contained in:
Bart Louwers
2024-06-22 11:46:05 +02:00
committed by GitHub
parent 7af1b8d5ef
commit 7f1b76cdaf
25 changed files with 1015 additions and 66 deletions

View File

@@ -15,24 +15,24 @@ NS_ASSUME_NONNULL_BEGIN
connect them. For example, you could use an polyline to represent a road or the
path along which something moves.
You can add polyline shapes to the map by adding them to an `MLNShapeSource`
object. Configure the appearance of an `MLNShapeSource`’s or
`MLNVectorTileSource`’s polylines collectively using an `MLNLineStyleLayer` or
`MLNSymbolStyleLayer` object. To access a polyline’s attributes, use an
`MLNPolylineFeature` object.
You can add polyline shapes to the map by adding them to an ``MLNShapeSource``
object. Configure the appearance of an ``MLNShapeSource``’s or
``MLNVectorTileSource``’s polylines collectively using an ``MLNLineStyleLayer`` or
``MLNSymbolStyleLayer`` object. To access a polyline’s attributes, use an
``MLNPolylineFeature`` object.
Alternatively, you can add a polyline overlay directly to a map view using the
`-[MLNMapView addAnnotation:]` or `-[MLNMapView addOverlay:]` method. Configure
``MLNMapView/addAnnotation:`` or ``MLNMapView/addOverlay:`` methods. Configure
a polyline overlay’s appearance using
`-[MLNMapViewDelegate mapView:strokeColorForShapeAnnotation:]` and
`-[MLNMapViewDelegate mapView:lineWidthForPolylineAnnotation:]`.
``MLNMapViewDelegate/mapView:strokeColorForShapeAnnotation:`` and
``MLNMapViewDelegate/mapView:lineWidthForPolylineAnnotation:``.
The vertices are automatically connected in the order in which you provide
them. The first and last vertices are not connected to each other, but you can
specify the same `CLLocationCoordinate2D` as the first and last vertices in
order to close the polyline. To fill the space within the shape, use an
`MLNPolygon` object. To group multiple polylines together in one shape, use an
`MLNMultiPolyline` or `MLNShapeCollection` object.
``MLNPolygon`` object. To group multiple polylines together in one shape, use an
``MLNMultiPolyline`` or ``MLNShapeCollection`` object.
To make the polyline go across the antimeridian or international date line,
specify some longitudes less than −180 degrees or greater than 180 degrees.
@@ -54,8 +54,7 @@ NS_ASSUME_NONNULL_BEGIN
#### Related examples
- <doc:LineOnUserTap>
TODO: Annotation models, learn how to add an `MLNPolyine` object to your map.
- <doc:AnimatedLineExample>
*/
MLN_EXPORT
@interface MLNPolyline : MLNMultiPoint <MLNOverlay>
@@ -75,18 +74,18 @@ MLN_EXPORT
@end
/**
An `MLNMultiPolyline` object represents a shape consisting of one or more
An ``MLNMultiPolyline`` object represents a shape consisting of one or more
polylines. For example, you could use a multipolyline shape to represent both
sides of a divided highway (dual carriageway), excluding the median (central
reservation): each carriageway would be a distinct `MLNPolyline` object.
You can add multipolyline shapes to the map by adding them to an
`MLNShapeSource` object. Configure the appearance of an `MLNShapeSource`’s or
`MLNVectorTileSource`’s multipolylines collectively using an
`MLNLineStyleLayer` or `MLNSymbolStyleLayer` object.
``MLNShapeSource`` object. Configure the appearance of an `MLNShapeSource`’s or
``MLNVectorTileSource``’s multipolylines collectively using an
``MLNLineStyleLayer`` or ``MLNSymbolStyleLayer`` object.
You cannot add an `MLNMultiPolyline` object directly to a map view using
`-[MLNMapView addAnnotation:]` or `-[MLNMapView addOverlay:]`. However, you can
You cannot add an ``MLNMultiPolyline`` object directly to a map view using
``MLNMapView/addAnnotation:`` or ``MLNMapView/addOverlay:``. However, you can
add the `polylines` array’s items as overlays individually.
A multipolyline is known as a

View File

@@ -0,0 +1,47 @@
# Add Marker
Simply add a marker to a map!
> Note: This example uses UIKit.
<!-- include-example(AddMarkerSymbolExample) -->
```swift
class AddMarkerSymbolExampleUIKit: UIViewController, MLNMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MLNMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
// Set the map’s center coordinate and zoom level.
mapView.setCenter(CLLocationCoordinate2D(latitude: 41.8864, longitude: -87.7135), zoomLevel: 13, animated: false)
view.addSubview(mapView)
}
func mapView(_ mapView: MLNMapView, didFinishLoading style: MLNStyle) {
// Create point to represent where the symbol should be placed
let point = MLNPointAnnotation()
point.coordinate = mapView.centerCoordinate
// Create a data source to hold the point data
let shapeSource = MLNShapeSource(identifier: "marker-source", shape: point, options: nil)
// Create a style layer for the symbol
let shapeLayer = MLNSymbolStyleLayer(identifier: "marker-style", source: shapeSource)
// Add the image to the style's sprite
if let image = UIImage(named: "house-icon") {
style.setImage(image, forName: "home-symbol")
}
// Tell the layer to use the image in the sprite
shapeLayer.iconImageName = NSExpression(forConstantValue: "home-symbol")
// Add the source and style layer to the map
style.addSource(shapeSource)
style.addLayer(shapeLayer)
}
}
```

View File

@@ -0,0 +1,173 @@
# Animated Line
Add an animated line to a map
> Note: This example uses UIKit.
Demonstrates using ``MLNPolyline/polylineWithCoordinates:count:`` to update an ``MLNPolyline``.
<!-- include-example(AnimatedLineExample) -->
```swift
class AnimatedLineExample: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
var timer: Timer?
var polylineSource: MLNShapeSource?
var currentIndex = 1
var allCoordinates: [CLLocationCoordinate2D]!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(
CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
zoomLevel: 11,
animated: false
)
view.addSubview(mapView)
mapView.delegate = self
allCoordinates = coordinates
}
// Wait until the map is loaded before adding to the map.
func mapViewDidFinishLoadingMap(_ mapView: MLNMapView) {
addPolyline(to: mapView.style!)
animatePolyline()
}
func addPolyline(to style: MLNStyle) {
// Add an empty MLNShapeSource, we’ll keep a reference to this and add points to this later.
let source = MLNShapeSource(identifier: "polyline", shape: nil, options: nil)
style.addSource(source)
polylineSource = source
// Add a layer to style our polyline.
let layer = MLNLineStyleLayer(identifier: "polyline", source: source)
layer.lineJoin = NSExpression(forConstantValue: "round")
layer.lineCap = NSExpression(forConstantValue: "round")
layer.lineColor = NSExpression(forConstantValue: UIColor.blue)
// The line width should gradually increase based on the zoom level.
layer.lineWidth = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [14: 5, 18: 20]))
style.addLayer(layer)
}
func animatePolyline() {
currentIndex = 1
// Start a timer that will simulate adding points to our polyline. This could also represent coordinates being added to our polyline from another source, such as a CLLocationManagerDelegate.
timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(tick), userInfo: nil, repeats: true)
}
@objc func tick() {
if currentIndex > allCoordinates.count {
timer?.invalidate()
timer = nil
return
}
// Create a subarray of locations up to the current index.
let coordinates = Array(allCoordinates[0 ..< currentIndex])
// Update our MLNShapeSource with the current locations.
updatePolylineWithCoordinates(coordinates: coordinates)
currentIndex += 1
}
func updatePolylineWithCoordinates(coordinates: [CLLocationCoordinate2D]) {
var mutableCoordinates = coordinates
let polyline = MLNPolylineFeature(coordinates: &mutableCoordinates, count: UInt(mutableCoordinates.count))
// Updating the MLNShapeSource’s shape will have the map redraw our polyline with the current coordinates.
polylineSource?.shape = polyline
}
let coordinates = [
(-122.63748, 45.52214),
(-122.64855, 45.52218),
(-122.6545, 45.52219),
(-122.65497, 45.52196),
(-122.65631, 45.52104),
(-122.6578, 45.51935),
(-122.65867, 45.51848),
(-122.65872, 45.51293),
(-122.66576, 45.51295),
(-122.66745, 45.51252),
(-122.66813, 45.51244),
(-122.67359, 45.51385),
(-122.67415, 45.51406),
(-122.67481, 45.51484),
(-122.676, 45.51532),
(-122.68106, 45.51668),
(-122.68503, 45.50934),
(-122.68546, 45.50858),
(-122.6852, 45.50783),
(-122.68424, 45.50714),
(-122.68433, 45.50585),
(-122.68429, 45.50521),
(-122.68456, 45.50445),
(-122.68538, 45.50371),
(-122.68653, 45.50311),
(-122.68731, 45.50292),
(-122.68742, 45.50253),
(-122.6867, 45.50239),
(-122.68545, 45.5026),
(-122.68407, 45.50294),
(-122.68357, 45.50271),
(-122.68236, 45.50055),
(-122.68233, 45.49994),
(-122.68267, 45.49955),
(-122.68257, 45.49919),
(-122.68376, 45.49842),
(-122.68428, 45.49821),
(-122.68573, 45.49798),
(-122.68923, 45.49805),
(-122.68926, 45.49857),
(-122.68814, 45.49911),
(-122.68865, 45.49921),
(-122.6897, 45.49905),
(-122.69346, 45.49917),
(-122.69404, 45.49902),
(-122.69438, 45.49796),
(-122.69504, 45.49697),
(-122.69624, 45.49661),
(-122.69781, 45.4955),
(-122.69803, 45.49517),
(-122.69711, 45.49508),
(-122.69688, 45.4948),
(-122.69744, 45.49368),
(-122.69702, 45.49311),
(-122.69665, 45.49294),
(-122.69788, 45.49212),
(-122.69771, 45.49264),
(-122.69835, 45.49332),
(-122.7007, 45.49334),
(-122.70167, 45.49358),
(-122.70215, 45.49401),
(-122.70229, 45.49439),
(-122.70185, 45.49566),
(-122.70215, 45.49635),
(-122.70346, 45.49674),
(-122.70517, 45.49758),
(-122.70614, 45.49736),
(-122.70663, 45.49736),
(-122.70807, 45.49767),
(-122.70807, 45.49798),
(-122.70717, 45.49798),
(-122.70713, 45.4984),
(-122.70774, 45.49893),
].map { CLLocationCoordinate2D(latitude: $0.1, longitude: $0.0) }
}
```
![](AnimatedLineExample.gif)
This map uses data from OpenStreetMap, the style uses OpenMapTiles.

View File

@@ -0,0 +1,101 @@
# Custom Annotation View
Add a custom annotation view
This examples shows how you can implement and use a custom ``MLNAnnotationView``.
You need to implement ``MLNMapViewDelegate/mapView:viewForAnnotation:`` of ``MLNMapViewDelegate`` which will be called when you add an ``MLNAnnotation`` to the example. In this case, three ``MLNPointAnnotation``s are added to the map. When one is selected selected ``MLNAnnotationView/setSelected:animated:`` will be called.
<!-- include-example(AnnotationViewExample) -->
```swift
class AnnotationViewExample: UIViewController, MLNMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MLNMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.attributionButton.isHidden = true
mapView.tintColor = .lightGray
mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 0, longitude: 66)
mapView.zoomLevel = 2
mapView.delegate = self
view.addSubview(mapView)
// Specify coordinates for our annotations.
let coordinates = [
CLLocationCoordinate2D(latitude: 0, longitude: 33),
CLLocationCoordinate2D(latitude: 0, longitude: 66),
CLLocationCoordinate2D(latitude: 0, longitude: 99),
]
// Fill an array with point annotations and add it to the map.
var pointAnnotations = [MLNPointAnnotation]()
for coordinate in coordinates {
let point = MLNPointAnnotation()
point.coordinate = coordinate
point.title = "\(coordinate.latitude), \(coordinate.longitude)"
pointAnnotations.append(point)
}
mapView.addAnnotations(pointAnnotations)
}
// MARK: - MLNMapViewDelegate methods
// This delegate method is where you tell the map to load a view for a specific annotation. To load a static MLNAnnotationImage, you would use `-mapView:imageForAnnotation:`.
func mapView(_ mapView: MLNMapView, viewFor annotation: MLNAnnotation) -> MLNAnnotationView? {
// This example is only concerned with point annotations.
guard annotation is MLNPointAnnotation else {
return nil
}
// Use the point annotation’s longitude value (as a string) as the reuse identifier for its view.
let reuseIdentifier = "\(annotation.coordinate.longitude)"
// For better performance, always try to reuse existing annotations.
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
// If there’s no reusable annotation view available, initialize a new one.
if annotationView == nil {
annotationView = CustomAnnotationView(reuseIdentifier: reuseIdentifier)
annotationView!.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
// Set the annotation view’s background color to a value determined by its longitude.
let hue = CGFloat(annotation.coordinate.longitude) / 100
annotationView!.backgroundColor = UIColor(hue: hue, saturation: 0.5, brightness: 1, alpha: 1)
}
return annotationView
}
func mapView(_: MLNMapView, annotationCanShowCallout _: MLNAnnotation) -> Bool {
true
}
}
//
// MLNAnnotationView subclass
class CustomAnnotationView: MLNAnnotationView {
override func layoutSubviews() {
super.layoutSubviews()
// Use CALayer’s corner radius to turn this view into a circle.
layer.cornerRadius = bounds.width / 2
layer.borderWidth = 2
layer.borderColor = UIColor.white.cgColor
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Animate the border width in/out, creating an iris effect.
let animation = CABasicAnimation(keyPath: "borderWidth")
animation.duration = 0.1
layer.borderWidth = selected ? bounds.width / 4 : 2
layer.add(animation, forKey: "borderWidth")
}
}
```
![](AnnotationViewExample.png)

View File

@@ -2,7 +2,7 @@
Constrain the map to a certain area.
## Overview
> Note: This example uses SwiftUI.
To constrain the map to a certain area, you need to implement the ``MLNMapViewDelegate/mapView:shouldChangeFromCamera:toCamera:`` method of ``MLNMapViewDelegate``. By returning a boolean you can either allow or disallow a camera change.

View File

@@ -5,5 +5,8 @@ The following styles are used in the examples:
<!-- include-example(ExampleStyles) -->
```swift
let AMERICANA_STYLE = URL(string:
"https://zelonewolf.github.io/openstreetmap-americana/style.json")
let VERSATILES_COLORFUL_STYLE = URL(string: "https://tiles.versatiles.org/assets/styles/colorful.json")
```

View File

@@ -28,6 +28,8 @@ Verify you can import MapLibre in your app:
import MapLibre
```
## SwiftUI
To use MapLibre with SwiftUI we need to create a wrapper for the UIKit view that MapLibre provides (using UIViewRepresentable. The simplest way to implement this protocol is as follows:
<!-- include-example(SimpleMap) -->
@@ -59,3 +61,27 @@ struct MyApp: App {
When running your app in the simulator you should be greeted with the default [Demotiles](https://demotiles.maplibre.org/) style:
![](DemotilesScreenshot.png)
## UIKit
You can use the following `UIViewController` to get started with MapLibre Native iOS with UIKit.
```swift
class SimpleMap: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MLNMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.delegate = self
}
// MLNMapViewDelegate method called when map has finished loading
func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) {
}
}
```

View File

@@ -2,7 +2,7 @@
Demonstrating adding ``MLNPolyline`` annotations and responding to user input.
## Overview
> Note: This example uses SwiftUI.
This example draws a line from the tapped location to the center of the map. Handling the tap is done by the `Coordinator` class. It converts the location on the view to a geographic coordinate. It removes existing annotations before adding the new line.

View File

@@ -1,11 +1,13 @@
# Using GeoJSON with a line style layer (UIKit)
# Using GeoJSON with a line style layer
> Note: This example uses UIKit.
Adding an ``MLNLineStyleLayer`` to the map using a GeoJSON file.
<!-- include-example(LineStyleLayerExampleUIKit) -->
<!-- include-example(LineStyleLayerExample) -->
```swift
class LineStyleLayerExampleUIKit: UIViewController, MLNMapViewDelegate {
class LineStyleLayerExample: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {

View File

@@ -2,7 +2,7 @@
Requesting precise location with ``MLNLocationManager``.
## Overview
> Note: This example uses SwiftUI.
This example shows how to request a precise location with ``MLNLocationManager``.

View File

@@ -4,6 +4,10 @@
@Available(iOS, introduced: "12.0")
}
@Options(scope: global) {
@AutomaticArticleSubheading(disabled)
}
Powerful, free and open-source mapping toolkit with full control over data sources and styling.
## Overview
@@ -17,8 +21,10 @@ Powerful, free and open-source mapping toolkit with full control over data sourc
- <doc:LineOnUserTap>
- <doc:LocationPrivacyExample>
- <doc:BlockingGesturesExample>
- <doc:LineStyleLayerExampleUIKit>
- <doc:WebAPIDataExampleUIKit>
- <doc:LineStyleLayerExample>
- <doc:WebAPIDataExample>
- <doc:AnimatedLineExample>
- <doc:AnnotationViewExample>
## Topics

View File

@@ -0,0 +1,226 @@
# Showing data from an API
> Note: This example uses UIKit.
Showing data from an API with custom styling and interaction
This example loads lighthouses in the United States from [WikiData](http://tinyurl.com/zrl2jc4). It adds points to the map and applies dynamic styling to these points. When zooming in the dots become larger circles with a custom icon and the name of the lighthouse shown next to it. When tapping a callout is shown with the name of the lighthouse that was tapped on.
<!-- include-example(WebAPIDataExample) -->
```swift
class WebAPIDataExample: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MLNMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(CLLocationCoordinate2D(latitude: 37.090240, longitude: -95.712891), zoomLevel: 2, animated: false)
mapView.delegate = self
mapView.attributionButton.isHidden = true
view.addSubview(mapView)
// Add a single tap gesture recognizer. This gesture requires the built-in MLNMapView tap gestures (such as those for zoom and annotation selection) to fail.
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(sender:)))
for recognizer in mapView.gestureRecognizers! where recognizer is UITapGestureRecognizer {
singleTap.require(toFail: recognizer)
}
mapView.addGestureRecognizer(singleTap)
}
func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) {
fetchPoints { [weak self] features in
self?.addItemsToMap(features: features)
}
}
func addItemsToMap(features: [MLNPointFeature]) {
// MLNMapView.style is optional, so you must guard against it not being set.
guard let style = mapView.style else { return }
// You can add custom UIImages to the map style.
// These can be referenced by an MLNSymbolStyleLayer’s iconImage property.
style.setImage(UIImage(named: "lighthouse")!, forName: "lighthouse")
// Add the features to the map as a shape source.
let source = MLNShapeSource(identifier: "us-lighthouses", features: features, options: nil)
style.addSource(source)
let lighthouseColor = UIColor(red: 0.08, green: 0.44, blue: 0.96, alpha: 1.0)
// Use MLNCircleStyleLayer to represent the points with simple circles.
// In this case, we can use style functions to gradually change properties between zoom level 2 and 7: the circle opacity from 50% to 100% and the circle radius from 2pt to 3pt.
let circles = MLNCircleStyleLayer(identifier: "lighthouse-circles", source: source)
circles.circleColor = NSExpression(forConstantValue: lighthouseColor)
// The circles should increase in opacity from 0.5 to 1 based on zoom level.
circles.circleOpacity = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [2: 0.5, 7: 1]))
circles.circleRadius = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [2: 2, 7: 3]))
// Use MLNSymbolStyleLayer for more complex styling of points including custom icons and text rendering.
let symbols = MLNSymbolStyleLayer(identifier: "lighthouse-symbols", source: source)
symbols.iconImageName = NSExpression(forConstantValue: "lighthouse")
symbols.iconColor = NSExpression(forConstantValue: lighthouseColor)
symbols.iconScale = NSExpression(forConstantValue: 0.5)
symbols.iconHaloColor = NSExpression(forConstantValue: UIColor.white.withAlphaComponent(0.5))
symbols.iconHaloWidth = NSExpression(forConstantValue: 1)
symbols.iconOpacity = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [5.9: 0, 6: 1]))
// "name" references the "name" key in an MLNPointFeature’s attributes dictionary.
symbols.text = NSExpression(forKeyPath: "name")
symbols.textColor = symbols.iconColor
symbols.textFontSize = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [10: 10, 16: 16]))
symbols.textTranslation = NSExpression(forConstantValue: NSValue(cgVector: CGVector(dx: 15, dy: 0)))
symbols.textOpacity = symbols.iconOpacity
symbols.textHaloColor = symbols.iconHaloColor
symbols.textHaloWidth = symbols.iconHaloWidth
symbols.textJustification = NSExpression(forConstantValue: NSValue(mlnTextJustification: .left))
symbols.textAnchor = NSExpression(forConstantValue: NSValue(mlnTextAnchor: .left))
style.addLayer(circles)
style.addLayer(symbols)
}
// MARK: - Feature interaction
@IBAction func handleMapTap(sender: UITapGestureRecognizer) {
if sender.state == .ended {
// Limit feature selection to just the following layer identifiers.
let layerIdentifiers: Set = ["lighthouse-symbols", "lighthouse-circles"]
// Try matching the exact point first.
let point = sender.location(in: sender.view!)
for feature in mapView.visibleFeatures(at: point, styleLayerIdentifiers: layerIdentifiers)
where feature is MLNPointFeature
{
guard let selectedFeature = feature as? MLNPointFeature else {
fatalError("Failed to cast selected feature as MLNPointFeature")
}
showCallout(feature: selectedFeature)
return
}
let touchCoordinate = mapView.convert(point, toCoordinateFrom: sender.view!)
let touchLocation = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
// Otherwise, get all features within a rect the size of a touch (44x44).
let touchRect = CGRect(origin: point, size: .zero).insetBy(dx: -22.0, dy: -22.0)
let possibleFeatures = mapView.visibleFeatures(in: touchRect, styleLayerIdentifiers: Set(layerIdentifiers)).filter { $0 is MLNPointFeature }
// Select the closest feature to the touch center.
let closestFeatures = possibleFeatures.sorted(by: {
CLLocation(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude).distance(from: touchLocation) < CLLocation(latitude: $1.coordinate.latitude, longitude: $1.coordinate.longitude).distance(from: touchLocation)
})
if let feature = closestFeatures.first {
guard let closestFeature = feature as? MLNPointFeature else {
fatalError("Failed to cast selected feature as MLNPointFeature")
}
showCallout(feature: closestFeature)
return
}
// If no features were found, deselect the selected annotation, if any.
mapView.deselectAnnotation(mapView.selectedAnnotations.first, animated: true)
}
}
func showCallout(feature: MLNPointFeature) {
let point = MLNPointFeature()
point.title = feature.attributes["name"] as? String
point.coordinate = feature.coordinate
// Selecting an feature that doesn’t already exist on the map will add a new annotation view.
// We’ll need to use the map’s delegate methods to add an empty annotation view and remove it when we’re done selecting it.
mapView.selectAnnotation(point, animated: true, completionHandler: nil)
}
// MARK: - MLNMapViewDelegate
func mapView(_: MLNMapView, annotationCanShowCallout _: MLNAnnotation) -> Bool {
true
}
func mapView(_ mapView: MLNMapView, didDeselect annotation: MLNAnnotation) {
mapView.removeAnnotations([annotation])
}
func mapView(_: MLNMapView, viewFor _: MLNAnnotation) -> MLNAnnotationView? {
// Create an empty view annotation. Set a frame to offset the callout.
MLNAnnotationView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
}
// MARK: - Data fetching and parsing
func fetchPoints(withCompletion completion: @escaping (([MLNPointFeature]) -> Void)) {
// Wikidata query for all lighthouses in the United States: http://tinyurl.com/zrl2jc4
let query = "SELECT DISTINCT ?item " +
"?itemLabel ?coor ?image " +
"WHERE " +
"{ " +
"?item wdt:P31 wd:Q39715 . " +
"?item wdt:P17 wd:Q30 . " +
"?item wdt:P625 ?coor . " +
"OPTIONAL { ?item wdt:P18 ?image } . " +
"SERVICE wikibase:label { bd:serviceParam wikibase:language \"en\" } " +
"} " +
"ORDER BY ?itemLabel"
let characterSet = NSMutableCharacterSet()
characterSet.formUnion(with: CharacterSet.urlQueryAllowed)
characterSet.removeCharacters(in: "?")
characterSet.removeCharacters(in: "&")
characterSet.removeCharacters(in: ":")
let encodedQuery = query.addingPercentEncoding(withAllowedCharacters: characterSet as CharacterSet)!
let request = URLRequest(url: URL(string: "https://query.wikidata.org/sparql?query=\(encodedQuery)&format=json")!)
URLSession.shared.dataTask(with: request, completionHandler: { data, _, error in
guard error == nil else {
preconditionFailure("Failed to load GeoJSON data: \(error!)")
}
guard
let data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject],
let results = json["results"] as? [String: AnyObject],
let items = results["bindings"] as? [[String: AnyObject]]
else {
preconditionFailure("Failed to parse GeoJSON data")
}
DispatchQueue.main.async {
completion(self.parseJSONItems(items: items))
}
}).resume()
}
func parseJSONItems(items: [[String: AnyObject]]) -> [MLNPointFeature] {
var features = [MLNPointFeature]()
for item in items {
guard let label = item["itemLabel"] as? [String: AnyObject],
let title = label["value"] as? String else { continue }
guard let coor = item["coor"] as? [String: AnyObject],
let point = coor["value"] as? String else { continue }
let parsedPoint = point.replacingOccurrences(of: "Point(", with: "").replacingOccurrences(of: ")", with: "")
let pointComponents = parsedPoint.components(separatedBy: " ")
let coordinate = CLLocationCoordinate2D(latitude: Double(pointComponents[1])!, longitude: Double(pointComponents[0])!)
let feature = MLNPointFeature()
feature.coordinate = coordinate
feature.title = title
// A feature’s attributes can used by runtime styling for things like text labels.
feature.attributes = [
"name": title,
]
features.append(feature)
}
return features
}
}
```
![](WebAPIDataExample.png)

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "house-icon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -12,6 +12,7 @@ swift_library(
srcs = glob(["Sources/*.swift"]),
data = glob([
"Data/*",
"Assets.xcassets/*",
]),
module_name = "Sources",
tags = ["manual"],

View File

@@ -0,0 +1,54 @@
import MapLibre
import SwiftUI
import UIKit
// #-example-code(AddMarkerSymbolExample)
class AddMarkerSymbolExampleUIKit: UIViewController, MLNMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MLNMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
// Set the map’s center coordinate and zoom level.
mapView.setCenter(CLLocationCoordinate2D(latitude: 41.8864, longitude: -87.7135), zoomLevel: 13, animated: false)
view.addSubview(mapView)
}
func mapView(_ mapView: MLNMapView, didFinishLoading style: MLNStyle) {
// Create point to represent where the symbol should be placed
let point = MLNPointAnnotation()
point.coordinate = mapView.centerCoordinate
// Create a data source to hold the point data
let shapeSource = MLNShapeSource(identifier: "marker-source", shape: point, options: nil)
// Create a style layer for the symbol
let shapeLayer = MLNSymbolStyleLayer(identifier: "marker-style", source: shapeSource)
// Add the image to the style's sprite
if let image = UIImage(named: "house-icon") {
style.setImage(image, forName: "home-symbol")
}
// Tell the layer to use the image in the sprite
shapeLayer.iconImageName = NSExpression(forConstantValue: "home-symbol")
// Add the source and style layer to the map
style.addSource(shapeSource)
style.addLayer(shapeLayer)
}
}
// #-end-example-code
struct AddMarkerSymbolExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = AddMarkerSymbolExampleUIKit
func makeUIViewController(context _: Context) -> AddMarkerSymbolExampleUIKit {
AddMarkerSymbolExampleUIKit()
}
func updateUIViewController(_: AddMarkerSymbolExampleUIKit, context _: Context) {}
}

View File

@@ -0,0 +1,173 @@
import MapLibre
import SwiftUI
import UIKit
// #-example-code(AnimatedLineExample)
class AnimatedLineExample: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
var timer: Timer?
var polylineSource: MLNShapeSource?
var currentIndex = 1
var allCoordinates: [CLLocationCoordinate2D]!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(
CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
zoomLevel: 11,
animated: false
)
view.addSubview(mapView)
mapView.delegate = self
allCoordinates = coordinates
}
// Wait until the map is loaded before adding to the map.
func mapViewDidFinishLoadingMap(_ mapView: MLNMapView) {
addPolyline(to: mapView.style!)
animatePolyline()
}
func addPolyline(to style: MLNStyle) {
// Add an empty MLNShapeSource, we’ll keep a reference to this and add points to this later.
let source = MLNShapeSource(identifier: "polyline", shape: nil, options: nil)
style.addSource(source)
polylineSource = source
// Add a layer to style our polyline.
let layer = MLNLineStyleLayer(identifier: "polyline", source: source)
layer.lineJoin = NSExpression(forConstantValue: "round")
layer.lineCap = NSExpression(forConstantValue: "round")
layer.lineColor = NSExpression(forConstantValue: UIColor.blue)
// The line width should gradually increase based on the zoom level.
layer.lineWidth = NSExpression(forMLNInterpolating: NSExpression.zoomLevelVariable, curveType: MLNExpressionInterpolationMode.linear, parameters: nil, stops: NSExpression(forConstantValue: [14: 5, 18: 20]))
style.addLayer(layer)
}
func animatePolyline() {
currentIndex = 1
// Start a timer that will simulate adding points to our polyline. This could also represent coordinates being added to our polyline from another source, such as a CLLocationManagerDelegate.
timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(tick), userInfo: nil, repeats: true)
}
@objc func tick() {
if currentIndex > allCoordinates.count {
timer?.invalidate()
timer = nil
return
}
// Create a subarray of locations up to the current index.
let coordinates = Array(allCoordinates[0 ..< currentIndex])
// Update our MLNShapeSource with the current locations.
updatePolylineWithCoordinates(coordinates: coordinates)
currentIndex += 1
}
func updatePolylineWithCoordinates(coordinates: [CLLocationCoordinate2D]) {
var mutableCoordinates = coordinates
let polyline = MLNPolylineFeature(coordinates: &mutableCoordinates, count: UInt(mutableCoordinates.count))
// Updating the MLNShapeSource’s shape will have the map redraw our polyline with the current coordinates.
polylineSource?.shape = polyline
}
let coordinates = [
(-122.63748, 45.52214),
(-122.64855, 45.52218),
(-122.6545, 45.52219),
(-122.65497, 45.52196),
(-122.65631, 45.52104),
(-122.6578, 45.51935),
(-122.65867, 45.51848),
(-122.65872, 45.51293),
(-122.66576, 45.51295),
(-122.66745, 45.51252),
(-122.66813, 45.51244),
(-122.67359, 45.51385),
(-122.67415, 45.51406),
(-122.67481, 45.51484),
(-122.676, 45.51532),
(-122.68106, 45.51668),
(-122.68503, 45.50934),
(-122.68546, 45.50858),
(-122.6852, 45.50783),
(-122.68424, 45.50714),
(-122.68433, 45.50585),
(-122.68429, 45.50521),
(-122.68456, 45.50445),
(-122.68538, 45.50371),
(-122.68653, 45.50311),
(-122.68731, 45.50292),
(-122.68742, 45.50253),
(-122.6867, 45.50239),
(-122.68545, 45.5026),
(-122.68407, 45.50294),
(-122.68357, 45.50271),
(-122.68236, 45.50055),
(-122.68233, 45.49994),
(-122.68267, 45.49955),
(-122.68257, 45.49919),
(-122.68376, 45.49842),
(-122.68428, 45.49821),
(-122.68573, 45.49798),
(-122.68923, 45.49805),
(-122.68926, 45.49857),
(-122.68814, 45.49911),
(-122.68865, 45.49921),
(-122.6897, 45.49905),
(-122.69346, 45.49917),
(-122.69404, 45.49902),
(-122.69438, 45.49796),
(-122.69504, 45.49697),
(-122.69624, 45.49661),
(-122.69781, 45.4955),
(-122.69803, 45.49517),
(-122.69711, 45.49508),
(-122.69688, 45.4948),
(-122.69744, 45.49368),
(-122.69702, 45.49311),
(-122.69665, 45.49294),
(-122.69788, 45.49212),
(-122.69771, 45.49264),
(-122.69835, 45.49332),
(-122.7007, 45.49334),
(-122.70167, 45.49358),
(-122.70215, 45.49401),
(-122.70229, 45.49439),
(-122.70185, 45.49566),
(-122.70215, 45.49635),
(-122.70346, 45.49674),
(-122.70517, 45.49758),
(-122.70614, 45.49736),
(-122.70663, 45.49736),
(-122.70807, 45.49767),
(-122.70807, 45.49798),
(-122.70717, 45.49798),
(-122.70713, 45.4984),
(-122.70774, 45.49893),
].map { CLLocationCoordinate2D(latitude: $0.1, longitude: $0.0) }
}
// #-end-example-code
struct AnimatedLineExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = AnimatedLineExample
func makeUIViewController(context _: Context) -> AnimatedLineExample {
AnimatedLineExample()
}
func updateUIViewController(_: AnimatedLineExample, context _: Context) {}
}

View File

@@ -0,0 +1,104 @@
import MapLibre
import SwiftUI
import UIKit
// #-example-code(AnnotationViewExample)
class AnnotationViewExample: UIViewController, MLNMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MLNMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.attributionButton.isHidden = true
mapView.tintColor = .lightGray
mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 0, longitude: 66)
mapView.zoomLevel = 2
mapView.delegate = self
view.addSubview(mapView)
// Specify coordinates for our annotations.
let coordinates = [
CLLocationCoordinate2D(latitude: 0, longitude: 33),
CLLocationCoordinate2D(latitude: 0, longitude: 66),
CLLocationCoordinate2D(latitude: 0, longitude: 99),
]
// Fill an array with point annotations and add it to the map.
var pointAnnotations = [MLNPointAnnotation]()
for coordinate in coordinates {
let point = MLNPointAnnotation()
point.coordinate = coordinate
point.title = "\(coordinate.latitude), \(coordinate.longitude)"
pointAnnotations.append(point)
}
mapView.addAnnotations(pointAnnotations)
}
// MARK: - MLNMapViewDelegate methods
// This delegate method is where you tell the map to load a view for a specific annotation. To load a static MLNAnnotationImage, you would use `-mapView:imageForAnnotation:`.
func mapView(_ mapView: MLNMapView, viewFor annotation: MLNAnnotation) -> MLNAnnotationView? {
// This example is only concerned with point annotations.
guard annotation is MLNPointAnnotation else {
return nil
}
// Use the point annotation’s longitude value (as a string) as the reuse identifier for its view.
let reuseIdentifier = "\(annotation.coordinate.longitude)"
// For better performance, always try to reuse existing annotations.
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
// If there’s no reusable annotation view available, initialize a new one.
if annotationView == nil {
annotationView = CustomAnnotationView(reuseIdentifier: reuseIdentifier)
annotationView!.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
// Set the annotation view’s background color to a value determined by its longitude.
let hue = CGFloat(annotation.coordinate.longitude) / 100
annotationView!.backgroundColor = UIColor(hue: hue, saturation: 0.5, brightness: 1, alpha: 1)
}
return annotationView
}
func mapView(_: MLNMapView, annotationCanShowCallout _: MLNAnnotation) -> Bool {
true
}
}
//
// MLNAnnotationView subclass
class CustomAnnotationView: MLNAnnotationView {
override func layoutSubviews() {
super.layoutSubviews()
// Use CALayer’s corner radius to turn this view into a circle.
layer.cornerRadius = bounds.width / 2
layer.borderWidth = 2
layer.borderColor = UIColor.white.cgColor
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Animate the border width in/out, creating an iris effect.
let animation = CABasicAnimation(keyPath: "borderWidth")
animation.duration = 0.1
layer.borderWidth = selected ? bounds.width / 4 : 2
layer.add(animation, forKey: "borderWidth")
}
}
// #-end-example-code
struct AnnotationViewExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = AnnotationViewExample
func makeUIViewController(context _: Context) -> AnnotationViewExample {
AnnotationViewExample()
}
func updateUIViewController(_: AnnotationViewExample, context _: Context) {}
}

View File

@@ -2,8 +2,8 @@ import MapLibre
import SwiftUI
import UIKit
// #-example-code(LineStyleLayerExampleUIKit)
class LineStyleLayerExampleUIKit: UIViewController, MLNMapViewDelegate {
// #-example-code(LineStyleLayerExample)
class LineStyleLayerExample: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {
@@ -102,11 +102,11 @@ class LineStyleLayerExampleUIKit: UIViewController, MLNMapViewDelegate {
// #-end-example-code
struct LineStyleLayerExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = LineStyleLayerExampleUIKit
typealias UIViewControllerType = LineStyleLayerExample
func makeUIViewController(context _: Context) -> LineStyleLayerExampleUIKit {
LineStyleLayerExampleUIKit()
func makeUIViewController(context _: Context) -> LineStyleLayerExample {
LineStyleLayerExample()
}
func updateUIViewController(_: LineStyleLayerExampleUIKit, context _: Context) {}
func updateUIViewController(_: LineStyleLayerExample, context _: Context) {}
}

View File

@@ -20,9 +20,18 @@ struct MapLibreNavigationView: View {
NavigationLink("LineStyleLayerExample") {
LineStyleLayerExampleUIViewControllerRepresentable()
}
NavigationLink("WebAPIDataExampleUIKit") {
NavigationLink("WebAPIDataExample") {
WebAPIDataExampleUIViewControllerRepresentable()
}
NavigationLink("AddMarkerExample") {
AddMarkerSymbolExampleUIViewControllerRepresentable()
}
NavigationLink("AnimatedLineExample") {
AnimatedLineExampleUIViewControllerRepresentable()
}
NavigationLink("AnnotationViewExample") {
AnnotationViewExampleUIViewControllerRepresentable()
}
}
}
}

View File

@@ -2,6 +2,9 @@ import Foundation
// #-example-code(ExampleStyles)
let AMERICANA_STYLE = URL(string:
"https://zelonewolf.github.io/openstreetmap-americana/style.json")
let VERSATILES_COLORFUL_STYLE = URL(string: "https://tiles.versatiles.org/assets/styles/colorful.json")
// #-end-example-code

View File

@@ -3,8 +3,8 @@ import MapLibre
import SwiftUI
import UIKit
// #-example-code(WebAPIDataExampleUIKit)
class WebAPIDataExampleUIKit: UIViewController, MLNMapViewDelegate {
// #-example-code(WebAPIDataExample)
class WebAPIDataExample: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {
@@ -220,11 +220,11 @@ class WebAPIDataExampleUIKit: UIViewController, MLNMapViewDelegate {
// #-end-example-code
struct WebAPIDataExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = WebAPIDataExampleUIKit
typealias UIViewControllerType = WebAPIDataExample
func makeUIViewController(context _: Context) -> WebAPIDataExampleUIKit {
WebAPIDataExampleUIKit()
func makeUIViewController(context _: Context) -> WebAPIDataExample {
WebAPIDataExample()
}
func updateUIViewController(_: WebAPIDataExampleUIKit, context _: Context) {}
func updateUIViewController(_: WebAPIDataExample, context _: Context) {}
}

View File

@@ -59,19 +59,23 @@ filter_filenames() {
ios_headers=$(filter_filenames "platform/ios/src" "$public_headers")
darwin_headers=$(filter_filenames "platform/darwin/src" "$public_headers")
for header in $ios_headers $darwin_headers $style_headers; do
xcrun --toolchain swift clang \
-extract-api \
--product-name=MapLibre \
-isysroot $SDK_PATH \
-F "$SDK_PATH"/System/Library/Frameworks \
-I "$PWD" \
-I "$build_dir"/headers \
-I platform/darwin/src \
-x objective-c-header \
-o "$build_dir"/symbol-graphs/$(basename $header).symbols.json \
$header
done
docc_options=(
--toolchain swift clang
-extract-api
--product-name=MapLibre
-isysroot "$SDK_PATH"
-F "$SDK_PATH/System/Library/Frameworks"
-I "$PWD"
-I "$build_dir/headers"
-I platform/darwin/src
-x objective-c-header
)
headers=($ios_headers $darwin_headers build/headers/*.h)
output_name="combined.symbols.json"
xcrun "${docc_options[@]}" \
-o "$build_dir/symbol-graphs/$output_name" \
"${headers[@]}"
export DOCC_HTML_DIR=$(dirname $(xcrun --toolchain swift --find docc))/../share/docc/render
$(xcrun --find docc) "$cmd" platform/ios/MapLibre.docc \

View File

@@ -41,7 +41,7 @@ typedef NS_ENUM(NSUInteger, MLNAnnotationViewDragState) {
/**
The `MLNAnnotationView` class is responsible for marking a point annotation
with a view. Annotation views represent an annotation object, which is an
object that corresponds to the `MLNAnnotation` protocol. When an annotation’s
object that corresponds to the ``MLNAnnotation`` protocol. When an annotation’s
geographic coordinate is visible in the map view, the map view asks its
delegate to a corresponding annotation view. If an annotation view is created
with a reuse identifier, the map view may recycle the view when it goes
@@ -49,7 +49,7 @@ typedef NS_ENUM(NSUInteger, MLNAnnotationViewDragState) {
Annotation views are compatible with UIKit, Core Animation, and other Cocoa
Touch frameworks. On the other hand, if you do not need animation or
interactivity such as dragging, you can use an `MLNAnnotationImage` instead to
interactivity such as dragging, you can use an ``MLNAnnotationImage`` instead to
conserve memory and optimize drawing performance.
*/
MLN_EXPORT
@@ -80,21 +80,21 @@ MLN_EXPORT
Initializes and returns a new annotation view object.
Providing an annotation allows you to explicitly associate the annotation instance
with the new view and, in custom subclasses of `MLNAnnotationView`, customize the view
with the new view and, in custom subclasses of ``MLNAnnotationView``, customize the view
based on properties of the annotation instance in an overridden initializer. However,
annotation views that are reused will not necessarily be associated with the
same annotation they were initialized with. Also, annotation views that are in
the reuse queue will have a nil value for the annotation property. Passing an annotation
instance to the view is optional and the map view will automatically associate annotations
with views when views are provided to the map via the `-[MLNMapViewDelegate
mapView:viewForAnnotation:]` method.
with views when views are provided to the map via the
``MLNMapViewDelegate/mapView:viewForAnnotation:`` method.
The reuse identifier provides a way for you to improve performance by recycling
annotation views as they enter and leave the map’s viewport. As an annotation
leaves the viewport, the map view moves its associated view to a reuse queue.
When a new annotation becomes visible, you can request a view for that
annotation by passing the appropriate reuse identifier string to the
`-[MLNMapView dequeueReusableAnnotationViewWithIdentifier:]` method.
``MLNMapView/dequeueReusableAnnotationViewWithIdentifier:`` method.
@param annotation The annotation object to associate with the new view.
@param reuseIdentifier A unique string identifier for this view that allows you
@@ -166,7 +166,7 @@ MLN_EXPORT
When the value of this property is `YES` and the map is tilted, the annotation
view appears smaller if it is towards the top of the view (closer to the
horizon) and larger if it is towards the bottom of the view (closer to the
viewpoint). This is also the behavior of `MLNAnnotationImage` objects. When the
viewpoint). This is also the behavior of ``MLNAnnotationImage`` objects. When the
value of this property is `NO` or the map’s pitch is zero, the annotation view
remains the same size regardless of its position on-screen.
@@ -184,7 +184,7 @@ MLN_EXPORT
with the map.
When the value of this property is `YES` and the map is rotated, the annotation
view rotates. This is also the behavior of `MLNAnnotationImage` objects. When the
view rotates. This is also the behavior of ``MLNAnnotationImage`` objects. When the
value of this property is `NO` the annotation has its rotation angle fixed.
The default value of this property is `NO`. Set this property to `YES` if the
@@ -202,12 +202,12 @@ MLN_EXPORT
By default, this property is set to `NO` and becomes `YES` when the user taps
the view. Selecting another annotation, whether it is associated with an
`MLNAnnotationView` or `MLNAnnotationImage` object, deselects any currently
``MLNAnnotationView`` or ``MLNAnnotationImage`` object, deselects any currently
selected view.
Setting this property changes the view’s appearance to reflect the new value
immediately. If you want the change to be animated, use the
`-setSelected:animated:` method instead.
``MLNAnnotationView/setSelected:animated:`` method instead.
*/
@property (nonatomic, assign, getter=isSelected) BOOL selected;
@@ -225,7 +225,7 @@ MLN_EXPORT
change is immediate.
#### Related examples
TODO: modify an `MLNAnnotationView`'s behavior when it is selected.
- <doc:AnnotationViewExample>
*/
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
@@ -246,7 +246,7 @@ MLN_EXPORT
If this property is set to `YES`, the user can drag the annotation after
pressing and holding the view, and the associated annotation object must also
implement the `-setCoordinate:` method. The default value of this property is
implement the `setCoordinate:` method. The default value of this property is
`NO`.
Setting this property to `YES` lets the map view know that the annotation is
@@ -263,9 +263,9 @@ MLN_EXPORT
/**
The current drag state of the annotation view.
All states are handled automatically when the `draggable` property is set to
All states are handled automatically when the ``MLNAnnotationView/draggable`` property is set to
`YES`. To perform a custom animation in response to a change to this property,
override the `-setDragState:animated:` method.
override the ``MLNAnnotationView/setDragState:animated:`` method.
*/
@property (nonatomic, readonly) MLNAnnotationViewDragState dragState;