Here is what I did which worked for me using Swift 5, XCode 12.
Step 1 (Optional) - Create a custom UINavigationController class
class CustomNavigationController: UINavigationController {
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    navigationBar.isTranslucent = true
}
Replace your UINavigationController with this UINavigationController subclass. I mark this as optional as this is based on preference, if you do not set this, your navigation bar will be opaque and you cannot see what's beneath it.
Setting the navigationBar.isTranslucent = true allows you to see the background beneath it which is what I like. A subclass is also optional but you might need to make other updates to your nav bar so I always like to make this a subclass.
Step 2 - Set up your background view constraints
class CustomViewController: UIViewController {
  // your background view
  let bgImageView: UIImageView = {
    let bgImageView = UIImageView()
    bgImageView.image = UIImage(named: "gradient_background")
    bgImageView.contentMode = .scaleAspectFill
    return bgImageView
}()
  // Get the height of the nav bar and the status bar so you
  // know how far up your background needs to go
  var topBarHeight: CGFloat {
    var top = self.navigationController?.navigationBar.frame.height ?? 0.0
    if #available(iOS 13.0, *) {
      top += UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
    } else {
      top += UIApplication.shared.statusBarFrame.height
    }
    return top
  }
  var isLayoutConfigured = false
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    title = "Site Visit"
    // you only want to do this once
    if !isLayoutConfigured() {
      isLayoutConfigured = true
      configBackground()
    }
  }
  private func configBackground() {
    view.addSubview(bgImageView)
    configureBackgroundConstraints()
  }
  
  // Set up your constraints, main one here is the top constraint
  private func configureBackgroundConstraints() {
    bgImageView.translatesAutoresizingMaskIntoConstraints = false
    bgImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
                                     constant: -topBarHeight).isActive = true
    bgImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,
                                         constant: 0).isActive = true
    bgImageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
                                        constant: 0).isActive = true
    bgImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor,
                                          constant: 0).isActive = true
    
    view.layoutIfNeeded()
}
Before setting constraints:

After setting above constraints:
