Slide 1

Slide 1 text

@@export_scripts@@ Accessible Maps and Navigation

Slide 2

Slide 2 text

@@export_scripts@@ Insert attention catching introduction here

Slide 3

Slide 3 text

@@export_scripts@@ Best practice #1

Slide 4

Slide 4 text

@@export_scripts@@ Challenge why you have a map in the first place

Slide 5

Slide 5 text

@@export_scripts@@ Example #1 Facebook Marketplace

Slide 6

Slide 6 text

@@export_scripts@@ Challenge what kind of map you need

Slide 7

Slide 7 text

@@export_scripts@@

Slide 8

Slide 8 text

@@export_scripts@@ Best practice #2

Slide 9

Slide 9 text

@@export_scripts@@ Let users choose to see your map

Slide 10

Slide 10 text

@@export_scripts@@ Example #2 Airbnb

Slide 11

Slide 11 text

@@export_scripts@@ Best practice #3

Slide 12

Slide 12 text

@@export_scripts@@ Expose your map details to assistive technology

Slide 13

Slide 13 text

@@export_scripts@@ MKAnnotation let annotation = MKPointAnnotation() annotation.title = "Title"

Slide 14

Slide 14 text

@@export_scripts@@ Map pin Map pin Map pin Map pin Map pin Map pin

Slide 15

Slide 15 text

@@export_scripts@@ MKAnnotationView let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "") annotationView.accessibilityLabel = "Label"

Slide 16

Slide 16 text

@@export_scripts@@ Google Maps SDK

Slide 17

Slide 17 text

@@export_scripts@@ Google Maps Markers let marker = GMSMarker() marker.title = "Title" marker.accessibilityLabel = "Label"

Slide 18

Slide 18 text

@@export_scripts@@ Google Maps gmsMapView.accessibilityElementsHidden = false

Slide 19

Slide 19 text

@@export_scripts@@ Best practice #4

Slide 20

Slide 20 text

@@export_scripts@@ Take care of the accessibility basics

Slide 21

Slide 21 text

@@export_scripts@@ An accessible map... supports portrait and landscape orientations supports light and dark mode doesn't override system gestures

Slide 22

Slide 22 text

@@export_scripts@@ An accessible annotation... has an accessibility label is big enough (44 x 44 px) allows dynamic type is localized scales glyphs as well as text combines text, images, and color to show meaning has enough contrast

Slide 23

Slide 23 text

@@export_scripts@@ Best practice #5

Slide 24

Slide 24 text

@@export_scripts@@ Simplify the user experience.

Slide 25

Slide 25 text

@@export_scripts@@ Custom Actions

Slide 26

Slide 26 text

@@export_scripts@@ UIAccessibilityCustomAction let name = "Toggle Favorite" let actionToggleFavorite = UIAccessibilityCustomAction(name: name) { // Do action return true|false }

Slide 27

Slide 27 text

@@export_scripts@@ accessibilityCustomActions let detailView: UIView detailView.accessibilityCustomActions = [ actionToggleFavorite, ... ]

Slide 28

Slide 28 text

@@export_scripts@@ Custom Actions let favoriteButton: UIButton favoriteButton .isAccessibilityElement = false

Slide 29

Slide 29 text

@@export_scripts@@ Custom Actions

Slide 30

Slide 30 text

@@export_scripts@@ VoiceOver Rotor

Slide 31

Slide 31 text

@@export_scripts@@ Rotor - Name let name = "Museums" let museumsRotor = UIAccessibilityCustomRotor(name: name) { ... }

Slide 32

Slide 32 text

@@export_scripts@@ Rotor - Current Index let museums: [MKAnnotationView] let museumsRotor = UIAccessibilityCustomRotor(name: name) { predicate in let currentElement = predicate .currentItem .targetElement as? MKAnnotationView let currentIndex = museums .firstIndex { $0 == currentElement } ... }

Slide 33

Slide 33 text

@@export_scripts@@ Rotor - Target Index ... let currentIndex = museums .firstIndex { $0 == currentElement } let targetIndex: Int switch predicate.searchDirection { case .previous: targetIndex = (currentIndex ?? 1) - 1 case .next: targetIndex = (currentIndex ?? -1) + 1 } guard 0..

Slide 34

Slide 34 text

@@export_scripts@@ Rotor - Rotor Item Result let museums: [MKAnnotationView] let targetIndex: Int ... return UIAccessibilityCustomRotorItemResult( targetElement: museums[targetIndex], targetRange: nil) ...

Slide 35

Slide 35 text

@@export_scripts@@ Rotor - All Together let museums: [MKAnnotationView] let name = "Museums" let museumsRotor = UIAccessibilityCustomRotor(name: name) { let currentElement = predicate .currentItem .targetElement as? MKAnnotationView let currentIndex = museums .firstIndex { $0 == currentElement } let targetIndex: Int switch predicate.searchDirection { case .previous: targetIndex = (currentIndex ?? 1) - 1 case .next: targetIndex = (currentIndex ?? -1) + 1 } guard 0..

Slide 36

Slide 36 text

@@export_scripts@@ Rotor - Map View // MapKit mkMapView.accessibilityCustomRotors = [museumsRotor] // Google Maps gmsMapView.accessibilityCustomRotors = [museumsRotor]

Slide 37

Slide 37 text

@@export_scripts@@ Categories

Slide 38

Slide 38 text

@@export_scripts@@ Best practice #6

Slide 39

Slide 39 text

@@export_scripts@@ Make it BIG

Slide 40

Slide 40 text

@@export_scripts@@ Best practice #7

Slide 41

Slide 41 text

@@export_scripts@@ Use wheelchair access information

Slide 42

Slide 42 text

@@export_scripts@@ Google Places API https://maps.googleapis.com /maps/api/place/details/json ?place_id=PLACE_ID &key=YOUR_API_KEY &fields=wheelchair_accessible_entrance

Slide 43

Slide 43 text

@@export_scripts@@ Best practice #8

Slide 44

Slide 44 text

@@export_scripts@@ Keep the layout clean and simple

Slide 45

Slide 45 text

@@export_scripts@@ Best practice #9

Slide 46

Slide 46 text

@@export_scripts@@ Help the user focus

Slide 47

Slide 47 text

@@export_scripts@@ setRegion let location = CLLocation( latitude: 48.852851, longitude: 2.349398) let radius = 500 // meters let region = MKCoordinateRegion( center: location.coordinate, latitudinalMeters: radius, longitudinalMeters: radius) mkMapView .setRegion(region, animated: true)

Slide 48

Slide 48 text

@@export_scripts@@ GMSCameraPosition let camera = GMSCameraPosition .camera( withLatitude: 48.852851, longitude: 2.349398, zoom: 16.0) let gmsMapView = GMSMapView.map( withFrame: self.view.frame, camera: camera)

Slide 49

Slide 49 text

@@export_scripts@@ setCameraBoundary let region = MKCoordinateRegion( center: location.coordinate, latitudinalMeters: 50000, longitudinalMeters: 60000) let boundary = CameraBoundary(coordinateRegion: region) mkMapView.setCameraBoundary( boundary, animated: true)

Slide 50

Slide 50 text

@@export_scripts@@ cameraTargetBounds let bounds = GMSCoordinateBounds(...) gmsMapView.cameraTargetBounds = bounds

Slide 51

Slide 51 text

@@export_scripts@@ pointOfInterestFilter // Show certain categories mkMapView.pointOfInterestFilter = MKPointOfInterestFilter( including: [ .restaurant, .cafe ] ) // Hide all mkMapView.pointOfInterestFilter = .excludingAll

Slide 52

Slide 52 text

@@export_scripts@@ Style.json [ { "featureType": "poi.business", "stylers": [ { "visibility": "off" } ] } ]

Slide 53

Slide 53 text

@@export_scripts@@ Style.json [ { "featureType": "poi", "stylers": [ { "visibility": "off" } ] } ]

Slide 54

Slide 54 text

@@export_scripts@@ GMSMapStyle let styleURL = Bundle.main.url(forResource: "style", withExtension: "json") let style = GMSMapStyle( contentsOfFileURL: styleURL) gmsMapView.mapStyle = style

Slide 55

Slide 55 text

@@export_scripts@@ Best practice #10

Slide 56

Slide 56 text

@@export_scripts@@ Make it easy to reset

Slide 57

Slide 57 text

@@export_scripts@@ Reset North-South // MapKit mkMapView.showsCompass = true // Google Maps gmsMapView.settings.compassButton = true

Slide 58

Slide 58 text

@@export_scripts@@ Reset to Current Location // Google Maps gmsMapView.settings.myLocationButton = true // MapKit let currentLocation: CLLocation let region = MKCoordinateRegion( center: currentLocation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500) mkMapView .setRegion(region, animated: true)

Slide 59

Slide 59 text

@@export_scripts@@ Navigation

Slide 60

Slide 60 text

@@export_scripts@@ Best practice #11

Slide 61

Slide 61 text

@@export_scripts@@ Give options for outsourcing

Slide 62

Slide 62 text

@@export_scripts@@ Google Maps // Base URL https://www.google.com/maps/dir/? api=1 // Parameters destination=48.85840,2.29449 travelmode=driving|walking|transit

Slide 63

Slide 63 text

@@export_scripts@@ Apple Maps MKMapItem.openInMaps(launchOptions:) // Key MKLaunchOptionsDirectionsModeKey // Values MKLaunchOptionsDirectionsModeDriving MKLaunchOptionsDirectionsModeWalking MKLaunchOptionsDirectionsModeTransit MKLaunchOptionsDirectionsModeDefault

Slide 64

Slide 64 text

@@export_scripts@@ Apple Maps // Base URL http://maps.apple.com/ // Parameters daddr=48.85840,2.29449 dirflg=d|w|r

Slide 65

Slide 65 text

@@export_scripts@@ Copy Paste let pasteboard = UIPasteboard.general pasteboard.string = "501 Main Street"

Slide 66

Slide 66 text

@@export_scripts@@ Best practice #12

Slide 67

Slide 67 text

@@export_scripts@@ Leverage all of the senses

Slide 68

Slide 68 text

@@export_scripts@@ Haptic Feedback let generator = UINotificationFeedbackGenerator() generator .notificationOccurred( .warning|.success|.error )

Slide 69

Slide 69 text

@@export_scripts@@ Audio Instructions AVFoundation AVSpeechSynthesizer AVSpeechUtterance

Slide 70

Slide 70 text

@@export_scripts@@ Spacial Audio Microsoft Soundscape github.com/microsoft/soundscape

Slide 71

Slide 71 text

@@export_scripts@@ Best practice #13

Slide 72

Slide 72 text

@@export_scripts@@ Personalise the navigation experience

Slide 73

Slide 73 text

@@export_scripts@@ "Think beyond the visual" - Marco Salsiccia, Deque Systems

Slide 74

Slide 74 text

@@export_scripts@@ Best practice #14

Slide 75

Slide 75 text

@@export_scripts@@ Provide list of turn-by-turn instructions

Slide 76

Slide 76 text

@@export_scripts@@

Slide 77

Slide 77 text

@@export_scripts@@ Best practice #15

Slide 78

Slide 78 text

@@export_scripts@@ Tell the user where they are

Slide 79

Slide 79 text

@@export_scripts@@ CLGeocoder let currentLocation = locationManager.requestLocation() let geocoder = CLGeocoder() geocoder .reverseGeocodeLocation(currentLocation) { (placemarks, error) in // placemarks: [CLPlacemark] }

Slide 80

Slide 80 text

@@export_scripts@@ Best practice #16

Slide 81

Slide 81 text

@@export_scripts@@ Let the user save their current location

Slide 82

Slide 82 text

@@export_scripts@@ Best practice #17

Slide 83

Slide 83 text

@@export_scripts@@ Test with real people who use assistive technology

Slide 84

Slide 84 text

@@export_scripts@@ Recap

Slide 85

Slide 85 text

@@export_scripts@@ Thank you!