From 8e479eea968bcaa5271bce25294efd5ccba79872 Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Mon, 13 May 2024 15:44:30 +0200 Subject: [PATCH] Add iOS example for constraining map to certain area (#2392) --- .../MapLibre.docc/BlockingGesturesExample.md | 61 +++++++++++++++++++ platform/ios/MapLibre.docc/ExampleStyles.md | 9 +++ platform/ios/MapLibre.docc/MapLibre.md | 1 + .../Sources/BlockingGesturesExample.swift | 54 ++++++++++++++++ .../Sources/MapLibreNavigationView.swift | 3 + platform/ios/app-swift/Sources/Styles.swift | 7 +++ platform/ios/src/MLNMapViewDelegate.h | 3 +- 7 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 platform/ios/MapLibre.docc/BlockingGesturesExample.md create mode 100644 platform/ios/MapLibre.docc/ExampleStyles.md create mode 100644 platform/ios/app-swift/Sources/BlockingGesturesExample.swift create mode 100644 platform/ios/app-swift/Sources/Styles.swift diff --git a/platform/ios/MapLibre.docc/BlockingGesturesExample.md b/platform/ios/MapLibre.docc/BlockingGesturesExample.md new file mode 100644 index 000000000..cc16c747e --- /dev/null +++ b/platform/ios/MapLibre.docc/BlockingGesturesExample.md @@ -0,0 +1,61 @@ +# Blocking Gestures + +Constrain the map to a certain area. + +## Overview + +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. + + + +```swift +// Denver, Colorado +private let center = CLLocationCoordinate2D(latitude: 39.748947, longitude: -104.995882) + +// Colorado’s bounds +private let colorado = MLNCoordinateBounds( + sw: CLLocationCoordinate2D(latitude: 36.986207, longitude: -109.049896), + ne: CLLocationCoordinate2D(latitude: 40.989329, longitude: -102.062592) +) + +struct BlockingGesturesExample: UIViewRepresentable { + class Coordinator: NSObject, MLNMapViewDelegate { + func mapView(_ mapView: MLNMapView, shouldChangeFrom _: MLNMapCamera, to newCamera: MLNMapCamera) -> Bool { + // Get the current camera to restore it after. + let currentCamera = mapView.camera + + // From the new camera obtain the center to test if it’s inside the boundaries. + let newCameraCenter = newCamera.centerCoordinate + + // Set the map’s visible bounds to newCamera. + mapView.camera = newCamera + let newVisibleCoordinates = mapView.visibleCoordinateBounds + + // Revert the camera. + mapView.camera = currentCamera + + // Test if the newCameraCenter and newVisibleCoordinates are inside self.colorado. + let inside = MLNCoordinateInCoordinateBounds(newCameraCenter, colorado) + let intersects = MLNCoordinateInCoordinateBounds(newVisibleCoordinates.ne, colorado) && MLNCoordinateInCoordinateBounds(newVisibleCoordinates.sw, colorado) + + return inside && intersects + } + } + + func makeUIView(context: Context) -> MLNMapView { + let mapView = MLNMapView(frame: .zero, styleURL: VERSATILES_COLORFUL_STYLE) + mapView.setCenter(center, zoomLevel: 10, direction: 0, animated: false) + mapView.delegate = context.coordinator + + return mapView + } + + func updateUIView(_: MLNMapView, context _: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator() + } +} +``` + +The style used in this example can be found here: . diff --git a/platform/ios/MapLibre.docc/ExampleStyles.md b/platform/ios/MapLibre.docc/ExampleStyles.md new file mode 100644 index 000000000..8ccacf1a5 --- /dev/null +++ b/platform/ios/MapLibre.docc/ExampleStyles.md @@ -0,0 +1,9 @@ +# Styles in Examples + +The following styles are used in the examples: + + + +```swift +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/MapLibre.md b/platform/ios/MapLibre.docc/MapLibre.md index c1595de3f..6334e24e7 100644 --- a/platform/ios/MapLibre.docc/MapLibre.md +++ b/platform/ios/MapLibre.docc/MapLibre.md @@ -16,6 +16,7 @@ Powerful, free and open-source mapping toolkit with full control over data sourc - - - +- ## Topics diff --git a/platform/ios/app-swift/Sources/BlockingGesturesExample.swift b/platform/ios/app-swift/Sources/BlockingGesturesExample.swift new file mode 100644 index 000000000..1802544b1 --- /dev/null +++ b/platform/ios/app-swift/Sources/BlockingGesturesExample.swift @@ -0,0 +1,54 @@ +import MapLibre +import SwiftUI +import UIKit + +// #-example-code(BlockingGesturesExample) +// Denver, Colorado +private let center = CLLocationCoordinate2D(latitude: 39.748947, longitude: -104.995882) + +// Colorado’s bounds +private let colorado = MLNCoordinateBounds( + sw: CLLocationCoordinate2D(latitude: 36.986207, longitude: -109.049896), + ne: CLLocationCoordinate2D(latitude: 40.989329, longitude: -102.062592) +) + +struct BlockingGesturesExample: UIViewRepresentable { + class Coordinator: NSObject, MLNMapViewDelegate { + func mapView(_ mapView: MLNMapView, shouldChangeFrom _: MLNMapCamera, to newCamera: MLNMapCamera) -> Bool { + // Get the current camera to restore it after. + let currentCamera = mapView.camera + + // From the new camera obtain the center to test if it’s inside the boundaries. + let newCameraCenter = newCamera.centerCoordinate + + // Set the map’s visible bounds to newCamera. + mapView.camera = newCamera + let newVisibleCoordinates = mapView.visibleCoordinateBounds + + // Revert the camera. + mapView.camera = currentCamera + + // Test if the newCameraCenter and newVisibleCoordinates are inside self.colorado. + let inside = MLNCoordinateInCoordinateBounds(newCameraCenter, colorado) + let intersects = MLNCoordinateInCoordinateBounds(newVisibleCoordinates.ne, colorado) && MLNCoordinateInCoordinateBounds(newVisibleCoordinates.sw, colorado) + + return inside && intersects + } + } + + func makeUIView(context: Context) -> MLNMapView { + let mapView = MLNMapView(frame: .zero, styleURL: VERSATILES_COLORFUL_STYLE) + mapView.setCenter(center, zoomLevel: 10, direction: 0, animated: false) + mapView.delegate = context.coordinator + + return mapView + } + + func updateUIView(_: MLNMapView, context _: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator() + } +} + +// #-end-example-code diff --git a/platform/ios/app-swift/Sources/MapLibreNavigationView.swift b/platform/ios/app-swift/Sources/MapLibreNavigationView.swift index 8559de588..a5fa59c08 100644 --- a/platform/ios/app-swift/Sources/MapLibreNavigationView.swift +++ b/platform/ios/app-swift/Sources/MapLibreNavigationView.swift @@ -14,6 +14,9 @@ struct MapLibreNavigationView: View { NavigationLink("LocationPrivacyExample") { LocationPrivacyExampleView() } + NavigationLink("BlockingGesturesExample") { + BlockingGesturesExample() + } } } } diff --git a/platform/ios/app-swift/Sources/Styles.swift b/platform/ios/app-swift/Sources/Styles.swift new file mode 100644 index 000000000..76cecdd8e --- /dev/null +++ b/platform/ios/app-swift/Sources/Styles.swift @@ -0,0 +1,7 @@ +import Foundation + +// #-example-code(ExampleStyles) + +let VERSATILES_COLORFUL_STYLE = URL(string: "https://tiles.versatiles.org/assets/styles/colorful.json") + +// #-end-example-code diff --git a/platform/ios/src/MLNMapViewDelegate.h b/platform/ios/src/MLNMapViewDelegate.h index 5e0ccc39f..e21c0e22a 100644 --- a/platform/ios/src/MLNMapViewDelegate.h +++ b/platform/ios/src/MLNMapViewDelegate.h @@ -44,8 +44,7 @@ NS_ASSUME_NONNULL_BEGIN `oldCamera` or change to `newCamera`. #### Related examples - TODO: Restrict map panning to an area, learn how to use this method - and `MLNMapCamera` objects to restrict a users ability to pan your map. + - */ - (BOOL)mapView:(MLNMapView *)mapView shouldChangeFromCamera:(MLNMapCamera *)oldCamera toCamera:(MLNMapCamera *)newCamera;