Map view

Map Floorplan

Overview

Use SAP Fiori Map Floorplan to extend Apple’s MapKit and Esri’s ArcGIS frameworks. The floorplan supports native map APIs and adds additional components to enhance the map experience. Maps are built upon layers of geometries (points, polylines, and polygons). Floorplans provide foundation for functionality including, showing search results, geometry details, and geometry creation. Floorplans currently only support iPad devices.

Components

Floorplan Image

Display Geometries

Layers

The map view may consist of multiple display layers. Each layer is represented by an FUIGeometryLayer object, which has the displayName property. By assigning geometries to distinct layers, geometries can be organized by their business context and then consumed by the map display. The state or behavior of all geometries can then be easily controlled layer by layer. For example, developer can show or hide all geometries on a specified layer using setLayerHidden(_:hidden:) method.

An FUIGeometryLayer object can be initialized by setting its displayName. In the example App, there are three geometry layers defined: special zones, NYC ferry, and NYC MTA.

  enum Layer {
      static let zones = FUIGeometryLayer(displayName: "Special Zones")
      static let stops = FUIGeometryLayer(displayName: "NYC Ferry")
      static let mtaStops = FUIGeometryLayer(displayName: "NYC MTA")
  }

Display Objects

In the example App, we added annotations and overlays to the FUIMKMapView. By default FUIMKMapView has its own data source implementation and all added annotations and overlays will be automatically managed when calling reloadData().

  • Add annotation objects to the internally managed map data source

Developer can add annotations to the data source of FUIMKMapView directly by calling reloadData(). In such way, the added annotations will also be automatically appended to the corresponding geometry layer. Below code snippet demonstrates adding an array of stop annotations to the data source.

  var stopAnnotations: [MKPointAnnotation] = [] {
      didSet {
          reloadData()
      }
  }
  • Add overlay objects to the internally managed map data source

Likewise, developer can add overlays to the data source of FUIMKMapView directly by calling reloadData(). In such way, the added overlays will also be automatically appended to the corresponding geometry layer. Below code snippet demonstrates adding an array of zone overlays to the data source.

  var zonesOverlays: [MKPolygon] = [] {
      didSet {
          reloadData()
      }
  }
  • Working with native MKMapView instance methods

Developer may still use native MKMapView methods to manage annotations and overlays on the map view. Native methods like addAnnotation(_:), addOverlay(_:) works the same on FUIMKMapView. However, for annotation and overlay objects added without geometry layer information, they cannot be configured using methods specialized for wrapper display geometries.

Additionally, developer should manage the display of their own annotations and overlays objects. This can be achieved by implementing your own mapView:viewForAnnotation: and renderer(for:) delegate methods. Below code snippet demonstrates the implementation of custom annotation view for MTA stops.

  func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
      ...
      if mtaStopAnnotations.contains(pointAnnotation) {
          let view = mapView.dequeueReusableAnnotationView(withIdentifier: "FUIMarkerAnnotationView", for: annotation) as! FUIMarkerAnnotationView
          view.markerTintColor = UIColor.preferredFioriColor(forStyle: .map6)
          view.glyphImage = FUIIconLibrary.map.marker.bus
          view.clusteringIdentifier = nil
          return view
      } else if stopAnnotations.contains(pointAnnotation) {
          return nil
      } else if editAnnotations.contains(where: { return $0 as? MKPointAnnotation != nil }) {
          let view = mapView.dequeueReusableAnnotationView(withIdentifier: "FUIMarkerAnnotationView", for: annotation) as! FUIMarkerAnnotationView
          view.markerTintColor = UIColor.preferredFioriColor(forStyle: .map5)
          view.glyphImage = FUIIconLibrary.map.marker.venue
          view.clusteringIdentifier = nil
          return view
      }
      return nil
  }

Clustering

A clustering annotation groups two or more distinct annotations into a single entity. FUIMKMapView utilize the same mechanism from MapKit to generate clustered annotations. Depends on the global setting of property isClusteringEnabled on the floorplan view controller as well as the specific setting of property clusteringIdentifier for each annotation view, the map view automatically creates cluster annotations when two or more annotation views become grouped too closely together on the map surface.

In the example App, only stop annotations are set to enable clustering. For MTA stop annotations, clustering is disable. The screenshot below illustrates their different behavior.

Clustering Annotations

By default, clustering is enable for your map view controller once it is inherited from FUIMapFloorplanViewController. Therefore, in default state all annotations added to the map view will be clustered automatically depending on the zoom level. Two ways of customizations on specific annotation view can be made on the map view:

  • Determine if an annotation view should be clustered or not:

If clusteringIdentifier is set to nil, the annotation view will not be clustered. You may also define distinct clusteringIdentifier value for annotation views that should not be clustered. The settings of clusteringIdentifier is commonly done by implementing the delegate method mapView(_:viewFor:).

  public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
      ...
      guard let _ = annotation as? FUIAnnotation else {
          let view = mapView.dequeueReusableAnnotationView(withIdentifier: "FUIMarkerAnnotationView", for: annotation) as! FUIMarkerAnnotationView
          view.markerTintColor = UIColor.preferredFioriColor(forStyle: .map6)
          view.glyphImage = FUIIconLibrary.map.marker.bus
          view.clusteringIdentifier = nil
          return view
      }
      return nil
  }
  • Customize the cluster annotation:

To customize the cluster annotation for the specified set of annotations, implement the mapView(_:clusterAnnotationForMemberAnnotations:) method in your map’s delegate. This delegate will not be called if the clusteringIdentifier for the annotation view is set to nil.

Map Interactions

  1. Overlay Selection and Deselection

Native MapKit provides some handy property and methods to manage interaction with annotations: selectedAnnotations to get an array of selected annotations, selectAnnotation(_:animated:) to select the specified annotation and displays a callout view for it, and deselectAnnotation(_:animated:) to deselect the specified annotation and hides its callout view.

Likewise, FUIMKMapView provides equivalent property and methods to manage interaction with overlays:

selectedOverlays: get an array of selected overlays. selectOverlay(_:animated:): select the specified overlay and change its display to selected state. deselectOverlay(_:animated:): deselect the specified overlay and restore its display to default state.

  • Map view delegate setting

Before using the overlay selection functionality, developer should make sure their own map view controller has been sub-classed from FUIMKMapFloorplanViewController. Also developer should set map view’s delegate to their own map view delegate implementation:

  let mapDelegate = MKMapViewDelegateImpl()
  mapView.delegate = mapDelegate
  • Selection/Deselection behavior of overlay on map view

At this point, once the custom map view’s delegate is correctly set, the interaction with overlays through tap gesture is supported right out of box. Please note that at one time only one overlay object on map can be selected. Also if an annotation call out is highlighted, all other selected overlay reset to default state.

Overlay selection and deselection can also be implemented using the detail panel as below.

Overlay Selection

  • Link selection behavior of overlay to detail panel

Detail panel is implemented by a table view. So to define the selection behavior from the detail panel, developer has to implement the tableView(_didSelectRowAt:) method for the detail panel.

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      ...
      if let zone = feature.zone {
          self.pushContent(for: zone)
      }
      ...
  }

After that, the pushContent(for:) method needs to be implemented to trigger the selection behavior for the overlay. Developer needs to get selected overlay through table view index value, and then initiate selectOverlay(_:animated:) to enable selection state on the overlay.

  func pushContent(for zone: Feature<BetaNYC.Zone>) {
      ...  
      if let index = zonesFeatureCollection.features.firstIndex(where: { $0.properties.id == zone.properties.id }) {
          self.mapView.selectOverlay(zonesOverlays[index], animated: true)
      }
      ...
  }
  • Link deselection behavior of overlay to detail panel

Deselection behavior from detail panel may be implemented through the completion handler on the close button: detailPanel?.content.closeButton.didSelectHandler. Developer may get a list of selected overlays from the selectedOverlays property, and then initiate deselectOverlay(_:animated:) to unselect all.

  self.detailPanel?.content.closeButton.didSelectHandler = { [unowned self] _ in
      ...
      for selectedOverlay in self.mapView.selectedOverlays {
          self.mapView.deselectOverlay(selectedOverlay, animated: true)
      }
      ...
  }

Map Components

Default Toolbar

The toolbar contains buttons for common map functionalities. By default the toolbar has button for presenting settings, showing user location, presenting a map legend, and showing all annotations. The view is anchored to the right top right side of the map.

Default Toolbar

Settings

The settingsController is a UIViewController for the developer to set additional configurations for his/her application. The controller is presented modally as a formsheet over the map. Tapping on the close button will dismiss the controller and return to the map. In the example project, the settings acts as a placeholder and does not serve a functional purpose.

Settings Image

  • Settings Data Binding

Settings is set below in viewDidLoad():

// Components: Settings
self.settingsController = SettingsTableViewController.shared

User Location

This button zooms the map to center on the coordinates of the user’s location.

User Location On

Privacy settings must be changed in the Info.plist file to request permission to track location. Modify the file by adding the Privacy – Location When In Use Usage Description key. Check if permissions have been authorized in the viewDidAppear(_:) method.

Legend

The legend shows additional information about the MKAnnotationViews, MKPolylines, and MKPolygons that appear on the map. The legend is presented as a table in a UIPopoverPresentationController. The popover itself is anchored to the legend button within the toolbar. In the example, legend information about the Ferry Stops & Special Zones is provided.

Legend Image

  • Legend Items

The legend contains a list of FUIMapLegendItems. Items contain icon, line, or fillItem, to represent the MKAnnotationView, MKPolyline, or MKPolygon. In the example app the legend items are defined according to their layer. The icon and fillItem variants are shown.

 enum Layer {
    ...

     static var zonesLegendItem: FUIMapLegendItem = {
         var item = FUIMapLegendItem(title: Layer.zones.displayName)
         item.fillItem = FUIMapLegendFillItem()
         item.fillItem?.backgroundColor = UIColor.preferredFioriColor(forStyle: .map1)
         item.fillItem?.borderColor = UIColor.preferredFioriColor(forStyle: .map1)
         return item
     }()

     static var stopsLegendItem: FUIMapLegendItem = {
         var item = FUIMapLegendItem(title: Layer.stops.displayName)
         let image = FUIAttributedImage(image: FUIIconLibrary.map.marker.bus.withRenderingMode(.alwaysTemplate))
         image.tintColor = .white
         item.icon = FUIMapLegendIcon(glyphImage: image) //FUIMapLegendIcon(glyphImage: image)
         item.backgroundColor = UIColor.preferredFioriColor(forStyle: .map6)
         return item
     }()
 }
  • Legend Title

A title to the legend by accessing the headerTextView and settings it’s text. Titles are truncated after reaching the legend maximum width.

  • Legend passthrough views

Passthrough views allow for the legend to remain open while tapping separate views. By default both the detailPanel and toolbar are passthrough views. Add additional passthrough views by appending to the list,

  • Legend Data Binding

Legend configurations are set in viewDidLoad():

 // Components: Legend
 self.legend.headerTextView.text = "New York Legend"
 self.legend.items = [Layer.zonesLegendItem, Layer.stopsLegendItem]
 self.legend.passThroughViews.append(mapView)

Zoom Extents

This button zooms the map to the region containing all annotations.

Note

Zoom Extents does not include the user’s location

Zoom Extents Image

Detail Panel

The detailPanel is a view that shows searchResults of map features and additional details about map components. The panel resizes to fit its content based on the controllers preferredContentSize and is anchored to the top left of the map.

Search Results

The searchResults control allows the user to filter map information when typing in the searchbar.

Search Results Unfiltered

Search Results Filtered

  • Enable Search

Search Results is enabled by setting the isSearchEnabled Boolean value.

  • SearchResults tableView dataSource

Setting the dataSource to populates the searchResults tableView. The tableView can also be configured by registering cells, adding estimated row heights, and enabling automatic dimensions.

  • SearchResults tableView delegate

The developer can set the delegate to respond to events. In the example project, row selection will push the content controller. (See Content Section below)

  • SearchResults searchbardelegate

The developer can set the delegate to respond to events. In the example project, row selection will push the content controller. (See Content Section below)

  • SearchResults Data Binding

In the example project, the ViewController implements the UITableViewDataSource, UITableViewDelegate, and UISearchBarDelegate.

class ViewController: FUIMKMapFloorplanViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, EditingGeometryProviding {
  ...

  // MARK: UITableViewDataSource

  func numberOfSections(in tableView: UITableView) -> Int { ... }

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { ... }

  // MARK: UITableViewDelegate

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { ... }

  // MARK: UISearchBarDelegate

  func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { ... }

  func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { ... }

  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { ... }
}

SearchResults configurations are set in the viewDidLoad().

// Components: Detail Panel - SearchResults
self.detailPanel.isSearchEnabled = true
self.detailPanel.searchResults.tableView.dataSource = self
self.detailPanel.searchResults.tableView.delegate = self
self.detailPanel.searchResults.tableView.register(FUIObjectTableViewCell.self, forCellReuseIdentifier: FUIObjectTableViewCell.reuseIdentifier)
self.detailPanel.searchResults.tableView.estimatedRowHeight = 60
self.detailPanel.searchResults.tableView.rowHeight = UITableView.automaticDimension
self.detailPanel.searchResults.searchBar.delegate = self

Content

The content control presents additional information about a map component (MKAnnotationView, MKPolyline, MKPolygon, etc.). This controller is presented and dismissed by using the pushChildViewController() and popChildViewController() API respectively.

Content Image

  • Content Headline and Subheadline

The headlineText provides the title of the content and the subheadlineText provides additional title information. The headline is unscrollable and will always remain visible while the subheadline will be hidden while scrolling. A didSelectTitleHandler is that is called when the headlineText is tapped.

  • Content DataSource and Delegate

Configure the tableView and set the dataSource and delegate to the tableView similar to the searchResults.

  • Content Present

Calling pushChildViewController() will transition the content into view. It is up to the developer to determine when to present the content. In the example project, the controller is presented when a tableView row is selected. Update the controller properties and reloadData immediately prior to calling pushChildViewController().

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      ...
      self.pushContent(for: stop)
  }

  func pushContent(for stop: Feature<TransitLand.Stop>) {
      ...
      detailPanel.content.headlineText = stop.properties.name
      detailPanel.content.subheadlineText = stop.properties.operators.first?.values.first
      detailPanel.content.didSelectTitleHandler = {
          print(self.detailPanel.content.headlineText)
      }
      detailPanel.content.tableView.reloadData()
      detailPanel.pushChildViewController()
  }
Only one `content` can be displayed at a time.  To update a `contentViewController`, reload the table then resize the panel by calling the `fitToContent()` method.
  • Content Dismiss

Calling popChildViewController() dismisses the content and returns to searchResults.

This controller has a closeButton intended to dismiss itself. The developer can make additional changes prior to calling popChildViewController().

  • Content Data Binding

As stated in searchResults, example project’s ViewController already implements UITableViewDataSource and UITableViewDelegate

Initial content configurations are set in the viewDidLoad().

  // Components: Detail Panel - Content
  self.detailPanel.content.tableView.dataSource = self
  self.detailPanel.content.tableView.delegate = self
  self.detailPanel.content.tableView.register(FUIObjectTableViewCell.self, forCellReuseIdentifier: FUIObjectTableViewCell.reuseIdentifier)
  self.detailPanel.content.tableView.register(FUIMapDetailTagObjectTableViewCell.self, forCellReuseIdentifier: FUIMapDetailTagObjectTableViewCell.reuseIdentifier)
  self.detailPanel.content.tableView.estimatedRowHeight = 44
  self.detailPanel.content.tableView.rowHeight = UITableView.automaticDimension
  self.detailPanel.content.closeButton.didSelectHandler = { [unowned self] _ in
      ...
      self.detailPanel.popChildViewController()
  }

Editing

The floorplan provides an editing interface to add geometries. Users can draw directly onto the map or input addresses to create their shape (point, polyline, or polygon)

Editing Image

  • Set Editable Floorplan

To allow editing of the panel set the isEditable property to true.

  • Set Create Geometry Items

The developer chooses what types of items can be created and placed on the map. In the example project, the user can create items that appear in the legend.

Tapping the + right bar button presents a popover to select the create geometry item. Once selected, the view controller will transition to an editing state by setting all other annotations to disabled and uninteractable, showing editing toolbar buttons, and presenting the editing panel. In the example project, the item that can be created is an editLegendItem.

  • Set Creation Results Controller

This controller is available for the user to make additional changes to the workorder. In the example project, a placeholder controller is provided but does not have additional functionality.

  • Save the Geometry

The developer is responsible for updating the model with the returned geometry. Use the didSaveResults closure to update the model. The first argument is the editing shape and the second argument is the type of workorder created. This closure is called after tapping the save bar button item on the createGeometryResultsController.

The Floorplan sets geometry creation and editing in viewDidLoad().

// Components: Editing Panel
self.isEditable = true
self.editingPanel.createGeometryItems = [Layer.editLegendItem]
self.editingPanel.createGeometryResultsController = CreateGeometryResultsController(provider: self)
self.editingPanel.didSaveResults = { [unowned self] shape, createObject in
    if let point = shape as? MKPointAnnotation {
        self.editAnnotations.append(point)
    } else if let polyline = shape as? MKPolyline {
        self.editAnnotations.append(polyline)
    } else if let polygon = shape as? MKPolygon {
        self.editAnnotations.append(polygon)
    }
}

Editing Toolbar

The editing toolbar shows buttons to assist in geometry creation.

Editing Image

User Location Button

When tapped, the map will zoom to the user’s location. This button has the same functionality as the Default Toolbar User Location Button.

Zoom Extents Button

When tapped, the map will zoom to show all map annotations. This button has the same functionality as the Default Toolbar Zoom Extents Button.

Add Button

When selected, tapping on the map will add points to the map.

Delete Button

When selected, tapping on points of the current editing shape will delete the points.

Undo Button

Add and delete actions can be undone by tapping on the undo button.

Redo Button

Undo actions can be redone by tapping on the redo button.

Branch Button

To create a branch off of a polyline or polygon, select the branch button.

Note

The branch button is disabled when creating a point and when the delete button is selected.

Editing Panel

The Editing Panel shows editing configurations while creating a geometry. A user can select the type of geometry to create (point, polyline, or polygon) as well as add addresses directly to the shape.

Editing Image

Geometry Segmented Control

A user can switch between the geometries (point, polyline, or polygon) by tapping on the segment.

Note

Switching from polyline or polygon to point geometries will clear the stored points. The user will be prompted with an alert if they choose to continue.

Clear All Button

A user can clear all points by tapping the clear all button. The user will be prompted with an alert if they wish to continue.

Add New Point Field

This field allows the user to add addresses directly to the editing shape. Tapping the field will launch the keyboard.

Save Button

The save button will prepare to commit the changes and launch a create results controller. (See Save Geometries)

Drawing Interaction

Tap on the + button to open the create geometry popover. The example project allows the user to create “Edit Annotations”. Tap the cell to start.

edit annotation popover

Select the polyline image to create a polyline.

polyline segment selected

When the add button is selected points are added when the map is tapped

tap point 1

tap point 2

tap point 3

tap point 4

To add a point on a segment, tap directly on the line.

tap point on segment

To move the point, long press and drag on the point to a new location.

long press picture

To delete a point first select the delete button. The add button will become deselected.

delete button selected

Tap on a point to delete.

delete point

To undo the delete, tap the undo button.

tap undo

To redo the delete, tap the redo button.

tap redo

To add a branch select the add button,

select add button

then select the branch button.

select branch button

Select a point on the segment then tap away.

select new point

tap away from branch

Edit Panel Interaction

A user can directly add an address by tapping the Add New Point field. This launches the keyboard and shows suggestions.

show suggestions

Typing into the field provides suggestions for addresses.

show suggestions with entries

Selecting the cell adds the address to the model.

show added suggestion

Cells can be rearranged by dragging the cell with the hamburger icon.

show dragged cell

To delete a cell from the panel, tap the red circle to show the delete button.

show delete button

Accept the prompt to delete the point.

show delete prompt

Switching between geometries is possible by changing the segmented control. First a prompt will appear.

show change polygon prompt

Accept the prompt to change to polygon.

show polygon

Changing from a polyline or polygon to a point will delete all the points. This action cannot be undone!

show change geometry point

Points can be wiped by tapping the clear all button. This action cannot be undone!

show clear all alert

Cancel the alert and return to the editing line. Tap the save button in the panel to launch the create results page.

show create result page

The user can tap the cancel button to return back to editing. Tap the save bar button item to return back to the map and the editing geometry will be saved.

show geometry saved

  • The main view of FUIMapFloorplanViewController.

    Declaration

    Swift

    open class FUIMapFloorplanContentView : FUIBaseDrawingView, FUIMapFloorplanComponent
  • A generic map floorplan view controller that supports creating new geometry, editing geometry and save geometry. In general, you do not subclass this class directly. Use FUIMKMapFloorplanViewController(MapKit).

    Views Available in FUIMapFloorplanViewController:

    • detailPanel: Displays a resizing panel that holds the search controller, content controller, and edit controller. The panel resizes based on the presented controller preferredContentSize.

    • toolbar: A view that contains toolbar buttons for easy access to common map functionalities. Default toolbar buttons are provided.

    • legend: A view that shows mapAnnotations and their title.

    • settingsController: A controller presented as a formSheet over the map allowing for customization of the map.

    • createGeometryResultsController: A controller presented when a geometry is saved. Allows for additional configuration before committing the geometry.

    Variables Available in FUIMapFloorplanViewController:

    • isEditable: A Bool that prepares the floorplan for editing.

    • createGeometryItems: An array that displays work orders to create in editing mode. The contents are displayed in a table and on cell selection displays the editing panel.

    • isCreatePointEnabled: A Bool that enables editing of point geometries.

    • isCreatePolylineEnabled: A Bool that enables editing of polyline geometries.

    • isCreatePolygonEnabled: A Bool that enables editing of polygon geometries.

    Usage

    override func viewDidLoad() {
        detailPanel.search.tableView.dataSource = dataSource
        detailPanel.content.tableView.dataSource = dataSource
        legend.items = items
        settingsController = controller
        createGeometryResultsController = controller
    }
    

    Editing Interaction:

    The floorplan supports editing to allow users to add geometries (points, polylines, and polygons) by drawing directly on the map.

    A floorplan subclass can become editable by setting isEditable. The view controller will transition to an editing state by setting all other annotations to disabled, showing editing toolbar buttons, and presenting the editing panel. The + right bar button presents a popover allowing the user to select a geometry.

    Once the panel is presented, a user can toggle between geometry types (points, polylines, and polygons) to edit. The user can draw directly on the map by tapping on screen. The editing tool bar buttons show how the user can interact with the map. When the add Button is selected, the user can tap to add points. When the delete button is selected, a user can tap a point to delete. Actions can be undone and redone with the undo and redo buttons. Lastly the branch button can extend a polyline or polygon. To edit existing points the user can use the long press gesture to drag points to new locations.

    Alternatively, A user can edit existing points using the panel. The user can input directly an address and a list of suggestions will be presented. Tapping on a suggestion will add the point to the map. To delete, tap the red circle to show the red delete button. Tap delete to remove the point. Reordering points is done by dragging cells among each other.

    To save, tap the save button. This will launch the developer’s createGeometryResultsController where the user can make additional changes to the work order. Tapping the save right bar button dismisses the controller and exits the editing state. It is up to the developer to update his/her model with the saved geometry.

    See more

    Declaration

    Swift

    open class FUIMapFloorplanViewController<InnerView, EditingGeometryWrapper, EditingTypes> : FUIBaseDrawingViewController<InnerView>, FUIMapFloorplanComponent where InnerView : FUIMapFloorplanContentView, EditingGeometryWrapper : FUIManageRepresentation, EditingTypes : FUIGeometryTypeWrapper
  • A subclass of MKMapView is designed to be used with MapKit framework. You can use FUIMKMapView to display map information and to manipulate the map contents from your application. The FUIMKMapView class supports the ability to cache common map interface objects: annotations and overlays. Native MKMapView API methods are also available for developer use. It also adds the selection and deselection handlers for overlays.

    Usage

    When configuring your map interface with FUIMKMapView, all the added annotation and overlay objects and their corresponding display objects will be cached internally. Add annotation objects to the map view

    func addAnnotation(_ annotation: MKAnnotation, toLayer layer: FUIGeometryLayer) {}
    func addAnnotation(_ annotation: MKAnnotation, geometryLayer layer: FUIGeometryLayer) {}
    func addAnnotations(_ annotations: [MKAnnotation], geometryLayer layer: FUIGeometryLayer) {}
    

    Add overlay objects to the map view

    func addOverlays(_ overlays: [MKOverlay], level: MKOverlayLevel, geometryLayer layer: FUIGeometryLayer) {}
    func addOverlays(_ overlays: [MKOverlay], geometryLayer layer: FUIGeometryLayer) {}
    

    Respond to select/deselect overlay action

    func selectOverlay(_ overlay: MKOverlay, animated: Bool) {}
    func deselectOverlay(_ overlay: MKOverlay, animated: Bool) {}
    

    Theming

    See more

    Declaration

    Swift

    open class FUIMKMapView : MKMapView
  • Declaration

    Swift

    open class FUIMKMapFloorplanContentView : FUIMapFloorplanContentView
  • A subclass of FUIMapFloorplanViewController designed to be used with MapKit framework. You can use it to display business objects(points, polylines, polygons) on the map view and view details by selecting a business object. It also supports clustering of point geometries.

    Usage

    The map view consists of mutiple FUIGeometryLayers. By assigning geometries onto different layers, you can show/hide all geometries on a specified layer using setLayerHidden(_:hidden:) api.

    1. Implement FUIMKMapViewDataSource methods.

    In the floorplan controller viewDidLoad() set the datasource.

    self.dataSource = dataSource
    
    func numberOfLayers(in mapView: MKMapView) -> Int { return 1 }
    
    func mapView(_ mapView: MKMapView, layerAtIndex index: Int) -> FUIGeometryLayer { return FUIGeometryLayer("Functional Location") }
    
    func mapView(_ mapView: MKMapView, geometriesForLayer layer: FUIGeometryLayer) -> [MKAnnotation] { return polylines }
    

    You can also add annotations/overlays to a specified layer using FUIMKMapView’s addAnnotation(_:geometryLayer:) and addOverlays(_:geometryLayer:) APIs. Make sure you update you data model accordingly.

    Please Note: If you use MapKit’s native APIs to add annotations/overlays, the FUIMKMapViewDelegate object is not able to manage these objects.

    1. Implement MKMapViewDelegate methods as needed.

    Developer can supply custom annotation views. FUIMarkerAnnotationView is provided by the floorplan.

    mapView.delegate = delegate
    
    open func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let pointAnnotation = annotation as? MKPointAnnotation else { return nil }
        guard let view = FUIMarkerAnnotationView(annotation: pointAnnotation, reuseIdentifier: "cell") else { return nil }
        view.glyphImage = FUIIconLibrary.map.marker.venue.withRenderingMode(.alwaysTemplate)
        view.priorityIcon =  FUIIconLibrary.map.marker.veryHighPriority
    }
    
    1. FUIMKMapViewDelegate provides more functionalities that you can make your app more powerful.

    In the floorplan controller viewDidLoad() set the delegate.

    self.delegate = delegate
    

    Determine how annotation view/overlay renderer should appear. You can customize the view/render provided with fiori default styles.

    func mapView(_ mapView: MKMapView, willRender overlay: MKOverlayRenderer, for geometryIndex: Int, in layer: FUIGeometryLayer, in state: FUIMapFloorplan.State) {
        guard let polylineRenderer = overlayRenderer as? MKPolylineRenderer else { return }
        polylineRenderer.strokeColor = UIColor.blue
        polylineRenderer.setNeedsDisplay()
    }
    
    

    Respond to select/deselect action

    func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation, for geometryIndex: Int, in layer: FUIGeometryLayer) {}
    
    func mapView(_ mapView: MKMapView, didSelect clusterAnnotation: MKClusterAnnotation, for geometryIndexesInLayers: [FUIGeometryLayer: [Int]]) {}
    
    func mapView(_ mapView: MKMapView, didSelect overlay: MKOverlay, for geometryIndex: Int, in layer: FUIGeometryLayer) {}
    
    func mapView(_ mapView: MKMapView, didSelect overlayRenderer: MKOverlayRenderer) {}
    
    func mapView(_ mapView: MKMapView, didDeselect annotation: MKAnnotation, for geometryIndex: Int, in layer: FUIGeometryLayer) {}
    
    func mapView(_ mapView: MKMapView, didDeselect clusterAnnotation: MKClusterAnnotation, for geometryIndexesInLayers: [FUIGeometryLayer: [Int]]) {}
    
    func mapView(_ mapView: MKMapView, didDeselect overlay: MKOverlay, for geometryIndex: Int, in layer: FUIGeometryLayer) {}
    
    func mapView(_ mapView: MKMapView, didDeselect overlayRenderer: MKOverlayRenderer) {}
    

    Theming

    See more

    Declaration

    Swift

    open class FUIMKMapFloorplanViewController : FUIMapFloorplanViewController<FUIMKMapFloorplanContentView, MKEditingGeometryWrapper, MKEditingTypes>, EditingGeometryProducing
  • A View that wraps the FUIMapToolbarButton in a vertical stack.

    • The FUIMapToolbar holds a maximum of 6 FUIMapToolbarButton.
    • Default variants of FUIMapToolbarButton exist within the SDK. See, FUIMapToolbarSettingsButton, FUIMapToolbarUserLocationButton, FUIMapToolbarZoomExtentButton, or FUIMapToolbarLegendButton
    • The FUIMapToolbar can be presented in either a dark or light variant.
    • The FUIMapToolbar will be pinned to the given MKMapView in the top right corner

    Usage

    var toolbar = FUIMapToolbar(mapView: mapView)
    toolbar.backgroundColorScheme = .dark // defaults to `.light`
    let locationButton = FUIMapToolbarUserLocationButton(mapView: self.mapView)
    let zoomExtentsButton = FUIMapToolbarZoomExtentButton(mapView: self.mapView)
    toolbar.items = [locationButton, zoomExtentsButton]
    
    
    See more

    Declaration

    Swift

    open class FUIMapToolbar : UIView, FUIBackgroundSchemeSupporting
  • FUIMapToolbarButton inherits from the FUIButton class and appears within the FUIMapToolbar.

    • Height and width are both set to 44
    • Images should be set to 28 by 28 icons. Icons will be centered within the button.
    • didSelectHandler can be set to add custom functionality on tap

    Usage:

    let toolbar = FUIMapToolbar(mapView: self.mapView)
    let settingsButton = FUIMapToolbarButton()
    settingsButton.isPersistentSelection = true
    settingsButton.setImage(FUIIconLibrary.system.information.withRenderingMode(.alwaysTemplate), for: .normal)
    settingsButton.didSelectHandler = { [weak self] button in
       DispatchQueue.main.async {
           let settings = DevMapSettingsViewController()
           settings.dismissButton = button
    
           let navController = UINavigationController(rootViewController: settings)
           if !(UIDevice.current.userInterfaceIdiom == .phone) {
               settings.modalPresentationStyle = .formSheet
               navController.modalPresentationStyle = .formSheet
               self.present(navController, animated: true, completion: nil)
           } else {
               navController.modalPresentationStyle = .overFullScreen
               let dismissClosure: (() -> Void)? = { [weak self] in
                   self.dismiss(animated: true, completion: nil)
               }
               settings.dismissClosure = dismissClosure
               self.present(navController, animated: true, completion: nil)
           }
       }
    }
    toolbar.items = [settingsButton]
    

    Notes

    • Subclass the FUIMapToolbarButton for custom variants

    See

    See provided default variants: FUIMapToolbarSettingsButton, FUIMapToolbarUserLocationButton, FUIMapToolbarZoomExtentButton, and FUIMapToolbarLegendButton

    See

    See FUIMapToolbarSettingsButton documentation for supplementary DevMapSettingsViewController class
    See more

    Declaration

    Swift

    open class FUIMapToolbarButton : FUIButton
  • A UIViewController containing a tableView of FUIMapLegendItems. This controller is intended to be presented within a UIPopoverPresentationController. It calculates its preferred content size based on the tableView’s content height and tableView’s content width between a maximum and minimum value.

    Available in FUIMapLegend:

    See more

    Declaration

    Swift

    open class FUIResizablePopoverContainer : UIViewController
  • FUIMapLegend extends UIViewController to show the Map Legend.

    FUIMapLegend is presented in a UIPopoverView on iPad and presented in a UIPresentationController on iPhone

    Available in FUIMapLegend:

    • headerTextView: a TextView intended to display a footnote text.
    • passThroughViews: an array of UIView that a user can interact with while popover is visible

    Example Initialization and Configuration:

    In view controller’s viewDidLoad method:

    
    let toolbar = FUIMapToolbar(mapView: mapView)
    let legend = FUIMapLegend()
    legend.toolbarButton = FUIMapToolbarLegendButton()
    legend.headerTextView.text = "MapView Example Legend"
    legend.passThroughViews = [toolbar, mapView]
    
    var venueItem = FUIMapLegendItem(title: "Self Service")
    venueItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map1)
    let venueImage = FUIAttributedImage(image: FUIIconLibrary.map.marker.venue.withRenderingMode(.alwaysTemplate))
    venueImage.tintColor = .white
    venueItem.icon = FUIMapLegendIcon(glyphImage: venueImage)
    
    var valetItem = FUIMapLegendItem(title: "Valet")
    valetItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map2)
    let valetImage =  FUIAttributedImage(image: FUIIconLibrary.map.marker.walk.withRenderingMode(.alwaysTemplate))
    valetImage.tintColor = .white
    valetItem.icon = FUIMapLegendIcon(glyphImage: valetImage)
    valetItem.line = FUIMapLegendLine(dashPattern: [10,8], dashPhase: 3.0)
    
    let highActivityRegion = FUIMapLegendFillItem()
    highActivityRegion.fillColor = UIColor.preferredFioriColor(forStyle: .map3)
    highActivityRegion.borderColor = UIColor.preferredFioriColor(forStyle: .map3)
    var highActivityRegionItem = FUIMapLegendItem(title: "High Activity Region")
    highActivityRegionItem.fillItem = highActivityRegion
    
    var highTrafficPathItem = FUIMapLegendItem(title: "High Traffic Path")
    highTrafficPathItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map4)
    highTrafficPathItem.line = FUIMapLegendLine(dashPattern: [2,2], dashPhase: 0.0)
    
    legend.items = [highActivityRegionItem, valetItem, venueItem, highTrafficPathItem]
    
    let legendHandler: ((FUIButton) -> Void)? = { [weak self] button in
        DispatchQueue.main.async {
    
            var dismissLegendAndRestoreCachedController = { [unowned self, unowned button] in
               self.dismiss(animated: true, completion: {
               button.isSelected = false
               })
            }
    
            let presentNewLegendPhone = {
               self.present(legend, animated: true, completion: nil)
            }
    
            let presentNewLegendPad = {
               legend.setupPopoverAttributes(popOver: legend.popoverPresentationController!, sender: button)
               if self.presentedViewController == nil {
                   self.present(legend, animated: false, completion: nil)
               }
            }
    
            guard button.isSelected else {
               dismissLegendAndRestoreCachedController()
               return
            }
    
            let presentNewClosure = UIDevice.current.userInterfaceIdiom == .phone ? presentNewLegendPhone : presentNewLegendPad
    
            guard let presentedController = UIDevice.current.userInterfaceIdiom == .phone ? self.presentedViewController : self.popoverPresentationController?.presentedViewController else {
               presentNewClosure()
               return
            }
    
            if presentedController == legend {
               dismissLegendAndRestoreCachedController()
            } else {
               self.dismiss(animated: true, completion: {
                   presentNewClosure()
               })
            }
        }
    }
    legend.toolbarButton?.didSelectHandler = legendHandler
    let locationButton = FUIMapToolbarUserLocationButton(mapView: self.mapView)
    toolbar.items = [legend.toolbarButton!, locationButton]
    
    

    theming

    fdlFUIMapLegendContainer_headerTextView {
    font-color: @primary3;
    font-name: semiboldSystem;
    font-style: footnote;
    }
    

    notes

    See

    See implementation of FUIMapLegend using a FUIMapToolbarLegendButton for a complete implementation.
    See more

    Declaration

    Swift

    open class FUIMapLegend : FUIResizablePopoverContainer
    extension FUIMapLegend: UIViewControllerTransitioningDelegate
    extension FUIMapLegend: UITableViewDelegate
    extension FUIMapLegend: UITableViewDataSource
  • FUIMapLegendItem is a struct to represent an entry in the Map Legend. A FUIMapLegendItem can be one of 4 types:

    1. icon: An item with a circular icon
    2. iconLine: An item with a circular icon and line
    3. line: An item with a line
    4. fill: An item with a filled polygon

    Available:

    • title: a String that describes the FUIMapLegendItem

    • type: a FUIMapLegendItemType designated for the item.

    • backgroundColor: a UIColor displayed as a backgroundColor for the item

    • tintColor: a UIColor displayed as a tintColor for the item

    • icon: a FUIMapLegendIcon for the designated item.

    • line: a FUIMapLegendLine for the designated item.

    • fillItem: a FUIMapLegendFillItem for the designated item.

    Example Initialization and Configuration:

    
    // Type: .icon
    var venueItem = FUIMapLegendItem(title: "Self Service")
    venueItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map1)
    let venueImage = FUIAttributedImage(image: FUIIconLibrary.map.marker.venue.withRenderingMode(.alwaysTemplate))
    venueImage.tintColor = .white
    venueItem.icon = FUIMapLegendIcon(glyphImage: venueImage)
    
    var valetItem = FUIMapLegendItem(title: "Valet")
    valetItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map2)
    let valetImage =  FUIAttributedImage(image: FUIIconLibrary.map.marker.walk.withRenderingMode(.alwaysTemplate))
    valetImage.tintColor = .white
    valetItem.icon = FUIMapLegendIcon(glyphImage: valetImage)
    valetItem.line = FUIMapLegendLine(dashPattern: [10,8], dashPhase: 3.0)
    
    let highActivityRegion = FUIMapLegendFillItem()
    highActivityRegion.fillColor = UIColor.preferredFioriColor(forStyle: .map3)
    highActivityRegion.borderColor = UIColor.preferredFioriColor(forStyle: .map3)
    var highActivityRegionItem = FUIMapLegendItem(title: "High Activity Region")
    highActivityRegionItem.fillItem = highActivityRegion
    
    var highTrafficPathItem = FUIMapLegendItem(title: "High Traffic Path")
    highTrafficPathItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map4)
    highTrafficPathItem.line = FUIMapLegendLine(dashPattern: [2,2], dashPhase: 0.0)
    
    legend.items = [highActivityRegionItem, valetItem, venueItem, highTrafficPathItem]
    
    
    See more

    Declaration

    Swift

    public struct FUIMapLegendItem
  • FUIMapLegendIcon is a struct to represent an entry in the Map Legend that displays an item with a circular icon.

    Example Initialization and Configuration:

    
    var venueItem = FUIMapLegendItem(title: "Self Service")
    venueItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map1)
    let venueImage = FUIAttributedImage(image: FUIIconLibrary.map.marker.venue.withRenderingMode(.alwaysTemplate))
    venueImage.tintColor = .white
    venueItem.icon = FUIMapLegendIcon(glyphImage: venueImage)
    
    var valetItem = FUIMapLegendItem(title: "Valet")
    valetItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map2)
    let valetImage =  FUIAttributedImage(image: FUIIconLibrary.map.marker.walk.withRenderingMode(.alwaysTemplate))
    valetImage.tintColor = .white
    valetItem.icon = FUIMapLegendIcon(glyphImage: valetImage)
    valetItem.line = FUIMapLegendLine(dashPattern: [10,8], dashPhase: 3.0)
    
    legend.items = [venueItem, valetItem]
    
    
    See more

    Declaration

    Swift

    public struct FUIMapLegendIcon
  • FUIMapLegendFillItem is a UIView that presents a square view with a fill color and border color.

    Available in FUIMapLegendFillItem:

    • fillColor: a UIColor that is the fill color of the square view

    • borderColor: a UIColor that is the border color of the square view

    Example Initialization and Configuration:

    
    let highActivityRegion = FUIMapLegendFillItem()
    highActivityRegion.fillColor = UIColor.preferredFioriColor(forStyle: .map3)
    highActivityRegion.borderColor = UIColor.preferredFioriColor(forStyle: .map3)
    var highActivityRegionItem = FUIMapLegendItem(title: "High Activity Region")
    highActivityRegionItem.fillItem = highActivityRegion
    
    legend.items = [highActivityRegionItem]
    
    
    See more

    Declaration

    Swift

    public class FUIMapLegendFillItem : UIView
  • FUIMapLegendLine is a struct to represent the line in the Map Legend. The color of the line is determined by the backgroundColor set by the item.

    Available in FUIMapLegendLine:

    • dashPattern: an array of NSNumber objects specifying the lenghts of line segments

    • dashPhase: a CGFloat describing how far into the dash pattern the line starts

    Example Initialization and Configuration:

    
    var valetItem = FUIMapLegendItem(title: "Valet")
    valetItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map2)
    let valetImage =  FUIAttributedImage(image: FUIIconLibrary.map.marker.walk.withRenderingMode(.alwaysTemplate))
    valetImage.tintColor = .white
    valetItem.icon = FUIMapLegendIcon(glyphImage: valetImage)
    valetItem.line = FUIMapLegendLine(dashPattern: [10,8], dashPhase: 3.0)
    
    var highTrafficPathItem = FUIMapLegendItem(title: "High Traffic Path")
    highTrafficPathItem.backgroundColor = UIColor.preferredFioriColor(forStyle: .map4)
    highTrafficPathItem.line = FUIMapLegendLine(dashPattern: [2,2], dashPhase: 0.0)
    
    legend.items = [valetItem, highTrafficPathItem]
    
    
    See more

    Declaration

    Swift

    public struct FUIMapLegendLine
  • A View Controller component within the FUIMapDetailPanel as the content. This controller shows additional details of a business object. It is up to the developer to set the tableView dataSource and delegate. Typically not used directly by developer.

    Available:

    • headlineText: A String used as the header text inside the panel. The headline text can wrap up to two lines and truncates at the tail.
    • subheadlineText: A String used as the subheadline text inside the panel. The subheadline text gradually disappears while the tableView scrolls.
    • didSelectTitleHandler: An optional handler that can be set by the developer that is executed when the headlineText is tapped. If the didSelectTitleHandler is not nil, the headlineText text color is changed to show that it is tappable.

    Usage:

    let content: FUIMapDetailPanelContentViewController = FUIMapDetailPanelContentViewController()
    content.headlineText = "Headline Text"
    content.subheadlineText = "Subheadline Text"
    content.didSelectTitleHandler = {
       print("didSelectTitleHandler called!")
    }
    content.tableView.delegate = delegate
    content.tableView.dataSource = dataSource
    content.tableView.register(FUIMapDetailBaseObjectTableViewCell.self, forCellReuseIdentifier: FUIMapDetailBaseObjectTableViewCell.reuseIdentifier)
    content.tableView.estimatedRowHeight = 100
    content.tableView.rowHeight  = UITableView.automaticDimension
    
    See more

    Declaration

    Swift

    final public class FUIMapDetailPanelContentViewController : FUIMapDetailPanelViewController
  • A View Controller component used in the FUIMapDetailPanel. It is up to the developer to set the tableView dataSource and delegate. Typically not used directly by developer.

    Usage:

    let vc = FUIMapDetailPanelViewController()
    vc.tableView.delegate = delegate
    vc.tableView.dataSource = dataSource
    
    See more

    Declaration

    Swift

    public class FUIMapDetailPanelViewController : UIViewController, FUIMapDetailPanelProtocol
  • Searchbar component of FUIMapDetailPanelSearchResultsViewController. It is up to the developer to set the search delegate.

    Usage

    let searchBar: FUIMapDetailPanelSearchBar = FUIMapDetailPanelSearchBar()
    searchBar.delegate = delegate
    
    See more

    Declaration

    Swift

    public class FUIMapDetailPanelSearchBar : FUISearchBar
  • A View Controller component within the FUIMapDetailPanel as the searchResults. This controller shows a tableView with search results using a given searchBar. It is up to the developer to set the searchBar delegate, tableView datasource, and tableView delegate. Typically not used directly by developer.

    Available:

    • tableView: A UITableView that displays search results. Developer must set the tableView datasource and delegate.
    • searchBar: A FUIMapDetailPanelSearchBar that should filter data. Developer must set the searchBar delegate.
    • preferredContentSize: A CGSize that determines the size of the UIViewController. This variable drives the resizing capabilities of the view and must set correctly and properly updated.
    • isApplyingBlurBackground: A Bool that determines if the tableView will use its blurredBackground. Setting it to false will set the tableView to the UITableView default values. isApplyingBlurBackground default value is true.
    • searchBar: A FUIMapDetailPanelSearchBar used to filter data.

    Usage:

    let search = FUIMapDetailPanelSearchResultsViewController()
    search.isApplyingBlurBackground = true
    search.tableView.dataSource = tableViewDataSource
    search.tableView.delegate = tableViewDelegate
    search.tableView.register(FUIMapDetailTagObjectTableViewCell.self, forCellReuseIdentifier: FUIMapDetailTagObjectTableViewCell.reuseIdentifier)
    search.searchBar.delegate = searchBarDelegate
    
    See more

    Declaration

    Swift

    public class FUIMapDetailPanelSearchResultsViewController : FUIMapDetailPanelViewController
  • A View Component that wraps the FUIMapDetailPanelSearchResultsViewController as the searchResultsController and the FUIMapDetailPanelContentViewController as the content. Switching between the two view controllers should be driven by the pushChildViewController and popChildViewController methods in the developer’s mapView(_:didSelect:) and mapView(_:didDeselect:) methods.

    ## iPhone The searchResults and content will be presented on cards that can be swipe and panned to a bottom, middle, or top position.

    ## iPad The view is placed in the top left corner of the iPad and will dynamically resize based on the preferredContentSize. The view is pinned to the given pinMapView and will resize accordingly. The fitToContent method must be called when reloading the table view.

    ## Available:

    • passThroughViews: A [UIView] that contains the views that are interactable when the map legend is presented on iPad. This prevents the popover from being dismissed while interacting with views in this list (ex. a MKMapView).
    • isApplyingBlurBackground: A Bool that determines if the child views will have a blurred background.
    • isSearchEnabled: A Boolean value to instantiate search.
    • searchResults: A FUIMapDetailPanelSearchResultsViewController used for the search function. Manipulate its tableView to show search results. The developer must set the datasource and delegate methods.
    • content: A FUIMapDetailPanelContentViewController used for showing the details. The developer must set the datasource and delegate methods.

    ## Usage

     container = FUIMapDetailPanel(parentViewController: self, mapView: mapView)
     favoritesData = Array(sampleData[3..<sampleData.count])
    
     container.isSearchEnabled = true
     container.isApplyingBlurBackground = true
     container.search.tableView.dataSource = searchResultsDataSource
     container.search.tableView.delegate = searchResultsDelegate
     container.search.tableView.register(FUIObjectTableViewCell.self, forCellReuseIdentifier: FUIObjectTableViewCell.reuseIdentifier)
     container.search.searchBar.delegate = searchResultsDelegate
    
     container.content.headlineText = "VA Palo Alto Health Care Sys wraps to two lines..."
     container.content.didSelectTitleHandler = {
        print("Developer Select Handler Called!")
     }
     container.content.subheadlineText = "Medical Center"
     container.content.tableView.dataSource = contentDataSource
     container.content.tableView.delegate = contentDelegate
     container.content.tableView.register(FUIObjectTableViewCell.self, forCellReuseIdentifier: FUIObjectTableViewCell.reuseIdentifier)
     container.content.tableView.estimatedRowHeight = 100
     container.content.tableView.rowHeight = UITableView.automaticDimension
    
    

    Manage presenting the controller on iPhone in viewDidAppear(_:).

     DispatchQueue.main.async {
        self.container!.presentContainer()
     }
    

    Manage dismissing the controller on iPhone in viewWillDisappear(_:)

     self.presentedViewController?.dismiss(animated: false, completion: nil)
    

    Present the detailPanel by managing selecting and deselecting map annotations in mapView(_:didSelect:) and mapView(_:didDeselect:)

     open func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    
         let selectedAnnotation = view.annotation
    
         if selectedAnnotation is MKUserLocation {
            return
         }
    
        self.devUpdateDetailVC(annotation: selectedAnnotation!)
    
         DispatchQueue.main.async {
             container.pushChildViewController()
         }
    
    
         for annotation in mapView.annotations {
            if let annotation = annotation as? MKAnnotation, !annotation.isEqual(selectedAnnotation) {
                self.container.content.tableView.dataSource = newDataSource
                self.container.fitToContent()
                DispatchQueue.main.async {
                    self.container.pushChildViewController()
                }
                return
            }
        }
     }
    
     open func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
    
         let selectedAnnotation = view.annotation
    
         if selectedAnnotation is MKUserLocation {
            return
         }
    
         DispatchQueue.main.async {
    
             if self.mapView.selectedAnnotations.isEmpty {
                self.container.popChildViewController()
             } else {
                self.container.content.tableView.dataSource = newDataSource
                self.container.fitToContent()
                self.container.content.tableView.reloadData()
             }
         }
     }
    
    
    See more

    Declaration

    Swift

    open class FUIMapDetailPanel : UIView
  • A FUIMapDetailTagObjectTableViewCell that inherits from FUIMapDetailBaseObjectTableViewCell and FUITags. Add tags to the tableViewCell in the upper left corner above the subheadline text.

    Available:

    • tags: An array of String that will be wrapped in a bordered cell. These tags will go in the upper left corner of the cell.

    Initialization and Configuration

    In the viewDidLoad

    tableView.register(FUIMapDetailTagObjectTableViewCell.self, forCellReuseIdentifier: FUIMapDetailTagObjectTableViewCell.reuseIdentifier)
    

    In the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)

    let cell = tableView.dequeueReusableCell(withIdentifier: FUIMapDetailTagObjectTableViewCell.reuseIdentifier, for: indexPath) as! FUIMapDetailTagObjectTableViewCell
    cell.headlineText = "VA Palo Alto Health Care Sys wraps to two lines..."
    cell.headlineLabel.numberOfLines = 1
    cell.footnoteText = "Medical Center"
    cell.statusLabel.font = UIFont.preferredFioriFont(forTextStyle: .headline)
    
    See more

    Declaration

    Swift

    open class FUIMapDetailTagObjectTableViewCell : FUIBaseDrawingTableViewCell<FUIMapDetailTagObjectView>
  • Status view component of FUIMapDetailStatusTableViewCell

    Typically not used directly by developer.

    A view with left aligned text and an optional image. Images are left aligned and placed to the left of the text. Images are expected to be 16px by 16px.

    Usage

    let statusView = FUIMapDetailStatusView()
    statusView.statusImage = FUIIconLibrary.indicator.veryHighPriority
    statusView.status.text = "High"
    

    Theming

    nuiClass:

    fdlFUIMapDetailStatusView {}
    

    Supported TEXT class paths:

    fdlFUIMapDetailStatusView_status {}
    

    Supported TEXT properties:

    font-color: Color;
    font-style: UIFontTextStyle;
    

    Supported IMAGE class paths:

    fdlFUIMapDetailStatusView_statusImage {}
    

    Supported IMAGE properties:

    tint-color: Color;
    
    See more

    Declaration

    Swift

    open class FUIMapDetailStatusView : FUIDrawingView, FUIStatusImageComponent
  • An object that manages editing data in the detailPanel. The developer specifies the items a user can create and which geometries he/she can use. A controller is presented when saving a geometry to add additional changes to the work order. When the geometry is saved, it is up to the developer to update their model with editing geometry.

    Variables Available in FUIEditingPanel:

    • createGeometryItems: An array of FUIMapLegendItem that displays the types of work orders that can be created. The items are presented in a table and when selected presents the editing panel. The panel takes the title of the item and sets it as the headline. The icon and title text has a white background and a tint color of UIColor.preferredFioriColor(forStyle: .tintColorDark)

    • basemapTypes: An array of MKMapType a user can pick from during editing. MapType is returned to the original state after editing is completed. By default, the user can pick from .standard, .hybrid, .mutedStandard, and .satellite.

    • isCreatePointEnabled: A Bool that enables the creation of a point geometry. By default, point geometry creation is enabled. Select the point symbol on the segmented control within the editing panel to begin editing.

    • isCreatePolylineEnabled: A Bool that enables the creation of a polyline geometry. By default, polyline geometry creation is enabled. Select the polyline symbol on the segmented control within the editing panel to begin editing.

    • isCreatePolygonEnabled: A Bool that enables the creation of a polygon geometry. By default, polygon geometry creation is enabled. Select the polygon symbol on the segmented control within the editing panel to begin editing.

    • createGeometryResultsController: A view controller that is presented when an editing geometry is saved. Used to make additional changes to the work order before a save is committed. The controller is presented modally over the map. The saved geometry is handled by the developer in the didSaveGeometry method where the map model should be updated.

    • willShowCreateGeometryResultsController: A closure that provides custom behaviors before showing createGeometryResultsController. This closure will only be called if createGeometryResultsController is displayed in .push mode. It is up to the developer to implement this closure and define custom actions before entering the editing results screen.

    • didDismissGeometryResultsController: A closure that provides custom behaviors before dismissing createGeometryResultsController. This closure will only be called if createGeometryResultsController is displayed in .push mode. It is up to the developer to implement this closure and define custom actions before dismissing the editing results screen.

    • didCommitGeometryResults: A closure that provides the committed saved geometry. It is up to the developer to implement this closure and update his/her own model with the saved geometry.

    See more

    Declaration

    Swift

    open class FUIEditingPanel<GeometryType> where GeometryType : FUIGeometry
  • A protocol extending MKAnnotation for storing index and state info that will be used by FUIMapFloorplanViewController. The properties of this protocol are managed by FUIMapFloorplanViewController. Developer is not supposed to set these properties.

    See more

    Declaration

    Swift

    public protocol FUIAnnotation : MKAnnotation
  • The FUIMapMarkerAnnotationView inherits from the MKMarkerAnnotationView and is presented as an annotation on the MKMapView. It is used to distinguish between location types and set a select priority to individual markers.

    Note

    We disabled setter of zPosition in order to prevent it from being modified when setting displayPriority. Set stickyZPosition instead.

    ## Available:

    • priorityIcon: a 17x17 icon image in the upper right corner of the marker. This can appear in both the selected and unselected state.

    ## Example Initialization and Configuration

     @available(iOS 11.0, *)
     class MyMarker: FUIMarkerAnnotationView {
         override var annotation: MKAnnotation? {
             willSet {
                 markerTintColor = UIColor.preferredFioriColor(forStyle: .map1)
                 glyphImage = FUIIconLibrary.map.marker.venue.withRenderingMode(.alwaysTemplate)
                 displayPriority = .defaultLow
                 priorityIcon =  FUIIconLibrary.map.marker.veryHighPriority
             }
         }
     }
    
    

    Register within the viewDidLoad()

     if #available(iOS 11.0, *) {
        mapView.register(MyMarker.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
     } else {
        // Fallback on earlier versions
     }
     let point1 = MKPointAnnotation()
     point1.coordinate = CLLocationCoordinate2D(latitude: 37.3318, longitude: -122.0312)
     let point2 = MKPointAnnotation()
     point2.coordinate = CLLocationCoordinate2D(latitude: 37.3988313, longitude: -122.1487375)
     let annotations = [point1 as MKAnnotation, point2 as MKAnnotation]
     mapView.addAnnotations(annotations)
    

    Set the annotation view in the mapView(_:viewFor:) method.

    var view: MKAnnotationView!
    if let pointAnnotation = annotation as? MKPointAnnotation {
        if #available(iOS 11.0, *) {
            view = FUIMarkerAnnotationView(annotation: pointAnnotation, reuseIdentifier: "cell")
            let annotationImage = FUIIconLibrary.map.marker.venue
            (view as! FUIMarkerAnnotationView).glyphImage = annotationImage.withRenderingMode(.alwaysTemplate)
            (view as! FUIMarkerAnnotationView).priorityIcon =  FUIIconLibrary.map.marker.veryHighPriority
        } else {
            // Fallback on earlier versions
            view = MKPinAnnotationView(annotation: pointAnnotation, reuseIdentifier: "cell")
        }
    }
    return view
    

    ## Note:

    • Set the glyphImage to a 20x20 icon.
    See more

    Declaration

    Swift

    @available(iOS 11.0, *)
    open class FUIMarkerAnnotationView : MKMarkerAnnotationView
  • The FUIProfileMarkerAnnotationView inherits from the MKMarkerAnnotationView and is presented as an annotation on the MKMapView. It is used to distinguish between static points on a map and person location. The annotation can either display a profilePicture or a set of initials to represent the person being annotated.

    Example Initialization and Configuration

    @available(iOS 11.0, *)
    class MyProfileMarker: FUIProfileMarkerAnnotationView {
       override var annotation: MKAnnotation? {
           willSet {
               markerTintColor = UIColor.preferredFioriColor(forStyle: .map1)
               glyphImage = FUIIconLibrary.map.marker.venue.withRenderingMode(.alwaysTemplate)
           }
       }
    }
    
    

    Register within the viewDidLoad()

    if #available(iOS 11.0, *) {
       mapView.register(MyProfileMarker.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
    } else {
       // Fallback on earlier versions
    }
    let point1 = MKPointAnnotation()
    point1.coordinate = CLLocationCoordinate2D(latitude: 37.3318, longitude: -122.0312)
    let point2 = MKPointAnnotation()
    point2.coordinate = CLLocationCoordinate2D(latitude: 37.3988313, longitude: -122.1487375)
    let annotations = [point1 as MKAnnotation, point2 as MKAnnotation]
    mapView.addAnnotations(annotations)
    mapView.showAnnotations(annotations, animated: true)
    

    Set the annotation view in the mapView(_:viewFor:) method.

    var view: MKAnnotationView!
    if let pointAnnotation = annotation as? MKPointAnnotation {
       if #available(iOS 11.0, *) {
           if pointAnnotation.coordinate.latitude == 37.3318 {
               view = FUIProfileMarkerAnnotationView(annotation: pointAnnotation, reuseIdentifier: "cell")
               (view as! FUIProfileMarkerAnnotationView).glyphText =  "ME"
               return view
           } else {
               view = FUIProfileMarkerAnnotationView(annotation: pointAnnotation, reuseIdentifier: "cell")
               (view as! FUIProfileMarkerAnnotationView).glyphImage = #imageLiteral(resourceName: "ProfilePic").withRenderingMode(.alwaysOriginal)
               (view as! FUIProfileMarkerAnnotationView).selectedGlyphImage = #imageLiteral(resourceName: "ProfilePic").withRenderingMode(.alwaysOriginal)
               return view
           }
       } else {
           // Fallback on earlier versions
           return nil
       }
    }
    return view
    

    Note:

    • Set the profileImage to a 24x24 icon.
    See more

    Declaration

    Swift

    @available(iOS 11.0, *)
    open class FUIProfileMarkerAnnotationView : MKMarkerAnnotationView
  • The FUICircleAnnotationView inherits from the MKAnnotationView and is presented as a circle annotation on the MKMapView. It is used to display annotation objects which have similar display effect as MKCircle object.

    Note

    We disabled setter of zPosition to prevent it from being modified when setting displayPriority. Set stickyZPosition instead.

    Example

    1. Add annotation objects to map let point1 = MKPointAnnotation() point1.coordinate = CLLocationCoordinate2D(latitude: 37.3318, longitude: -122.0312) let point2 = MKPointAnnotation() point2.coordinate = CLLocationCoordinate2D(latitude: 37.3988313, longitude: -122.1487375) let annotations = [point1 as MKAnnotation, point2 as MKAnnotation] mapView.addAnnotations(annotations)
    2. Set the circle annotation view in the mapView(_:viewFor:) method.
    let reuseIdentifier = "DefaultCircleAnnotationView"
    let view = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? FUICircleAnnotationView
    ?? FUICircleAnnotationView(annotation: editAnnotation, reuseIdentifier: reuseIdentifier, clusteringIdentifier: nil)
    view.outerColor = UIColor.preferredFioriColor(forStyle: .primary1)
    view.outerWidth = 2.0
    view.innerColor = UIColor.preferredFioriColor(forStyle: .tintColorTapState)
    view.circleRadius = 6.0
    return view
    
    See more

    Declaration

    Swift

    open class FUICircleAnnotationView : MKAnnotationView
  • A protocol extending FUIAnnotation and MKOverlay for storing extra info that will be used by FUIMapFloorplanViewController. The properties of this protocol are managed by FUIMapFloorplanViewController. Developer is not supposed to set these properties.

    See more

    Declaration

    Swift

    public protocol FUIOverlay : MKOverlay, FUIAnnotation
  • Geometry protocol for FUIMapView.

    Declaration

    Swift

    public protocol FUIGeometry : Hashable
  • A protocol adopted by an object that plays as the data model of FUIMKMapFloorplanViewController.

    See more

    Declaration

    Swift

    public protocol FUIMKMapViewDataSource : AnyObject
  • A protocol adopted by an object which plays as the delegate of FUIMKMapFloorplanViewController.

    See more

    Declaration

    Swift

    public protocol FUIMKMapViewDelegate : AnyObject
  • Component protocol for Map Floorplan properties

    See more

    Declaration

    Swift

    public protocol FUIMapFloorplanComponent
  • Component protocol for FUIMapView.

    Declaration

    Swift

    public protocol FUIMapView
  • Map point geometry protocol conforms to FUIGeometry.

    Declaration

    Swift

    public protocol FUIPoint : FUIGeometry
  • Map polygon geometry protocol conforms to FUIGeometry.

    Declaration

    Swift

    public protocol FUIPolygon : FUIGeometry
  • Map polyline geometry protocol conforms to FUIGeometry.

    Declaration

    Swift

    public protocol FUIPolyline : FUIGeometry
  • Geometry layer object adds Hashable functionality and other properties.

    See more

    Declaration

    Swift

    public struct FUIGeometryLayer : Hashable
  • Generic map Floorplan class for both MapKit and EsriMap.

    See more

    Declaration

    Swift

    public struct FUIMapFloorplan