diff --git a/MapboxCoreNavigation/EndOfRouteFeedback.swift b/MapboxCoreNavigation/EndOfRouteFeedback.swift deleted file mode 100644 index 54c3e5cb..00000000 --- a/MapboxCoreNavigation/EndOfRouteFeedback.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -/** - Feedback Model Object for End Of Route Experience. - */ -@objc open class EndOfRouteFeedback: NSObject { - /** - Rating: The user's rating for the route. Normalized between 0 and 100. - */ - let rating: Int? - - /** - Comment: Any comments that the user had about the route. - */ - let comment: String? - - @nonobjc public init(rating: Int? = nil, comment: String? = nil) { - self.rating = rating - self.comment = comment - super.init() - } - - @objc public convenience init(rating ratingNumber: NSNumber?, comment: String?) { - let rating = ratingNumber?.intValue - self.init(rating: rating, comment: comment) - } -} diff --git a/MapboxNavigation/DayStyle.swift b/MapboxNavigation/DayStyle.swift index d0f6dc55..b8113ef9 100644 --- a/MapboxNavigation/DayStyle.swift +++ b/MapboxNavigation/DayStyle.swift @@ -129,8 +129,6 @@ open class DayStyle: Style { PrimaryLabel.appearance(whenContainedInInstancesOf: [InstructionsBannerView.self]).normalTextColor = #colorLiteral(red: 0.09803921569, green: 0.09803921569, blue: 0.09803921569, alpha: 1) PrimaryLabel.appearance(whenContainedInInstancesOf: [StepInstructionsView.self]).normalTextColor = #colorLiteral(red: 0.09803921569, green: 0.09803921569, blue: 0.09803921569, alpha: 1) ProgressBar.appearance().barColor = #colorLiteral(red: 0.149, green: 0.239, blue: 0.341, alpha: 1) - RatingControl.appearance().normalColor = #colorLiteral(red: 0.8508961797, green: 0.8510394692, blue: 0.850877285, alpha: 1) - RatingControl.appearance().selectedColor = #colorLiteral(red: 0.1205472574, green: 0.2422055006, blue: 0.3489340544, alpha: 1) ReportButton.appearance().backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) ReportButton.appearance().textColor = tintColor! ReportButton.appearance().textFont = UIFont.systemFont(ofSize: 15, weight: .medium).adjustedFont @@ -189,8 +187,6 @@ open class NightStyle: DayStyle { DistanceLabel.appearance(whenContainedInInstancesOf: [StepInstructionsView.self]).valueTextColor = #colorLiteral(red: 0.9842069745, green: 0.9843751788, blue: 0.9841964841, alpha: 1) DistanceRemainingLabel.appearance().normalTextColor = #colorLiteral(red: 0.7991961837, green: 0.8232284188, blue: 0.8481693864, alpha: 1) EndOfRouteButton.appearance().textColor = .white - EndOfRouteCommentView.appearance().backgroundColor = #colorLiteral(red: 0.1875049942, green: 0.2981707989, blue: 0.4181857639, alpha: 1) - EndOfRouteCommentView.appearance().normalTextColor = .white EndOfRouteContentView.appearance().backgroundColor = backgroundColor EndOfRouteStaticLabel.appearance().alpha = 1.0 EndOfRouteStaticLabel.appearance().textColor = UIColor.white.withAlphaComponent(0.9) @@ -216,8 +212,6 @@ open class NightStyle: DayStyle { PrimaryLabel.appearance(whenContainedInInstancesOf: [InstructionsBannerView.self]).normalTextColor = #colorLiteral(red: 0.9996390939, green: 1, blue: 0.9997561574, alpha: 1) PrimaryLabel.appearance(whenContainedInInstancesOf: [StepInstructionsView.self]).normalTextColor = #colorLiteral(red: 0.9996390939, green: 1, blue: 0.9997561574, alpha: 1) ProgressBar.appearance().barColor = #colorLiteral(red: 0.9842069745, green: 0.9843751788, blue: 0.9841964841, alpha: 1) - RatingControl.appearance().normalColor = #colorLiteral(red: 0.149668334, green: 0.1680230035, blue: 0.1472480238, alpha: 1) - RatingControl.appearance().selectedColor = #colorLiteral(red: 0.9803059896, green: 0.9978019022, blue: 1, alpha: 1) ReportButton.appearance().backgroundColor = backgroundColor ReportButton.appearance().textColor = #colorLiteral(red: 0.9842069745, green: 0.9843751788, blue: 0.9841964841, alpha: 1) ResumeButton.appearance().backgroundColor = backgroundColor diff --git a/MapboxNavigation/EndOfRouteViewController.swift b/MapboxNavigation/EndOfRouteViewController.swift index 92ba0a0a..ab30a551 100644 --- a/MapboxNavigation/EndOfRouteViewController.swift +++ b/MapboxNavigation/EndOfRouteViewController.swift @@ -23,43 +23,60 @@ open class EndOfRouteTitleLabel: StylableLabel {} @objc(MBEndOfRouteStaticLabel) open class EndOfRouteStaticLabel: StylableLabel {} -/// :nodoc: -@objc(MBEndOfRouteCommentView) -open class EndOfRouteCommentView: StylableTextView {} - /// :nodoc: @objc(MBEndOfRouteButton) open class EndOfRouteButton: StylableButton {} @objc(MBEndOfRouteViewController) class EndOfRouteViewController: UIViewController { - // MARK: - IBOutlets - - @IBOutlet var labelContainer: UIView! - @IBOutlet var staticYouHaveArrived: EndOfRouteStaticLabel! - @IBOutlet var primary: UILabel! - @IBOutlet var endNavigationButton: UIButton! - @IBOutlet var stars: RatingControl! - @IBOutlet var commentView: UITextView! - @IBOutlet var commentViewContainer: UIView! - @IBOutlet var showCommentView: NSLayoutConstraint! - @IBOutlet var hideCommentView: NSLayoutConstraint! - @IBOutlet var ratingCommentsSpacing: NSLayoutConstraint! - // MARK: - Properties - lazy var placeholder: String = NSLocalizedString("END_OF_ROUTE_TITLE", bundle: .mapboxNavigation, value: "How can we improve?", comment: "Comment Placeholder Text") - lazy var endNavigation: String = NSLocalizedString("END_NAVIGATION", bundle: .mapboxNavigation, value: "End Navigation", comment: "End Navigation Button Text") - - typealias DismissHandler = (Int, String?) -> Void - var dismissHandler: DismissHandler? - var comment: String? - var rating: Int = 0 { - didSet { - self.rating == 0 ? self.hideComments() : self.showComments() - } - } - + lazy var column: UIStackView = { + let spacerView = UIView() + spacerView.setContentHuggingPriority(.defaultLow, for: .vertical) + spacerView.setContentCompressionResistancePriority(.defaultLow, for: .vertical) + let column = UIStackView(arrangedSubviews: [self.staticYouHaveArrived, self.primaryLabel, spacerView, self.endNavigationButton]) + column.axis = .vertical + column.alignment = .center + column.spacing = 8 + column.translatesAutoresizingMaskIntoConstraints = false + return column + }() + + lazy var staticYouHaveArrived: EndOfRouteStaticLabel = { + let label = EndOfRouteStaticLabel() + label.text = self.endOfRouteArrivedText + return label + }() + + lazy var primaryLabel: EndOfRouteTitleLabel = { + let label = EndOfRouteTitleLabel() + label.numberOfLines = 3 + label.adjustsFontSizeToFitWidth = true + return label + }() + + lazy var endNavigationButton: EndOfRouteButton = { + let button = EndOfRouteButton(type: .system) + button.setTitle(self.endNavigationText, for: .normal) + button.addTarget(self, action: #selector(self.endNavigationPressed(_:)), for: .touchUpInside) + return button + }() + + lazy var endOfRouteArrivedText: String = NSLocalizedString("END_OF_ROUTE_ARRIVED", bundle: .mapboxNavigation, value: "You have arrived", comment: "Title used for arrival") + lazy var endNavigationText: String = NSLocalizedString("END_NAVIGATION", bundle: .mapboxNavigation, value: "End Navigation", comment: "End Navigation Button Text") + + let dismissHandler: () -> Void + init(dismissHandler: @escaping () -> Void) { + self.dismissHandler = dismissHandler + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + open var destination: Waypoint? { didSet { guard isViewLoaded else { return } @@ -69,86 +86,32 @@ class EndOfRouteViewController: UIViewController { // MARK: - Lifecycle Methods - override func viewDidLoad() { - super.viewDidLoad() - self.clearInterface() - self.stars.didChangeRating = { [weak self] new in self?.rating = new } - self.setPlaceholderText() - self.styleCommentView() - self.commentViewContainer.alpha = 0.0 // setting initial hidden state + override func loadView() { + self.view = EndOfRouteContentView() + self.view.addSubview(self.column) + self.activateLayoutConstraints() } override func viewWillAppear(_ animated: Bool) { - super.viewWillDisappear(animated) - view.roundCorners([.topLeft, .topRight]) + super.viewWillAppear(animated) preferredContentSize.height = self.height(for: .normal) self.updateInterface() } - // MARK: - IBActions + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + // roundCorners needs to be called whenever the bounds change + view.roundCorners([.topLeft, .topRight]) + } - @IBAction func endNavigationPressed(_ sender: Any) { - self.dismissView() + // MARK: - Actions + + @objc func endNavigationPressed(_ sender: Any) { + self.dismissHandler() } // MARK: - Private Functions - private func styleCommentView() { - self.commentView.layer.cornerRadius = 6.0 - self.commentView.layer.borderColor = UIColor.lightGray.cgColor - self.commentView.layer.borderWidth = 1.0 - self.commentView.textContainerInset = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) - } - - fileprivate func dismissView() { - let dismissal: () -> Void = { self.dismissHandler?(self.rating, self.comment) } - guard self.commentView.isFirstResponder else { return _ = dismissal() } - self.commentView.resignFirstResponder() - let fireTime = DispatchTime.now() + 0.3 // Not ideal, but works for now - DispatchQueue.main.asyncAfter(deadline: fireTime, execute: dismissal) - } - - private func showComments(animated: Bool = true) { - self.showCommentView.isActive = true - self.hideCommentView.isActive = false - self.ratingCommentsSpacing.constant = ConstraintSpacing.closer.rawValue - preferredContentSize.height = self.height(for: .commentShowing) - - let animate = { - self.view.layoutIfNeeded() - self.commentViewContainer.alpha = 1.0 - self.labelContainer.alpha = 0.0 - } - - let completion: (Bool) -> Void = { _ in self.labelContainer.isHidden = true } - let noAnimate = { animate(); completion(true) } - if animated { - UIView.animate(withDuration: 0.3, animations: animate, completion: nil) - } else { - noAnimate() - } - } - - private func hideComments(animated: Bool = true) { - self.labelContainer.isHidden = false - self.showCommentView.isActive = false - self.hideCommentView.isActive = true - self.ratingCommentsSpacing.constant = ConstraintSpacing.further.rawValue - preferredContentSize.height = self.height(for: .normal) - - let animate = { - self.view.layoutIfNeeded() - self.commentViewContainer.alpha = 0.0 - self.labelContainer.alpha = 1.0 - } - - let completion: (Bool) -> Void = { _ in self.commentViewContainer.isHidden = true } - let noAnimation = { animate(); completion(true) } - if animated { - UIView.animate(withDuration: 0.3, animations: animate, completion: nil) - } else { noAnimation() } - } - private func height(for height: ContainerHeight) -> CGFloat { let window = UIApplication.shared.keyWindow let bottomMargin = window!.safeAreaInsets.bottom @@ -156,50 +119,24 @@ class EndOfRouteViewController: UIViewController { } private func updateInterface() { - guard let name = destination?.name?.nonEmptyString else { return self.styleForUnnamedDestination() } - self.primary.text = name - } - - private func clearInterface() { - self.primary.text = nil - self.stars.rating = 0 - } - - private func styleForUnnamedDestination() { - self.staticYouHaveArrived.alpha = 0.0 - self.primary.text = NSLocalizedString("END_OF_ROUTE_ARRIVED", bundle: .mapboxNavigation, value: "You have arrived", comment: "Title used for arrival") - } - - private func setPlaceholderText() { - self.commentView.text = self.placeholder - } -} - -// MARK: - UITextViewDelegate - -extension EndOfRouteViewController: UITextViewDelegate { - func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - guard text.count == 1, text.rangeOfCharacter(from: CharacterSet.newlines) != nil else { return true } - textView.resignFirstResponder() - return false - } - - func textViewDidChange(_ textView: UITextView) { - self.comment = textView.text // Bind data model - } - - func textViewDidBeginEditing(_ textView: UITextView) { - if textView.text == self.placeholder { - textView.text = nil - textView.alpha = 1.0 - } - textView.becomeFirstResponder() - } - - func textViewDidEndEditing(_ textView: UITextView) { - if (textView.text?.isEmpty ?? true) == true { - textView.text = self.placeholder - textView.alpha = 0.9 + guard let name = destination?.name?.nonEmptyString else { + self.staticYouHaveArrived.alpha = 0.0 + self.primaryLabel.text = self.endOfRouteArrivedText + return } + self.staticYouHaveArrived.alpha = 1.0 + self.primaryLabel.text = name + } + + private func activateLayoutConstraints() { + NSLayoutConstraint.activate([ + self.column.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), + self.column.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -8), + self.column.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + self.column.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), + self.endNavigationButton.heightAnchor.constraint(equalToConstant: 60), + self.endNavigationButton.leadingAnchor.constraint(equalTo: self.column.leadingAnchor), + self.endNavigationButton.trailingAnchor.constraint(equalTo: self.column.trailingAnchor) + ]) } } diff --git a/MapboxNavigation/RatingControl.swift b/MapboxNavigation/RatingControl.swift deleted file mode 100644 index 12560faf..00000000 --- a/MapboxNavigation/RatingControl.swift +++ /dev/null @@ -1,136 +0,0 @@ -import CoreGraphics -import UIKit - -typealias RatingClosure = (Int) -> Void // rating - -/*@IBDesignable*/ -class RatingControl: UIStackView { - // MARK: Constants - - static let defaultSize = CGSize(width: 32.0, height: 32.0) - private let starTemplate = UIImage(named: "star", in: .mapboxNavigation, compatibleWith: nil) - - // MARK: Properties - - private var stars = [UIButton]() - - var didChangeRating: RatingClosure? - - var rating: Int = 0 { - didSet { - self.updateSelectionStates() - self.didChangeRating?(self.rating) - } - } - - @objc public dynamic var selectedColor: UIColor = #colorLiteral(red: 0.1205472574, green: 0.2422055006, blue: 0.3489340544, alpha: 1) { - didSet { - updateSelectionStates() - } - } - - @objc public dynamic var normalColor: UIColor = #colorLiteral(red: 0.8508961797, green: 0.8510394692, blue: 0.850877285, alpha: 1) { - didSet { - updateSelectionStates() - } - } - - @objc public dynamic var starSize: CGSize = defaultSize { - didSet { - self.configureStars() - } - } - - @objc public dynamic var starCount: Int = 5 { - didSet { - self.configureStars() - } - } - - // MARK: Initializers - - override public init(frame: CGRect) { - super.init(frame: frame) - self.configureStars() - } - - public required init(coder: NSCoder) { - super.init(coder: coder) - self.configureStars() - } - - // MARK: Private Functions - - private func configureStars() { - self.removeStars() - self.addStars() - self.updateSelectionStates() - } - - private func addStars() { - for index in 0 ..< self.starCount { - let button = UIButton(type: .custom) - button.setImage(self.starTemplate, for: .normal) - button.adjustsImageWhenHighlighted = false - self.addButtonSizeConstraints(to: button) - - let setRatingNumber = index + 1 - let localizedString = NSLocalizedString("RATING_ACCESSIBILITY_SET", bundle: .mapboxNavigation, value: "Set %ld-star rating", comment: "Format for accessibility label of button for setting a rating; 1 = number of stars") - button.accessibilityLabel = String.localizedStringWithFormat(localizedString, setRatingNumber) - - button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(button:)), for: .touchUpInside) - - addArrangedSubview(button) - - self.stars.append(button) - } - } - - private func removeStars() { - for star in self.stars { - removeArrangedSubview(star) - star.removeFromSuperview() - } - self.stars.removeAll() - } - - private func updateSelectionStates() { - for (index, button) in self.stars.enumerated() { - let selected = index < self.rating - button.tintColor = selected ? self.selectedColor : self.normalColor - button.isSelected = selected - - self.setAccessibility(for: button, at: index) - } - } - - private func setAccessibility(for button: UIButton, at index: Int) { - self.setAccessibilityHint(for: button, at: index) - - let value: String = if self.rating == 0 { - NSLocalizedString("NO_RATING", bundle: .mapboxNavigation, value: "No rating set.", comment: "Accessibility value of label indicating the absence of a rating") - } else { - String.localizedStringWithFormat(NSLocalizedString("RATING_STARS_FORMAT", bundle: .mapboxNavigation, value: "%ld star(s) set.", comment: "Format for accessibility value of label indicating the existing rating; 1 = number of stars"), self.rating) - } - - button.accessibilityValue = value - } - - private func setAccessibilityHint(for button: UIButton, at index: Int) { - guard self.rating == (index + 1) else { return } // This applies only to the zero-resettable button. - - button.accessibilityHint = NSLocalizedString("RATING_ACCESSIBILITY_RESET", bundle: .mapboxNavigation, value: "Tap to reset the rating to zero.", comment: "Rating Reset To Zero Accessability Hint") - } - - private func addButtonSizeConstraints(to view: UIView) { - view.widthAnchor.constraint(equalToConstant: self.starSize.width).isActive = true - view.heightAnchor.constraint(equalToConstant: self.starSize.height).isActive = true - } - - @objc private func ratingButtonTapped(button sender: UIButton) { - guard let index = stars.firstIndex(of: sender) else { return assertionFailure("RatingControl.swift: The Star button that was tapped was not found in the RatingControl.stars array. This should never happen.") } - let selectedRating = index + 1 - - self.rating = (selectedRating == self.rating) ? 0 : selectedRating - } -} diff --git a/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard b/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard deleted file mode 100644 index 893359a1..00000000 --- a/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MapboxNavigation/Resources/Base.lproj/Navigation.strings b/MapboxNavigation/Resources/Base.lproj/Navigation.strings deleted file mode 100644 index 73acb1fb..00000000 --- a/MapboxNavigation/Resources/Base.lproj/Navigation.strings +++ /dev/null @@ -1,3 +0,0 @@ - -/* Class = "UILabel"; text = "Rate your trip"; ObjectID = "W5U-cV-cDO"; */ -"W5U-cV-cDO.text" = "Rate your trip"; diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 1157b6dc..37d8efed 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -124,11 +124,17 @@ class RouteMapViewController: UIViewController { right: 20) } - lazy var endOfRouteViewController: EndOfRouteViewController = { - let storyboard = UIStoryboard(name: "Navigation", bundle: .mapboxNavigation) - let viewController = storyboard.instantiateViewController(withIdentifier: "EndOfRouteViewController") as! EndOfRouteViewController - return viewController - }() + lazy var endOfRouteViewController: EndOfRouteViewController = .init(dismissHandler: { [weak self] in + guard let self else { + return + } + guard let routeController else { + assertionFailure("routeController was unexpectedly nil") + return + } + routeController.endNavigation() + self.delegate?.mapViewControllerDidFinish(self, byCanceling: false) + }) weak var delegate: RouteMapViewControllerDelegate? diff --git a/MapboxNavigation/UIView.swift b/MapboxNavigation/UIView.swift index 91512381..6ad588d6 100644 --- a/MapboxNavigation/UIView.swift +++ b/MapboxNavigation/UIView.swift @@ -70,7 +70,10 @@ extension UIView { } func pinInSuperview(respectingMargins margins: Bool = false) { - guard let superview else { return } + guard let superview else { + assertionFailure("superview was unexpectedly nil") + return + } let guide: Anchorable = margins ? superview.layoutMarginsGuide : superview let constraints = [