-
Notifications
You must be signed in to change notification settings - Fork 318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Visual Instruction Presentation Customization #1530
Changes from all commits
38a2442
ebc036c
b128e67
eb39237
f397d9c
876505f
90e8012
10c9ec5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ import Mapbox | |
The `NavigationViewControllerDelegate` provides methods for configuring the map view shown by a `NavigationViewController` and responding to the cancellation of a navigation session. | ||
*/ | ||
@objc(MBNavigationViewControllerDelegate) | ||
public protocol NavigationViewControllerDelegate { | ||
public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { | ||
/** | ||
Called when the navigation view controller is dismissed, such as when the user ends a trip. | ||
|
||
|
@@ -538,6 +538,10 @@ extension NavigationViewController: RouteMapViewControllerDelegate { | |
} | ||
return roadName | ||
} | ||
|
||
@objc public func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious, why does NavigationViewController need to implement the delegate method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just plumbing the delegate method through the view-controller hierarchy, just like we do here for a It's not really accurate to say that the |
||
return delegate?.label?(label, willPresent: instruction, as: presented) | ||
} | ||
} | ||
|
||
//MARK: - RouteControllerDelegate | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,9 @@ func makeVisualInstruction(_ maneuverType: ManeuverType = .arrive, | |
|
||
class InstructionsBannerViewIntegrationTests: XCTestCase { | ||
|
||
private lazy var reverseDelegate = TextReversingDelegate() | ||
private lazy var silentDelegate = DefaultBehaviorDelegate() | ||
|
||
lazy var imageRepository: ImageRepository = { | ||
let repo = ImageRepository.shared | ||
repo.sessionConfiguration = URLSessionConfiguration.default | ||
|
@@ -45,6 +48,8 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { | |
VisualInstructionComponent(type: .text, text: "Ankh-Morpork Highway 1", imageURL: nil, abbreviation: nil, abbreviationPriority: NSNotFound) | ||
] | ||
|
||
lazy var typicalInstruction: VisualInstructionBanner = makeVisualInstruction(primaryInstruction: [VisualInstructionComponent(type: .text, text: "Main Street", imageURL: nil, abbreviation: "Main St", abbreviationPriority: 0)], secondaryInstruction: nil) | ||
|
||
private func resetImageCache() { | ||
let semaphore = DispatchSemaphore(value: 0) | ||
imageRepository.resetImageCache { | ||
|
@@ -69,6 +74,27 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { | |
|
||
super.tearDown() | ||
} | ||
|
||
func testCustomVisualInstructionDelegate() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add a test for returning nil. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
let view = instructionsView() | ||
view.instructionDelegate = reverseDelegate | ||
|
||
view.set(typicalInstruction) | ||
|
||
XCTAssert(view.primaryLabel.attributedText?.string == "teertS niaM") | ||
|
||
} | ||
|
||
func testCustomDelegateReturningNilTriggersDefaultBehavior() { | ||
let view = instructionsView() | ||
view.instructionDelegate = silentDelegate | ||
|
||
view.set(typicalInstruction) | ||
|
||
XCTAssert(view.primaryLabel.attributedText?.string == "Main Street") | ||
|
||
} | ||
|
||
|
||
func testDelimiterIsShownWhenShieldsNotLoaded() { | ||
let view = instructionsView() | ||
|
@@ -291,3 +317,19 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { | |
} | ||
|
||
} | ||
|
||
private class TextReversingDelegate: VisualInstructionDelegate { | ||
func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { | ||
let forwards = Array(presented.string) | ||
let reverse = String(forwards.reversed()) | ||
var range = NSRange(location: 0, length: presented.string.count) | ||
let attributes = presented.attributes(at: 0, effectiveRange: &range) | ||
return NSAttributedString(string: reverse, attributes: attributes) | ||
} | ||
} | ||
|
||
private class DefaultBehaviorDelegate: VisualInstructionDelegate { | ||
func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { | ||
return nil | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With
VoiceController(_:WillSpeak:Instruction:)
, returning nil omits the voice instruction. I'm guess that's not the case here? In reality, I can't really think of a use case where a developer would want to show nothing though.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's kind of the idea. The utility of "I only want to override default behavior under specific circumstances" seems to outweigh the utility of "it's nice to say 'show nothing' via
nil
instead ofNSAttributedString()
". In this specific example you could also implement default behavior by returningpresented
, but I think we should get into the general habit of using a "returning nil means the delegate has no answer to the delegator, and thus default behavior should be used" pattern in our delegation, as otherwise you kind of force the developer to implement logic if they implement the optional delegate function. Not a super great pattern.