iOS Documentation: Animated Marker Example, Custom Annotation Example (#2539)
This commit is contained in:
@@ -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
|
||||
|
||||
47
platform/ios/MapLibre.docc/AddMarkerSymbolExample.md
Normal file
47
platform/ios/MapLibre.docc/AddMarkerSymbolExample.md
Normal 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)
|
||||
}
|
||||
}
|
||||
```
|
||||
173
platform/ios/MapLibre.docc/AnimatedLineExample.md
Normal file
173
platform/ios/MapLibre.docc/AnimatedLineExample.md
Normal 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) }
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
This map uses data from OpenStreetMap, the style uses OpenMapTiles.
|
||||
101
platform/ios/MapLibre.docc/AnnotationViewExample.md
Normal file
101
platform/ios/MapLibre.docc/AnnotationViewExample.md
Normal 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")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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")
|
||||
```
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
## 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) {
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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() {
|
||||
@@ -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``.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
226
platform/ios/MapLibre.docc/WebAPIDataExample.md
Normal file
226
platform/ios/MapLibre.docc/WebAPIDataExample.md
Normal 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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
6
platform/ios/app-swift/Assets.xcassets/Contents.json
Normal file
6
platform/ios/app-swift/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
12
platform/ios/app-swift/Assets.xcassets/house-icon.imageset/Contents.json
vendored
Normal file
12
platform/ios/app-swift/Assets.xcassets/house-icon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "house-icon.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
platform/ios/app-swift/Assets.xcassets/house-icon.imageset/house-icon.pdf
vendored
Normal file
BIN
platform/ios/app-swift/Assets.xcassets/house-icon.imageset/house-icon.pdf
vendored
Normal file
Binary file not shown.
@@ -12,6 +12,7 @@ swift_library(
|
||||
srcs = glob(["Sources/*.swift"]),
|
||||
data = glob([
|
||||
"Data/*",
|
||||
"Assets.xcassets/*",
|
||||
]),
|
||||
module_name = "Sources",
|
||||
tags = ["manual"],
|
||||
|
||||
54
platform/ios/app-swift/Sources/AddMarkerExample.swift
Normal file
54
platform/ios/app-swift/Sources/AddMarkerExample.swift
Normal 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) {}
|
||||
}
|
||||
173
platform/ios/app-swift/Sources/AnimatedLineExample.swift
Normal file
173
platform/ios/app-swift/Sources/AnimatedLineExample.swift
Normal 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) {}
|
||||
}
|
||||
104
platform/ios/app-swift/Sources/AnnotationViewExample.swift
Normal file
104
platform/ios/app-swift/Sources/AnnotationViewExample.swift
Normal 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) {}
|
||||
}
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,18 @@ struct MapLibreNavigationView: View {
|
||||
NavigationLink("LineStyleLayerExample") {
|
||||
LineStyleLayerExampleUIViewControllerRepresentable()
|
||||
}
|
||||
NavigationLink("WebAPIDataExampleUIKit") {
|
||||
NavigationLink("WebAPIDataExample") {
|
||||
WebAPIDataExampleUIViewControllerRepresentable()
|
||||
}
|
||||
NavigationLink("AddMarkerExample") {
|
||||
AddMarkerSymbolExampleUIViewControllerRepresentable()
|
||||
}
|
||||
NavigationLink("AnimatedLineExample") {
|
||||
AnimatedLineExampleUIViewControllerRepresentable()
|
||||
}
|
||||
NavigationLink("AnnotationViewExample") {
|
||||
AnnotationViewExampleUIViewControllerRepresentable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user