Custom banners

ViewController
import Foundation
import UIKit
import MapboxCoreNavigation
import MapboxNavigation
import MapboxDirections
class CustomBarsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let origin = CLLocationCoordinate2DMake(37.77440680146262, -122.43539772352648)
let destination = CLLocationCoordinate2DMake(37.76556957793795, -122.42409811526268)
let routeOptions = NavigationRouteOptions(coordinates: [origin, destination])
Directions.shared.calculate(routeOptions) { [weak self] (session, result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let response):
guard let route = response.routes?.first, let strongSelf = self else {
return
}
// For demonstration purposes, simulate locations if the Simulate Navigation option is on.
let navigationService = MapboxNavigationService(route: route, routeOptions: routeOptions, simulating: simulationIsEnabled ? .always : .onPoorGPS)
// Pass your custom implementations of `topBanner` and/or `bottomBanner` to `NavigationOptions`
// If you do not specify them explicitly, `TopBannerViewController` and `BottomBannerViewController` will be used by default.
// Those are `Open`, so you can also check thier source for more examples of using standard UI controls!
let topBanner = CustomTopBarViewController()
let bottomBanner = CustomBottomBarViewController()
let navigationOptions = NavigationOptions(navigationService: navigationService, topBanner: topBanner, bottomBanner: bottomBanner)
let navigationViewController = NavigationViewController(for: route, routeOptions: routeOptions, navigationOptions: navigationOptions)
bottomBanner.navigationViewController = navigationViewController
navigationViewController.modalPresentationStyle = .fullScreen
strongSelf.present(navigationViewController, animated: true, completion: nil)
}
}
}
}
// MARK: - CustomTopBarViewController
class CustomTopBarViewController: ContainerViewController {
private lazy var instructionsBannerTopOffsetConstraint = {
return instructionsBannerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
}()
private lazy var centerOffset: CGFloat = calculateCenterOffset(with: view.bounds.size)
private lazy var instructionsBannerCenterOffsetConstraint = {
return instructionsBannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0)
}()
// You can Include one of the existing Views to display route-specific info
lazy var instructionsBannerView: InstructionsBannerView = {
let banner = InstructionsBannerView()
banner.translatesAutoresizingMaskIntoConstraints = false
banner.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
banner.layer.cornerRadius = 25
banner.layer.opacity = 0.75
return banner
}()
override func viewDidLoad() {
view.addSubview(instructionsBannerView)
setupConstraints()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateConstraints()
}
private func setupConstraints() {
instructionsBannerCenterOffsetConstraint.isActive = true
instructionsBannerTopOffsetConstraint.isActive = true
}
private func updateConstraints() {
instructionsBannerCenterOffsetConstraint.constant = centerOffset
instructionsBannerTopOffsetConstraint.constant = (traitCollection.verticalSizeClass == .compact ? 10 : 44)
}
// MARK: - Device rotation
private func calculateCenterOffset(with size: CGSize) -> CGFloat {
return (size.height < size.width ? -size.width / 4 : 0)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
centerOffset = calculateCenterOffset(with: size)
}
open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateConstraints()
}
// MARK: - NavigationServiceDelegate implementation
public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) {
// pass updated data to sub-views which also implement `NavigationServiceDelegate`
instructionsBannerView.updateDistance(for: progress.currentLegProgress.currentStepProgress)
}
public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) {
instructionsBannerView.update(for: instruction)
}
public func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) {
instructionsBannerView.updateDistance(for: service.routeProgress.currentLegProgress.currentStepProgress)
}
}
// MARK: - CustomBottomBarViewController
class CustomBottomBarViewController: ContainerViewController, CustomBottomBannerViewDelegate {
weak var navigationViewController: NavigationViewController?
// Or you can implement your own UI elements
lazy var bannerView: CustomBottomBannerView = {
let banner = CustomBottomBannerView()
banner.translatesAutoresizingMaskIntoConstraints = false
banner.delegate = self
return banner
}()
override func loadView() {
super.loadView()
view.addSubview(bannerView)
let safeArea = view.layoutMarginsGuide
NSLayoutConstraint.activate([
bannerView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
bannerView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
bannerView.heightAnchor.constraint(equalTo: view.heightAnchor),
bannerView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupConstraints()
}
private func setupConstraints() {
if let superview = view.superview?.superview {
view.bottomAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
// MARK: - NavigationServiceDelegate implementation
func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) {
// Update your controls manually
bannerView.progress = Float(progress.fractionTraveled)
bannerView.eta = "~\(Int(round(progress.durationRemaining / 60))) min"
}
// MARK: - CustomBottomBannerViewDelegate implementation
func customBottomBannerDidCancel(_ banner: CustomBottomBannerView) {
navigationViewController?.dismiss(animated: true,
completion: nil)
}
}
ViewController
import UIKit
import MapboxNavigation
protocol CustomBottomBannerViewDelegate: class {
func customBottomBannerDidCancel(_ banner: CustomBottomBannerView)
}
class CustomBottomBannerView: UIView {
@IBOutlet var contentView: UIView!
@IBOutlet weak var etaLabel: UILabel!
@IBOutlet weak var progressBar: UIProgressView!
@IBOutlet weak var cancelButton: UIButton!
var progress: Float {
get {
return progressBar.progress
}
set {
progressBar.setProgress(newValue, animated: false)
}
}
var eta: String? {
get {
return etaLabel.text
}
set {
etaLabel.text = newValue
}
}
weak var delegate: CustomBottomBannerViewDelegate?
private func initFromNib() {
Bundle.main.loadNibNamed(String(describing: CustomBottomBannerView.self),
owner: self,
options: nil)
addSubview(contentView)
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
progressBar.progressTintColor = .systemGreen
progressBar.layer.borderColor = UIColor.black.cgColor
progressBar.layer.borderWidth = 2
progressBar.layer.cornerRadius = 5
cancelButton.backgroundColor = .systemGray
cancelButton.layer.cornerRadius = 5
backgroundColor = UIColor.black.withAlphaComponent(0.3)
layer.cornerRadius = 10
}
override init(frame: CGRect) {
super.init(frame: frame)
initFromNib()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initFromNib()
}
@IBAction @objc func onCancel(_ sender: Any) {
delegate?.customBottomBannerDidCancel(self)
}
}
Was this page helpful?