Skip to content

Commit

Permalink
Improve map annotation display in the sample app (#345)
Browse files Browse the repository at this point in the history
* Improve the sample app point annotations

* Enable Cluster point annotations

* Set Map view border to not render the view overlapped by the tap bar
  • Loading branch information
kried authored Nov 18, 2024
1 parent 63d6e00 commit 41f18b1
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 52 deletions.
16 changes: 16 additions & 0 deletions MapboxSearch.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@
2C10133429F1C6200094413F /* PlaceAutocompleteIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C10133329F1C6200094413F /* PlaceAutocompleteIntegrationTests.swift */; };
2C18E9F429F0A83900FD96E6 /* PlaceAutocompleteSuggestionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C18E9F229F0A82E00FD96E6 /* PlaceAutocompleteSuggestionStub.swift */; };
2C705F062A137CEB00B8B773 /* SearchNavigationProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C705F052A137CEB00B8B773 /* SearchNavigationProfile.swift */; };
2C7FEBFA2CE78E6300B7ED22 /* PointAnnotation+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C7FEBF92CE78E6300B7ED22 /* PointAnnotation+Search.swift */; };
2C7FEBFC2CE7A62C00B7ED22 /* MapView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C7FEBFB2CE7A62C00B7ED22 /* MapView+Search.swift */; };
2CA1E22129F09CD200A533CF /* PlaceAutocomplete.Suggestion+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1E22029F09CD200A533CF /* PlaceAutocomplete.Suggestion+Tests.swift */; };
2CA1E22329F0A47600A533CF /* PlaceAutocompleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1E22229F0A47600A533CF /* PlaceAutocompleteTests.swift */; };
2CD6C03C29F1982100D865D1 /* EventsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CD6C03B29F1982100D865D1 /* EventsManagerTests.swift */; };
Expand Down Expand Up @@ -662,6 +664,8 @@
2C10133329F1C6200094413F /* PlaceAutocompleteIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceAutocompleteIntegrationTests.swift; sourceTree = "<group>"; };
2C18E9F229F0A82E00FD96E6 /* PlaceAutocompleteSuggestionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceAutocompleteSuggestionStub.swift; sourceTree = "<group>"; };
2C705F052A137CEB00B8B773 /* SearchNavigationProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchNavigationProfile.swift; sourceTree = "<group>"; };
2C7FEBF92CE78E6300B7ED22 /* PointAnnotation+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PointAnnotation+Search.swift"; sourceTree = "<group>"; };
2C7FEBFB2CE7A62C00B7ED22 /* MapView+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MapView+Search.swift"; sourceTree = "<group>"; };
2CA1E22029F09CD200A533CF /* PlaceAutocomplete.Suggestion+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlaceAutocomplete.Suggestion+Tests.swift"; sourceTree = "<group>"; };
2CA1E22229F0A47600A533CF /* PlaceAutocompleteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceAutocompleteTests.swift; sourceTree = "<group>"; };
2CD6C03B29F1982100D865D1 /* EventsManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsManagerTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1376,6 +1380,7 @@
1440BF4A28FD7576009B3679 /* Search UI */ = {
isa = PBXGroup;
children = (
2C7FEBF82CE78E4F00B7ED22 /* UIComponents */,
FEEDD3C62508E3FB00DC0A98 /* Resources */,
FEEDD3B52508E3CD00DC0A98 /* MapRootController.swift */,
04CECA002C3EE28B007117E4 /* ResultDetailViewController.swift */,
Expand Down Expand Up @@ -1565,6 +1570,15 @@
path = Navigation;
sourceTree = "<group>";
};
2C7FEBF82CE78E4F00B7ED22 /* UIComponents */ = {
isa = PBXGroup;
children = (
2C7FEBF92CE78E6300B7ED22 /* PointAnnotation+Search.swift */,
2C7FEBFB2CE7A62C00B7ED22 /* MapView+Search.swift */,
);
path = UIComponents;
sourceTree = "<group>";
};
2CD6C03A29F1980700D865D1 /* Telemetry */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2934,6 +2948,7 @@
FEEDD3C32508E3CD00DC0A98 /* AppDelegate.swift in Sources */,
14F7186D29A139BF00D5BC2E /* PlaceAutocompleteDetailsViewController.swift in Sources */,
04CECA152C3EFAB9007117E4 /* TextViewLoggerViewController.swift in Sources */,
2C7FEBFA2CE78E6300B7ED22 /* PointAnnotation+Search.swift in Sources */,
04CECA0D2C3EFAAF007117E4 /* SimpleUISearchViewController.swift in Sources */,
FEEDD3BF2508E3CD00DC0A98 /* MapRootController.swift in Sources */,
04CECA162C3EFAB9007117E4 /* ExamplesListing.swift in Sources */,
Expand All @@ -2942,6 +2957,7 @@
0498A7442CB486AE008F8903 /* ForwardExampleViewController.swift in Sources */,
04CECA132C3EFAB9007117E4 /* Examples.swift in Sources */,
04CECA092C3EFAAF007117E4 /* MapboxMapsCategoryResultsViewController.swift in Sources */,
2C7FEBFC2CE7A62C00B7ED22 /* MapView+Search.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
5 changes: 1 addition & 4 deletions Sources/Demo/AddressAutofillResultViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,7 @@ extension AddressAutofillResultViewController {

func showAnnotations(results: [AddressAutofill.Result], cameraShouldFollow: Bool = true) {
annotationsManager.annotations = results.compactMap {
var point = PointAnnotation(coordinate: $0.coordinate)
point.textField = $0.name
UIImage(named: "pin").map { point.image = .init(image: $0, name: "pin") }
return point
PointAnnotation.pointAnnotation($0)
}

if cameraShouldFollow {
Expand Down
11 changes: 6 additions & 5 deletions Sources/Demo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="JVX-IJ-mtG">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23090" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="JVX-IJ-mtG">
<device id="retina5_5" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23079"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
Expand Down Expand Up @@ -274,12 +274,13 @@
<constraint firstItem="GC2-d5-Go6" firstAttribute="leading" secondItem="KC9-Og-jfE" secondAttribute="leading" constant="20" id="3zk-gR-kzL"/>
<constraint firstItem="jyc-Lb-8Kz" firstAttribute="top" secondItem="KC9-Og-jfE" secondAttribute="top" constant="20" id="AVj-tU-GLu"/>
<constraint firstItem="jyc-Lb-8Kz" firstAttribute="leading" secondItem="KC9-Og-jfE" secondAttribute="leading" constant="20" id="BYc-A6-d1e"/>
<constraint firstItem="GC2-d5-Go6" firstAttribute="bottom" secondItem="KC9-Og-jfE" secondAttribute="bottom" constant="-16" id="GcV-L7-awi"/>
<constraint firstItem="KC9-Og-jfE" firstAttribute="bottom" secondItem="GC2-d5-Go6" secondAttribute="bottom" constant="16" id="GcV-L7-awi"/>
<constraint firstItem="KC9-Og-jfE" firstAttribute="trailing" secondItem="jyc-Lb-8Kz" secondAttribute="trailing" constant="20" id="k2d-U6-vqh"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="evC-4X-ARn"/>
<connections>
<outlet property="searchButton" destination="GC2-d5-Go6" id="ssR-bw-Vfs"/>
<outlet property="segmentedControl" destination="jyc-Lb-8Kz" id="raK-8M-n1j"/>
</connections>
</viewController>
Expand Down Expand Up @@ -401,10 +402,10 @@
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemRedColor">
<color red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="tertiarySystemGroupedBackgroundColor">
<color red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>
26 changes: 12 additions & 14 deletions Sources/Demo/DiscoverViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import UIKit
final class DiscoverViewController: UIViewController {
private var mapView = MapView(frame: .zero, mapInitOptions: defaultMapOptions)
@IBOutlet private var segmentedControl: UISegmentedControl!
@IBOutlet private var searchButton: UIButton!

private let category = Discover()
lazy var annotationsManager = mapView.annotations.makePointAnnotationManager()
lazy var annotationsManager = mapView.makeClusterPointAnnotationManager()

override func viewDidLoad() {
super.viewDidLoad()
Expand All @@ -21,7 +22,7 @@ final class DiscoverViewController: UIViewController {
mapView.topAnchor.constraint(equalTo: view.topAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
mapView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])

// Show user location
Expand Down Expand Up @@ -96,11 +97,18 @@ extension DiscoverViewController {
)
} else {
do {
let inset: CGFloat = 24
let insets = UIEdgeInsets(
top: inset + segmentedControl.frame.height,
left: inset,
bottom: inset + searchButton.frame.height,
right: inset
)
let cameraState = mapView.mapboxMap.cameraState
let coordinatesCamera = try mapView.mapboxMap.camera(
for: annotations.map(\.point.coordinates),
camera: CameraOptions(cameraState: cameraState),
coordinatesPadding: UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24),
coordinatesPadding: insets,
maxZoom: nil,
offset: nil
)
Expand All @@ -114,17 +122,7 @@ extension DiscoverViewController {

private func showCategoryResults(_ results: [Discover.Result], cameraShouldFollow: Bool = true) {
annotationsManager.annotations = results.map {
var point = PointAnnotation(coordinate: $0.coordinate)
point.textField = $0.name

/// Display a corresponding Maki icon for this Result when available
if let name = $0.makiIcon, let maki = Maki(rawValue: name) {
point.image = .init(image: maki.icon, name: maki.name)
point.iconOpacity = 0.6
point.iconAnchor = .bottom
}

return point
PointAnnotation.pointAnnotation($0)
}

if cameraShouldFollow {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Demo/Examples/MapsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import UIKit

class MapsViewController: UIViewController, ExampleController {
let mapView = MapView(frame: .zero)
lazy var annotationsManager = mapView.annotations.makePointAnnotationManager()
lazy var annotationsManager = mapView.makeClusterPointAnnotationManager()

override func viewDidLoad() {
super.viewDidLoad()
Expand Down
10 changes: 2 additions & 8 deletions Sources/Demo/MapRootController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MapRootController: UIViewController {
mapView.topAnchor.constraint(equalTo: view.topAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
mapView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])

// Show user location
Expand Down Expand Up @@ -48,13 +48,7 @@ class MapRootController: UIViewController {

func showAnnotations(results: [SearchResult], cameraShouldFollow: Bool = true) {
annotationsManager.annotations = results.map { result in
var point = PointAnnotation(coordinate: result.coordinate)
point.textField = result.name
UIImage(named: "pin").map {
point.iconAnchor = .bottom
point.textAnchor = .top
point.image = .init(image: $0, name: "pin")
}
var point = PointAnnotation.pointAnnotation(result)

// Present a detail view upon annotation tap
point.tapHandler = { [weak self] _ in
Expand Down
7 changes: 2 additions & 5 deletions Sources/Demo/Offline/OfflineDemoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import UIKit
/// Demonstrate how to use Offline Search in the Demo app
class OfflineDemoViewController: UIViewController {
private var mapView = MapView(frame: .zero)
lazy var annotationsManager = mapView.annotations.makePointAnnotationManager()
lazy var annotationsManager = mapView.makeClusterPointAnnotationManager()
private var messageLabel = UILabel()

private lazy var searchController = MapboxSearchController()
Expand Down Expand Up @@ -103,10 +103,7 @@ class OfflineDemoViewController: UIViewController {

func showAnnotations(results: [SearchResult], cameraShouldFollow: Bool = true) {
annotationsManager.annotations = results.map {
var point = PointAnnotation(coordinate: $0.coordinate)
point.textField = $0.name
UIImage(named: "pin").map { point.image = .init(image: $0, name: "pin") }
return point
PointAnnotation.pointAnnotation($0)
}

if cameraShouldFollow {
Expand Down
11 changes: 2 additions & 9 deletions Sources/Demo/PlaceAutocompleteDetailsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import UIKit
final class PlaceAutocompleteResultViewController: UIViewController {
@IBOutlet private var tableView: UITableView!
@IBOutlet private var mapView: MapView!
lazy var annotationsManager = mapView.annotations.makePointAnnotationManager()
lazy var annotationsManager = mapView.makeClusterPointAnnotationManager()

private var result: PlaceAutocomplete.Result!
private var resultComponents: [(name: String, value: String)] = []
Expand Down Expand Up @@ -39,14 +39,7 @@ final class PlaceAutocompleteResultViewController: UIViewController {

func showAnnotations(results: [PlaceAutocomplete.Result], cameraShouldFollow: Bool = true) {
annotationsManager.annotations = results.compactMap {
guard let coordinate = $0.coordinate else {
return nil
}

var point = PointAnnotation(coordinate: coordinate)
point.textField = $0.name
UIImage(named: "pin").map { point.image = .init(image: $0, name: "pin") }
return point
PointAnnotation.pointAnnotation($0)
}

if cameraShouldFollow {
Expand Down
9 changes: 3 additions & 6 deletions Sources/Demo/ResultDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,9 @@ class ResultDetailViewController: UIViewController {
])

/// Add annotations and set camera
let annotationsManager = mapView.annotations.makePointAnnotationManager()
annotationsManager.annotations = [result].map { result in
var point = PointAnnotation(coordinate: result.coordinate)
point.textField = result.name
UIImage(named: "pin").map { point.image = .init(image: $0, name: "pin") }
return point
let annotationsManager = mapView.makeClusterPointAnnotationManager()
annotationsManager.annotations = [result].map {
PointAnnotation.pointAnnotation($0)
}

if let annotation = annotationsManager.annotations.first {
Expand Down
15 changes: 15 additions & 0 deletions Sources/Demo/UIComponents/MapView+Search.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import MapboxMaps
import UIKit

extension MapView {
func makeClusterPointAnnotationManager(
duration: TimeInterval = 0.5
) -> PointAnnotationManager {
let manager = annotations.makePointAnnotationManager(clusterOptions: .init())
manager.onClusterTap = { [weak self] context in
let cameraOptions = CameraOptions(center: context.coordinate, zoom: context.expansionZoom)
self?.camera.ease(to: cameraOptions, duration: duration)
}
return manager
}
}
48 changes: 48 additions & 0 deletions Sources/Demo/UIComponents/PointAnnotation+Search.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import MapboxMaps
import MapboxSearch
import UIKit

extension PointAnnotation {
static func pointAnnotation(_ searchResult: SearchResult) -> Self {
Self.pointAnnotation(coordinate: searchResult.coordinate, name: searchResult.name)
}

static func pointAnnotation(_ searchResult: AddressAutofill.Result) -> Self {
Self.pointAnnotation(coordinate: searchResult.coordinate, name: searchResult.name)
}

static func pointAnnotation(_ searchResult: PlaceAutocomplete.Result) -> Self? {
guard let coordinate = searchResult.coordinate else { return nil }
return Self.pointAnnotation(coordinate: coordinate, name: searchResult.name)
}

static func pointAnnotation(_ searchResult: Discover.Result) -> Self {
var point = Self.pointAnnotation(coordinate: searchResult.coordinate, name: searchResult.name, imageName: nil)

/// Display a corresponding Maki icon for this Result when available
if let name = searchResult.makiIcon, let maki = Maki(rawValue: name) {
point.image = .init(image: maki.icon, name: maki.name)
point.iconOpacity = 0.6
point.iconAnchor = .bottom
point.textAnchor = .top
}
return point
}

static func pointAnnotation(
coordinate: CLLocationCoordinate2D,
name: String,
imageName: String? = "pin"
) -> Self {
var point = PointAnnotation(coordinate: coordinate)
point.textField = name
point.textHaloColor = .init(.white)
point.textHaloWidth = 10
if let imageName, let image = UIImage(named: "pin") {
point.iconAnchor = .bottom
point.textAnchor = .top
point.image = .init(image: image, name: "pin")
}
return point
}
}

0 comments on commit 41f18b1

Please sign in to comment.