I'm using the Mapbox framework on iOS in Swift to create a custom NavigationViewController. I need help getting a reference to the FloatingStackView
on the NavigationView
.
I've added an additional button (see top left of screenshot) and I'd like to anchor it to the FloatingStackView on the right so that it moves up and down as the top banner changes (similar to the FloatingStackView behavior). The trouble is I can't seem to access the FloatingStackView. From what I've seen in the Mapbox source code the FloatingStackView
is a subview of a NavigationView
which is the view of a RouteMapViewController
which is the mapViewController
of a NavigationViewController. Trouble is I can't access the mapViewController as it is inaccessible due to internal protection level.
Here's an example of what I'd like to do:
import UIKit
import MapboxNavigation
class DemoNavigationViewController: NavigationViewController {
override func viewDidLoad() {
super.viewDidLoad()
let mapVC = mapViewController
// Do any additional setup after loading the view.
}
}
Here's the error on the let mapVC line:
'mapViewController' is inaccessible due to 'internal' protection level
My end goal would be to be able to access the FloatingStackView via something like this:
let navigationView = mapViewController.view as? NavigationView
let floatingStackView = navigationView.floatingStackView
If I can get a reference to that floatingStackView I think I can handle the layout.
How can I get around the is inaccessible due to 'internal' protection level
error or find another way to access the floating stack view?
From NavigationView class source code
You will not be able to reference it directly if it is marked internal. There is however a hacky way to get to any view and use it for AutoLayout (assuming the view uses autoLayout and has anchors you can use).
As each view has a subViews
array you can iterate through the subviews to find the one you want. All subViews are visible, even the ones that come from private instances. The trick is identifying the one you need.
If the Type definition is accessible, you can do something like this
let navView = viewController.subViews.first{$0 is NavigationView}!
let stackView = navView.subViews.first{$0 is UIStackView}!
floatingButton.topAnchor.constraint(equalTo: stackView.topAnchor).isActive true
If you don't have a Type you can match against you'll have to do something even more hacky, such as matching against the description: .first{$0.description conatins "stack"}
. Or even by trial and error by indexing the subView array until you get the result you want, and then hoping the order of views is always the same!
Whether you'd want to risk this in production code is only a decision you can make as it subject to breaking if the library changes it's UI design. Depending on the app's user base I might risk matching against type, but probably not the other ways!
Note I've force-unwrapped the views above for brevity - you'd want to be a bit more cautious in production code with that too :-)