material-components_materia.../components/AppBar/examples/AppBarNavigationControllerWithAppBarInitiallyHiddenExample.swift
Jeff Verkoeyen b78737be48 [AppBar] Inject AppBars after view controllers have been added to the navigation controller.
Before this change, it was never possible to interact with the navigationController property in a pushed view controller's viewDidLoad property when using MDCAppBarNavigationController. This behavior is surprising for folks using UINavigationController, which does allow you to access navigationController from within viewDidLoad.

This expectation is an anti-pattern (e.g. viewDidLoad is never guaranteed to be invoked after a view controller has been pushed onto a navigationController stack), but the goal of MDCAppBarNavigationController is to provide a drop-in replacement for UINavigationController so our intent is to align behaviors, even if they're not necessarily for the right reasons.

All that being said, the change introduced in this CL simply swaps the ordering of pushing the view controller and injecting the AppBar, such that the injection now happens after the super implementation of pushViewController has been invoked. This allows the viewDidLoad logic to be invoked after navigationController has been initialized on the view controller.

The reason we were doing it the other way before was so that the AppBar could intercept the relevant status bar appearance events. We work around that requirement in this change by invoking setNeedsStatusBarAppearanceUpdate and setNeedsUpdateOfHomeIndicatorAutoHidden immediately after injecting the AppBar.

PiperOrigin-RevId: 313410464
2020-05-27 10:06:04 -07:00

162 lines
5.0 KiB
Swift

// Copyright 2020-present the Material Components for iOS authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
import MaterialComponents.MaterialAppBar
import MaterialComponents.MaterialAppBar_Theming
import MaterialComponents.MaterialContainerScheme
// This example demonstrates that it is possible to hide a view controller's
// navigationController.navigationBar during viewDidLoad when presented within an
// MDCAppBarNavigationController.
class AppBarNavigationControllerWithAppBarInitiallyHiddenExample:
UIViewController,
MDCAppBarNavigationControllerDelegate {
@objc var containerScheme: MDCContainerScheming = MDCContainerScheme()
init() {
super.init(nibName: nil, bundle: nil)
self.title = "Navigation Controller"
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = self.containerScheme.colorScheme.backgroundColor
self.navigationItem.rightBarButtonItem =
UIBarButtonItem(title: "Present",
style: .done,
target: self,
action: #selector(presentModalAnimated))
}
@objc func presentModalAnimated() {
presentModal(animated: true)
}
func presentModal(animated: Bool) {
let contentViewController = PresentedViewController()
let navigationController = MDCAppBarNavigationController()
navigationController.shouldSetNavigationBarHiddenHideAppBar = true
navigationController.delegate = self
navigationController.pushViewController(contentViewController, animated: false)
contentViewController.navigationItem.rightBarButtonItem =
UIBarButtonItem(title: "Dismiss",
style: .done,
target: self,
action: #selector(dismissModal))
// Explicitly use the full-screen style to validate safe area insets behavior.
navigationController.modalPresentationStyle = .fullScreen
self.present(navigationController, animated: animated, completion: nil)
}
@objc func dismissModal() {
dismiss(animated: true)
}
// MARK: - MDCAppBarNavigationControllerDelegate
func appBarNavigationController(_ navigationController: MDCAppBarNavigationController,
willAdd appBarViewController: MDCAppBarViewController,
asChildOf viewController: UIViewController) {
appBarViewController.applyPrimaryTheme(withScheme: self.containerScheme)
}
}
private class PresentedViewController: UITableViewController {
init() {
super.init(nibName: nil, bundle: nil)
self.title = "Presented"
self.navigationItem.leftBarButtonItem =
UIBarButtonItem(title: "Toggle",
style: .done,
target: self,
action: #selector(toggleVisibility))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
}
// MARK - Actions
@objc func toggleVisibility() {
guard let navigationController = navigationController else {
return
}
navigationController.setNavigationBarHidden(!navigationController.isNavigationBarHidden,
animated: true)
}
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return self.tableView.dequeueReusableCell(withIdentifier: "cell") ??
UITableViewCell(style: .default, reuseIdentifier: "cell")
}
// MARK - UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
toggleVisibility()
tableView.deselectRow(at: indexPath, animated: true)
}
}
// MARK: Catalog by convention
extension AppBarNavigationControllerWithAppBarInitiallyHiddenExample {
@objc class func catalogMetadata() -> [String: Any] {
return [
"breadcrumbs": ["App Bar", "Navigation Controller (app bar hidden)"],
"primaryDemo": false,
"presentable": false,
]
}
}
// MARK: Snapshot testing by convention
extension AppBarNavigationControllerWithAppBarInitiallyHiddenExample {
@objc func testPresented() {
presentModal(animated: false)
}
}