Reputation: 609
Because I needed a UINavigationController inside another UINavigationController (and this is not possible by default), I created a UIViewController that acts as a UINavigationController, but dit does not subclass from UINavigationController.
The second NavigationController (the one that does to subclass UINavigationController), presents (depending on the ViewModel's state) a controller.
This is the custom NavigationController:
class OnboardingViewController: UIViewController {
// MARK: - Outlets
@IBOutlet weak var containerView: UIView!
// MARK: - Internal
private var viewModel: OnboardingViewModel = OnboardingViewModel()
// MARK: - View flow
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isHidden = true
navigateToNextFlow()
}
override var preferredFocusEnvironments: [UIFocusEnvironment] {
switch viewModel.state {
case .recommendations: return recommendationsController.preferredFocusEnvironments
default: return super.preferredFocusEnvironments
}
}
// MARK: - Handlers
var navigateToNextHandler: (() -> Void)?
// MARK: - Controllers
private var recommendationsController: OnboardingRecommendationsViewController {
let controller = UIViewController.instantiate(from: "Onboarding Recommendations") as OnboardingRecommendationsViewController
controller.navigateToNextHandler = { [unowned self] in
self.viewModel.state = .done
self.navigateToNextFlow(animated: true)
}
return controller
}
// MARK: - Navigation
private func navigateToNextFlow(animated: Bool = false) {
switch viewModel.state {
case .recommendations:
add(child: recommendationsController, to: containerView)
case .done:
viewModel.finish()
navigateToNextHandler?()
}
updateFocusIfNeeded()
setNeedsFocusUpdate()
}
}
This is the childViewController:
class OnboardingRecommendationsViewController: UIViewController {
// MARK: - Outlets
@IBOutlet weak var onOffButton: UIButton!
@IBOutlet weak var finishButton: UIButton!
// MARK: - Internal
fileprivate let viewModel: OnboardingRecommendationsViewModel = OnboardingRecommendationsViewModel()
// MARK: - View flow
override func viewDidLoad() {
super.viewDidLoad()
setupLabels()
setupOnOffButton()
}
// MARK: - Handlers
var navigateToNextHandler: (() -> Void)?
// MARK: - Focus
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [finishButton, onOffButton]
}
}
The finishButton is beneath the onOffButton in the storyboard. I'm trying to set the initial focus on the finishButton instead of the onOffButton. But the user can focus the onOffButton if he wants.
Whatever I try, it just doesn't work. The preferredFocusEnvironments gets called, but the focus of the buttons stays in the wrong order.
What am I doing wrong?
Upvotes: 3
Views: 2339
Reputation: 609
Sorry for the late answer. It turned out that the viewController I was pushing, defined was as let
, so I pushed a few instances over themselves, and that's why it seems that the preferredFocusEnvironments
was not working. I actually saw a new instance of the ViewController with another initial focus order. Changing the variable declaration of the viewController from let
to lazy var
did the trick. So, in the end, it had really nothing to do with preferredFocusEnvironments
not working. But thanks for the input!
Upvotes: 1
Reputation: 21
You should set restoresFocusAfterTransition = false to avoid the default behavior. And then, in preferredFocusEnvironments return the view you want to focus
Upvotes: 0
Reputation: 144
Did you try like this?
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [finishButton]
}
Or you can disable userInteraction for onOffButton until finishButton gets focused. (Not a good solution though)
Upvotes: 0