1

I managed to create translucent and rounded UITableViewCells in a UITableViewController that is embedded inside a Navigation Controller with this line of code in viewDidLoad():

    tableView.backgroundView = UIImageView(image: UIImage(named: "nightTokyo"))

Background image does not fill entire phone screen

But I want the background image to fill the entire phone screen. I changed the code (and only this line of code) to:

    navigationController?.view = UIImageView(image: UIImage(named: "nightTokyo"))

Now the background image fills up the entire phone screen, but my table and even the iPhone's time and battery indicator icons are missing.

Background image fills the entire phone screen but everything else is missing

What I want is for the background image to fill the entire screen, but the tableView, its cells, the iPhone time, battery level icon, etc. to remain displayed.

Kaplan
  • 91
  • 1
  • 11

2 Answers2

1

navigationController?.setNavigationBarHidden(true, animated: true)

m_k_s
  • 54
  • 3
  • Can I still display a Bar Button Item in the navigation bar and still have the navigation bar's background be transparent so that the background image shows through? – Kaplan Mar 01 '21 at 12:33
  • Hmm... I guess not. Per the online doc for the UINavigationBar: It is permissible to customize the appearance of the navigation bar using the methods and properties of the UINavigationBar class but you must never change its frame, bounds, or **alpha** values or modify its view hierarchy directly. To show or hide the navigation bar, you should always do so through the navigation controller by changing its isNavigationBarHidden property or calling the setNavigationBarHidden(_:animated:) method. – Kaplan Mar 01 '21 at 12:51
  • Is it okay now? – m_k_s Mar 01 '21 at 13:00
  • Yes, your answer worked. But I now see that by hiding the navigationBar, any Bar Button Item would be hidden. too. So now I am trying to see, instead of simply hiding the navigationBar, whether I can make the navigationBar semi-transparent instead. I was able to use the answer from https://stackoverflow.com/questions/18821347/how-can-i-make-my-navigation-bar-uniformly-semi-transparent to make the navigationBar semi-transparent but the area just above the navigationBar isn't made semi-transparent. – Kaplan Mar 01 '21 at 13:40
  • OK, this works. Instead of hiding the navigationBar, I set its backgroundImage to a 20x20 semi-transparent image. That image is automatically repeated across the whole top section of the navigationController, but still allowing the bar button items to show through: `navigationController?.navigationBar.setBackgroundImage(UIImage(named: "semiTransparent20x20"), for: .default)` – Kaplan Mar 01 '21 at 14:17
0

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: UIViewController without NavBar top constraints

After setting above constraints: UIViewController with NavBar top constraints

Shawn Frank
  • 4,381
  • 2
  • 19
  • 29