diff --git a/platform/darwin/src/MLNPolyline.h b/platform/darwin/src/MLNPolyline.h index 6377faea9..98ec8ca23 100644 --- a/platform/darwin/src/MLNPolyline.h +++ b/platform/darwin/src/MLNPolyline.h @@ -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 - - - TODO: Annotation models, learn how to add an `MLNPolyine` object to your map. + - */ MLN_EXPORT @interface MLNPolyline : MLNMultiPoint @@ -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 diff --git a/platform/ios/MapLibre.docc/AddMarkerSymbolExample.md b/platform/ios/MapLibre.docc/AddMarkerSymbolExample.md new file mode 100644 index 000000000..68ec9956d --- /dev/null +++ b/platform/ios/MapLibre.docc/AddMarkerSymbolExample.md @@ -0,0 +1,47 @@ +# Add Marker + +Simply add a marker to a map! + +> Note: This example uses UIKit. + + + +```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) + } +} +``` \ No newline at end of file diff --git a/platform/ios/MapLibre.docc/AnimatedLineExample.md b/platform/ios/MapLibre.docc/AnimatedLineExample.md new file mode 100644 index 000000000..f48e53ec6 --- /dev/null +++ b/platform/ios/MapLibre.docc/AnimatedLineExample.md @@ -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``. + + + + +```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. \ No newline at end of file diff --git a/platform/ios/MapLibre.docc/AnnotationViewExample.md b/platform/ios/MapLibre.docc/AnnotationViewExample.md new file mode 100644 index 000000000..9934a15be --- /dev/null +++ b/platform/ios/MapLibre.docc/AnnotationViewExample.md @@ -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. + + + +```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) \ No newline at end of file diff --git a/platform/ios/MapLibre.docc/BlockingGesturesExample.md b/platform/ios/MapLibre.docc/BlockingGesturesExample.md index cc16c747e..830b7c101 100644 --- a/platform/ios/MapLibre.docc/BlockingGesturesExample.md +++ b/platform/ios/MapLibre.docc/BlockingGesturesExample.md @@ -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. diff --git a/platform/ios/MapLibre.docc/ExampleStyles.md b/platform/ios/MapLibre.docc/ExampleStyles.md index 8ccacf1a5..d931c3094 100644 --- a/platform/ios/MapLibre.docc/ExampleStyles.md +++ b/platform/ios/MapLibre.docc/ExampleStyles.md @@ -5,5 +5,8 @@ The following styles are used in the examples: ```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") ``` \ No newline at end of file diff --git a/platform/ios/MapLibre.docc/GettingStarted.md b/platform/ios/MapLibre.docc/GettingStarted.md index 72538f892..1c579d964 100644 --- a/platform/ios/MapLibre.docc/GettingStarted.md +++ b/platform/ios/MapLibre.docc/GettingStarted.md @@ -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: @@ -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) { + } +} +``` \ No newline at end of file diff --git a/platform/ios/MapLibre.docc/LineOnUserTap.md b/platform/ios/MapLibre.docc/LineOnUserTap.md index 5dca589a9..a428f31db 100644 --- a/platform/ios/MapLibre.docc/LineOnUserTap.md +++ b/platform/ios/MapLibre.docc/LineOnUserTap.md @@ -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. diff --git a/platform/ios/MapLibre.docc/LineStyleLayerExampleUIKit.md b/platform/ios/MapLibre.docc/LineStyleLayerExample.md similarity index 96% rename from platform/ios/MapLibre.docc/LineStyleLayerExampleUIKit.md rename to platform/ios/MapLibre.docc/LineStyleLayerExample.md index 6285c373d..eb9bea017 100644 --- a/platform/ios/MapLibre.docc/LineStyleLayerExampleUIKit.md +++ b/platform/ios/MapLibre.docc/LineStyleLayerExample.md @@ -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. - + ```swift -class LineStyleLayerExampleUIKit: UIViewController, MLNMapViewDelegate { +class LineStyleLayerExample: UIViewController, MLNMapViewDelegate { var mapView: MLNMapView! override func viewDidLoad() { diff --git a/platform/ios/MapLibre.docc/LocationPrivacyExample.md b/platform/ios/MapLibre.docc/LocationPrivacyExample.md index 2d40600a3..3dc288b7d 100644 --- a/platform/ios/MapLibre.docc/LocationPrivacyExample.md +++ b/platform/ios/MapLibre.docc/LocationPrivacyExample.md @@ -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``. diff --git a/platform/ios/MapLibre.docc/MapLibre.md b/platform/ios/MapLibre.docc/MapLibre.md index a2d603b02..eccb843ac 100644 --- a/platform/ios/MapLibre.docc/MapLibre.md +++ b/platform/ios/MapLibre.docc/MapLibre.md @@ -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 - - - -- -- +- +- +- +- ## Topics diff --git a/platform/ios/MapLibre.docc/WebAPIDataExample.md b/platform/ios/MapLibre.docc/WebAPIDataExample.md new file mode 100644 index 000000000..20f0ef33c --- /dev/null +++ b/platform/ios/MapLibre.docc/WebAPIDataExample.md @@ -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. + + + +```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) \ No newline at end of file diff --git a/platform/ios/app-swift/Assets.xcassets/Contents.json b/platform/ios/app-swift/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/platform/ios/app-swift/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/platform/ios/app-swift/Assets.xcassets/house-icon.imageset/Contents.json b/platform/ios/app-swift/Assets.xcassets/house-icon.imageset/Contents.json new file mode 100644 index 000000000..1f4b49d19 --- /dev/null +++ b/platform/ios/app-swift/Assets.xcassets/house-icon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "house-icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/platform/ios/app-swift/Assets.xcassets/house-icon.imageset/house-icon.pdf b/platform/ios/app-swift/Assets.xcassets/house-icon.imageset/house-icon.pdf new file mode 100644 index 000000000..77a8ab693 Binary files /dev/null and b/platform/ios/app-swift/Assets.xcassets/house-icon.imageset/house-icon.pdf differ diff --git a/platform/ios/app-swift/BUILD.bazel b/platform/ios/app-swift/BUILD.bazel index 67b6fbdfd..dcd0a60af 100644 --- a/platform/ios/app-swift/BUILD.bazel +++ b/platform/ios/app-swift/BUILD.bazel @@ -12,6 +12,7 @@ swift_library( srcs = glob(["Sources/*.swift"]), data = glob([ "Data/*", + "Assets.xcassets/*", ]), module_name = "Sources", tags = ["manual"], diff --git a/platform/ios/app-swift/Sources/AddMarkerExample.swift b/platform/ios/app-swift/Sources/AddMarkerExample.swift new file mode 100644 index 000000000..3822b3fa9 --- /dev/null +++ b/platform/ios/app-swift/Sources/AddMarkerExample.swift @@ -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) {} +} diff --git a/platform/ios/app-swift/Sources/AnimatedLineExample.swift b/platform/ios/app-swift/Sources/AnimatedLineExample.swift new file mode 100644 index 000000000..30c587db9 --- /dev/null +++ b/platform/ios/app-swift/Sources/AnimatedLineExample.swift @@ -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) {} +} diff --git a/platform/ios/app-swift/Sources/AnnotationViewExample.swift b/platform/ios/app-swift/Sources/AnnotationViewExample.swift new file mode 100644 index 000000000..b4b5071c4 --- /dev/null +++ b/platform/ios/app-swift/Sources/AnnotationViewExample.swift @@ -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) {} +} diff --git a/platform/ios/app-swift/Sources/LineStyleLayerExampleUIKit.swift b/platform/ios/app-swift/Sources/LineStyleLayerExampleUIKit.swift index 58aa8c08a..fc6d749b6 100644 --- a/platform/ios/app-swift/Sources/LineStyleLayerExampleUIKit.swift +++ b/platform/ios/app-swift/Sources/LineStyleLayerExampleUIKit.swift @@ -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) {} } diff --git a/platform/ios/app-swift/Sources/MapLibreNavigationView.swift b/platform/ios/app-swift/Sources/MapLibreNavigationView.swift index e6ea2545d..9bbffbfa6 100644 --- a/platform/ios/app-swift/Sources/MapLibreNavigationView.swift +++ b/platform/ios/app-swift/Sources/MapLibreNavigationView.swift @@ -20,9 +20,18 @@ struct MapLibreNavigationView: View { NavigationLink("LineStyleLayerExample") { LineStyleLayerExampleUIViewControllerRepresentable() } - NavigationLink("WebAPIDataExampleUIKit") { + NavigationLink("WebAPIDataExample") { WebAPIDataExampleUIViewControllerRepresentable() } + NavigationLink("AddMarkerExample") { + AddMarkerSymbolExampleUIViewControllerRepresentable() + } + NavigationLink("AnimatedLineExample") { + AnimatedLineExampleUIViewControllerRepresentable() + } + NavigationLink("AnnotationViewExample") { + AnnotationViewExampleUIViewControllerRepresentable() + } } } } diff --git a/platform/ios/app-swift/Sources/Styles.swift b/platform/ios/app-swift/Sources/Styles.swift index 76cecdd8e..8c06eef6e 100644 --- a/platform/ios/app-swift/Sources/Styles.swift +++ b/platform/ios/app-swift/Sources/Styles.swift @@ -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 diff --git a/platform/ios/app-swift/Sources/WebAPIDataExampleUIKit.swift b/platform/ios/app-swift/Sources/WebAPIDataExampleUIKit.swift index 1fe07d1e7..af5c2ebf5 100644 --- a/platform/ios/app-swift/Sources/WebAPIDataExampleUIKit.swift +++ b/platform/ios/app-swift/Sources/WebAPIDataExampleUIKit.swift @@ -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) {} } diff --git a/platform/ios/scripts/docc.sh b/platform/ios/scripts/docc.sh index 5a5183141..c085b821b 100755 --- a/platform/ios/scripts/docc.sh +++ b/platform/ios/scripts/docc.sh @@ -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 \ diff --git a/platform/ios/src/MLNAnnotationView.h b/platform/ios/src/MLNAnnotationView.h index 6e139d030..14b7aa50e 100644 --- a/platform/ios/src/MLNAnnotationView.h +++ b/platform/ios/src/MLNAnnotationView.h @@ -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. + - */ - (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;