Skip to content

Navigation UI#43

Merged
frederoni merged 52 commits into
masterfrom
fred-ui
Feb 24, 2017
Merged

Navigation UI#43
frederoni merged 52 commits into
masterfrom
fred-ui

Conversation

@frederoni
Copy link
Copy Markdown
Contributor

@frederoni frederoni commented Feb 3, 2017

First draft of the Nav UI

simulator screen shot 8 feb 2017 14 12 46

  • Customize UI via appearance proxy or similar
  • UI Tests
  • Examples
  • Storyboard support
  • Write documentation

@frederoni frederoni self-assigned this Feb 3, 2017
@bsudekum
Copy link
Copy Markdown
Contributor

bsudekum commented Feb 3, 2017

@frederoni can we merge the UI example with the Example in place now? Also, let's see if we can remove the duplicate Geomtry.swift.

Comment thread UI/README.md Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does looks like this is a duplicate?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

voyage

Copy link
Copy Markdown
Contributor Author

@frederoni frederoni Feb 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted these plists because they should be included in the app/example and not the SDK.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include any translations?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed them temporarily while cleaning up but I think we should keep the translations in here.

@frederoni frederoni force-pushed the fred-ui branch 9 times, most recently from aeb4131 to 09c7d9f Compare February 10, 2017 15:59
@bsudekum
Copy link
Copy Markdown
Contributor

@frederoni what do you think about just including this in core instead of requiring an additional pod to install?

@frederoni
Copy link
Copy Markdown
Contributor Author

frederoni commented Feb 13, 2017

@bsudekum It'd be convenient not having to maintain the libs separately and we can only version one of them as long as they live in the same repo, unless we publish.

That being said, I still think we should keep them separate. Navigation UI has dependencies we don't want to enforce on all navigation users. Navigation.swift can technically be used on ⌚️ while Navigation UI is📱 only.

@bsudekum
Copy link
Copy Markdown
Contributor

@frederoni ah that makes sense, I'll review this today.

var currentManeuverArrowPolylines: [ArrowFillPolyline] = []
var currentManeuverArrowStrokePolylines: [ArrowFillPolyline] = []
let distanceFormatter = DistanceFormatter(approximate: true)
let secondsBeforeResetTrackingMode:TimeInterval = 25.0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should pull this out into a constant


if let camera = pendingCamera {
camera.altitude = 1_000
camera.pitch = 45
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a developer sends through a camera with a custom pitch and altitude, we should honor these values.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default camera with pitch 45 and 1000m but not overriding if pendingCamera != nil.

}

let imageName = imageNamePattern.replacingOccurrences(of: " ", with: "_").replacingOccurrences(of: "{ref}", with: number)
let apiURL = URL(string: "https://commons.wikimedia.org/w/api.php?action=query&format=json&maxage=86400&prop=imageinfo&titles=File%3A\(imageName)&iiprop=url%7Csize&iiurlheight=\(Int(round(height)))")!
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@1ec5 you think it's safe to ship this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the public Wikimedia Commons API. The maxage parameter should keep repeated requests from being expensive, but we should ensure that we’re calling the API only as frequently as necessary.

}

func giveLocalNotification(_ step: RouteStep) {
if UIApplication.shared.applicationState == .background {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make notifications an option.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added sendNotifications: Bool

// Generated by PaintCode (www.paintcodeapp.com)
//


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check in the paint code project?

class ArrowStrokePolyline: ArrowFillPolyline {}

struct RouteControllerNotification {
static let didReceiveNewRoute = Notification.Name("RouteControllerDidReceiveNewRoute")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should either be removed or added where other notifications are defined

Copy link
Copy Markdown
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not having taken a look at this PR until now, it’s a bit overwhelming to review the code closely. So I started by reviewing the example view controllers for symptoms of architectural and ergonomic problems, then did a spot check over some of the remaining files. A more thorough review will take a bit more time.

Aside from the comments below, the main README.md should refer to the UI/ folder, so developers know to look there.

Comment thread UI/README.md Outdated
| --- | --- |

### Examples
We provide examples in Swift and Objective-C. Run `pod install` from the Example folder and open `Example.xcworkspace` to try it out.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI it’s now possible to obtain the map SDK via Carthage: mapbox/mapbox-gl-native#1623 (comment).

Comment thread Example/Swift/ViewController.swift Outdated
// Pass through a
// 1. the route the user will take
// 2. A `Directions` class, used for rerouting.
let controller = NavigationUI.instantiate(route: route, directions: directions)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what “a navigation UI” is and how this object fits into an MVC architecture? It seems to be a factory for RouteViewController objects. Why not initialize a RouteViewController directly?

Copy link
Copy Markdown
Contributor Author

@frederoni frederoni Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I first added the NavigationUI to handle the theme only, but because of the way Pulley is set up with designated initializer which I can't override conveniently, a factory method was the most appropriate I could come up with. The terminology is inherited from

UIStoryboard.instantiateInitialViewController()
UIStoryboard.instantiateViewController(withIdentifier:)

That being said, I agree with you, I'd prefer an init method on RouteViewController. The problem is supporting initializing it programmatically and through storyboard without using a factory method. I guess I could fork Pulley although the intention is to replace it eventually.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UIStoryboard performs a very different role than NavigationUI. The initial view controller is essentially a singleton, whereas RouteViewController is not. If we stick with a factory method, we should name it something like routeViewController(for:).

Comment thread Example/Swift/ViewController.swift Outdated
// Pass through a
// 1. the route the user will take
// 2. A `Directions` class, used for rerouting.
let controller = NavigationUI.instantiate(route: route, directions: directions)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controller is a local variable that isn’t referenced anywhere else – does that mean the object gets deinitialized immediately after creation? If not, ViewController should hang onto it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controller is actually a view controller that gets pushed into the navigation stack which hangs onto it.

Comment thread Example/Swift/ViewController.swift Outdated
// Pass through a
// 1. the route the user will take
// 2. A `Directions` class, used for rerouting.
let controller = NavigationUI.instantiate(route: route, directions: directions)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift and Objective-C favor the term “init” over the term “instantiate”.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread Example/Swift/ViewController.swift Outdated
let controller = NavigationUI.instantiate(route: route, directions: directions)
present(controller, animated: true, completion: nil)

controller.willDismissNavigationHandler = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forcing RouteViewController to own callbacks makes it all too easy for a developer to write a reference cycle that leaks memory. The two most common patterns for “handlers” are:

  • Subclassing. If the application wants to customize the dismissal experience, it can subclass RouteViewController to override dismiss(animated:completion:) and viewDidDisappear(_:).
  • Delegation. Add a RouteViewControllerDelegate protocol that ViewController can conform to by implementing routeViewControllerWillDismiss(_:) and routeViewControllerDidDismiss(_:).

Also, is there anything we can do to facilitate the use of an unwind segue to dismiss RouteViewController all the way to a starting point?

Comment thread UI/MapboxNavigationUI/Placemark.swift Outdated

let distanceFormatter = DistanceFormatter()

extension MapboxGeocoder.GeocodedPlacemark {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a conflict that would force us to qualify GeocodedPlacemark with the MapboxGeocoder module here?

Comment thread UI/MapboxNavigationUI/Placemark.swift Outdated
// TODO: Show country if not the current country.
// Cut off country and postal code and add abbreviated state/region code at the end.

let stitle = lines.prefix(upTo: 2).joined(separator: NSLocalizedString("ADDRESS_LINE_SEPARATOR", value: ", ", comment: "Delimiter between lines in an address when displayed inline"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple references to localized strings, but this PR doesn’t include a .strings file.

return string(for: obj, markUpWithSSML: false)
}

func string(for obj: Any?, markUpWithSSML: Bool) -> String? {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This SSML markup code is unused. I take it it’s for the Polly integration being added in #42.

}
}

class OSRMInstructionFormatter: Formatter {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I pointed out in #42 (review), I think it’s very important for the Swift port of OSRMTextInstructions to live independently of the Mapbox navigation SDK. The instructions included in this PR are also out of date compared to what’s in Transifex that could be transformed via the existing script to plist format.

let number = components[1]
if var imageName = ShieldImageNamesByPrefix[network] {
imageName = imageName.replacingOccurrences(of: " ", with: "_").replacingOccurrences(of: "{ref}", with: number)
let url = URL(string: "https://commons.wikimedia.org/w/thumb.php?f=\(imageName)&w=\(imageView.bounds.width * UIScreen.main.scale)")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frankly, our use of thumb.php is more problematic than our use of the Wikimedia Commons API. One problem is that thumb.php has no maxage parameter like the API does. Does SDWebImage do a good job of avoiding duplicate requests for the same thumbnail image as the user scrolls a particular row in and out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It will only request once per url in this case. Even after restarting the process, it will not make the request again but instead fetch it from disk. maxage defaults to one week.

@frederoni frederoni merged commit d7138c0 into master Feb 24, 2017
@friedbunny friedbunny deleted the fred-ui branch March 1, 2017 02:39
wishtrip-dev pushed a commit to wishtrip-dev/mapbox-navigation-ios that referenced this pull request Feb 28, 2018
Rewrote the library to more closely parallel mapbox/MapboxGeocoder.swift#41 and mapbox/MapboxStatic.swift#19. Removed the class prefix from Swift but kept it for Objective-C. The goal is no longer to be a (poor) drop-in replacement for MapKit’s MKDirections API but rather to be as idiomatic as possible in Swift and Objective-C, with a priority on Swift. Symbols conform to platform naming conventions but in some cases eschew MapKit terminology in favor of Geocoding API terminology, so that api-documentation makes sense in the context of this library.

Removed the dependency on RequestKit. This library chooses reasonable default networking behavior; if you need anything more sophisticated, you can access the URL request information to use with a custom NSURLSession. Directions no longer needs to be strongly held in order for the request to finish. Instead, the request is made against the shared URL session; to use a custom URL session, make the request yourself using the URL returned by the URLForCalculatingDirections(options:) method. Removed the cancel() method; instead, directly cancel the NSURLSessionDataTask returned by calculateDirections(options:completionHandler:).

Added a shared (singleton) Directions object. Use the shared object if you’ve set your Mapbox access token in the MGLMapboxAccessToken key of your application’s Info.plist file. Otherwise, create a Directions object with the access token explicitly. A single directions object can handle multiple requests concurrently (since it just passes the requests off to the shared URL session).

Eliminated the separate APIs for calculating ETAs. Instead, all the options supported by the Directions API, including flags for including steps and the overview geometry, are now exposed through a new RouteOptions class. Effectively, this change adds support for the geometries, overview, radiuses, steps, and continue_straight query parameters and allows the heading accuracy of any waypoint to be customized. However, note that steps are no longer returned by default, and the overview geometry is simplified by default. The access_token parameter is now the last URL parameter instead of the first. Broke out MBDirectionsRequest.TransportType into three profile identifier constants. You always specify a profile identifier, not a transport type. That makes the desired mode of transportation more open-ended.

Removed the MBDirectionsResponse class in favor of passing the waypoints and routes from the response directly into the completion handler. Renamed Route.geometry to Route.coordinates. For Objective-C compatibility, all enums are backed by integer types instead of String, but they’re CustomStringConvertible to allow for the same expressiveness we’re used to in Swift.

Error handling is much more robust now. Various error conditions returned by the API cause the localized failure reason and recovery suggestion to be set in the NSError object that is passed into the completion handler.

Updated test fixtures and expectations for the v5 server-side API and the revamped client-side API, and updated examples.

Fixes mapbox#41, fixes mapbox#43, fixes mapbox#44.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants