Using standalone UINavigationBar in iOS
Tags: swift, ios, uinavigationbar
Published: 2018-01-10 14:16:49

---
title: Using standalone UINavigationBar in iOS
published: true
description: 
tags: swift, ios, uinavigationbar
---

Original post https://github.com/onmyway133/blog/issues/144

There are times we want the same `UIViewController` to look good when it's presented modally or pushed from `UINavigationController` stack. Take a look at BarcodeScanner and the PR https://github.com/hyperoslo/BarcodeScanner/pull/82

When it is presented, we need a header view so that we can show a title and a close button. We can create a custom `HeaderView` that inherits from `UIView` or either embed it in a `UINavigationController` before presenting.

If we go with the controller being embedded in `UINavigationController` approach, it will collide with the other `UINavigationController` when it is pushed.

If we go with custom `HeaderView`, then we need to layout the view so that it looks good on both portrait and landscape, and on iPhone X that as `safeAreaLayoutGuide`.

## Using standalone UINavigationBar

Since `UINavigationController` uses `UINavigationBar` under the hood, which uses `UINavigationItem` info to present the content. We can imitate this behavior by using a standalone `UINavigationBar`. See [Adding Content to a Standalone Navigation Bar](https://developer.apple.com/documentation/uikit/uinavigationbar)

> In the vast majority of scenarios you will use a navigation bar as part of a navigation controller. However, there are situations for which you might want to use the navigation bar UI and implement your own approach to content navigation. In these situations, you can use a standalone navigation bar.

> A navigation bar manages a stack of UINavigationItem objects

The beauty is that our standalone `UINavigationBar` and that of `UINavigationController` are the same, use the same `UINavigationItem` and no manual layout are needed

### Declare `UINavigationItem`

We can just set properties like we did with a normal `navigationItem`

```swift
let standaloneItem = UINavigationItem()
standaloneItem.leftBarButtonItem = UIBarButtonItem(customView: closeButton)
standaloneItem.titleView = UILabel()
```

### Adding `UINavigationBar`

Customise your bar, then declare layout constraints. You only need to pin left, right, and top. Note that you need to implement `UINavigationBarDelegate` to attach bar to status bar, so that it appears good on iPhone X too

```swift
let navigationBar = UINavigationBar()
navigationBar.isTranslucent = false
navigationBar.delegate = self
navigationBar.backgroundColor = .white
navigationBar.items = [standaloneItem]
 
navigationBar.translatesAutoresizingMaskIntoConstraints = false
navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
 
if #available(iOS 11, *) {
  navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
} else {
  navigationBar.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
```

```swift
extension BarcodeScannerController: UINavigationBarDelegate {
  public func position(for bar: UIBarPositioning) -> UIBarPosition {
    return .topAttached
  }
}
```

### Inside `UINavigationController`

When this `UIViewController` is pushed from a `UINavigationController` stack, we just need to hide our standalone `navigationBar`. If we prefer the default back button, we don't need to set `leftBarButtonItem`

```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
  if navigationController != nil {
    navigationItem.titleView = standaloneItem.titleView
    navigationBar.isHidden = true
  } else {
    navigationBar.isHidden = false
  }
}
```