10
votes

I am working on an application and I just upgraded to Xcode 9 / Swift 4 and also upgraded my iPhone to iOS 11. The application was installed when I installed iOS 11 and all seemed OK until I run it from Xcode. Now I am stuck with the default NavBar height.

The code I was using to change the height is no longer working:

class CustomNavControllerVC: UINavigationController
{
    let navBarHeight : CGFloat = 64.0
    let navbarBackButtonColor = UIColor(red: 247/255, green: 179/255, blue: 20/255, alpha: 1)

    override func viewDidLoad()
    {
        super.viewDidLoad()

        print("CustomNavControllerVC > viewDidLoad")
    }

    override func viewDidLayoutSubviews()
    {
        print("CustomNavControllerVC > viewDidLayoutSubviews")

        super.viewDidLayoutSubviews()

        navigationBar.frame.size.height = navBarHeight
        navigationBar.tintColor = navbarBackButtonColor
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}


// In my VCs

override func viewDidLoad()
{
    customizeNavBar()
}


func customizeNavBar()
{
    let navbarBackItem = UIBarButtonItem()
    navbarBackItem.title = "Înapoi"
    navigationItem.backBarButtonItem = navbarBackItem

    let navbarImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 55, height: 20))
    navbarImageView.contentMode = .scaleToFill

    let navbarLogo = UIImage(named: "NavBarLogo.png")
    navbarImageView.image = navbarLogo

    navigationItem.titleView = navbarImageView
}

So far the only thing I could find on this issue is this:

iOS 11 navigation bar height customizing

iOS11 customize navigation bar height

How to correctly set UINavigationBar height in iOS 11

But the info provided does not help, unfortunately.

Any ideas / suggestions?

1
Why can't go with Custom View instead of UINavigationBar ?Mukesh
openradar.appspot.com/32912789 It appears that the UIKit team never intended for UINavigationBar's to support custom heights. If you want to expand the size of the navigation bar I would use the techniques apple promotes here: developer.apple.com/library/content/samplecode/NavBar/…beyowulf
@mukesh_lokare - Initially the NavBar was supposed to have standard height with the logo image in the middle so I decided to use the standard NavBarController. 30 screens and lots of classes later, someone decided it must have custom height so I decided to use that method.daydr3am3r
@beyowulf - I saw that but I was hoping for a solution. I'll use the provided techniques, see what happens.daydr3am3r
So sad that we are hobbled by Apple's inept developers. Looks like the only thing to do is forget NavController and once again just DIY.amergin

1 Answers

-5
votes

Updated 2017.10.6

I had the same problem. Below is my solution. I assume that height size is 66.

My solution is working fine iOS 10, 11.

Please choose my answer if it helps you.

Create NavgationBar.swift

import UIKit

class NavigationBar: UINavigationBar {

    //set NavigationBar's height
    var customHeight : CGFloat = 66

    override func sizeThatFits(_ size: CGSize) -> CGSize {

        return CGSize(width: UIScreen.main.bounds.width, height: customHeight)

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        frame = CGRect(x: frame.origin.x, y:  0, width: frame.size.width, height: customHeight)

        // title position (statusbar height / 2)
        setTitleVerticalPositionAdjustment(-10, for: UIBarMetrics.default)

        for subview in self.subviews {
            var stringFromClass = NSStringFromClass(subview.classForCoder)
            if stringFromClass.contains("BarBackground") {
                subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: customHeight)
                subview.backgroundColor = .yellow

            }

            stringFromClass = NSStringFromClass(subview.classForCoder)
            if stringFromClass.contains("BarContent") {

                subview.frame = CGRect(x: subview.frame.origin.x, y: 20, width: subview.frame.width, height: customHeight - 20)

                subview.backgroundColor = UIColor(red: 20/255, green: 20/255, blue: 20/255, alpha: 0.4)

            }
        }
    }


}

Set Storyboard

enter image description here

Set NavigationBar class

Set Custom NavigationBar class

Add TestView

enter image description here

Add TestView + Set SafeArea

ViewController.swift

import UIKit

class ViewController: UIViewController {

    var navbar : UINavigationBar!

    @IBOutlet weak var testView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        //update NavigationBar's frame
        self.navigationController?.navigationBar.sizeToFit()
        print("NavigationBar Frame : \(String(describing: self.navigationController!.navigationBar.frame))")

    }

    //Hide Statusbar
    override var prefersStatusBarHidden: Bool {

        return true
    }

    override func viewDidAppear(_ animated: Bool) {

        super.viewDidAppear(false)

        //Important!
        if #available(iOS 11.0, *) {

            //Default NavigationBar Height is 44. Custom NavigationBar Height is 66. So We should set additionalSafeAreaInsets to 66-44 = 22
            self.additionalSafeAreaInsets.top = 22

        }

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

SecondViewController.swift

import UIKit

class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.


        // Create BackButton
        var backButton: UIBarButtonItem!
        let backImage = imageFromText("Back", font: UIFont.systemFont(ofSize: 16), maxWidth: 1000, color:UIColor.white)
        backButton = UIBarButtonItem(image: backImage, style: UIBarButtonItemStyle.plain, target: self, action: #selector(SecondViewController.back(_:)))

        self.navigationItem.leftBarButtonItem = backButton
        self.navigationItem.leftBarButtonItem?.setBackgroundVerticalPositionAdjustment(-10, for: UIBarMetrics.default)


    }
    override var prefersStatusBarHidden: Bool {

        return true
    }
    @objc func back(_ sender: UITabBarItem){

        self.navigationController?.popViewController(animated: true)

    }


    //Helper Function : Get String CGSize
    func sizeOfAttributeString(_ str: NSAttributedString, maxWidth: CGFloat) -> CGSize {
        let size = str.boundingRect(with: CGSize(width: maxWidth, height: 1000), options:(NSStringDrawingOptions.usesLineFragmentOrigin), context:nil).size
        return size
    }


    //Helper Function : Convert String to UIImage
    func imageFromText(_ text:NSString, font:UIFont, maxWidth:CGFloat, color:UIColor) -> UIImage
    {
        let paragraph = NSMutableParagraphStyle()
        paragraph.lineBreakMode = NSLineBreakMode.byWordWrapping
        paragraph.alignment = .center // potentially this can be an input param too, but i guess in most use cases we want center align

        let attributedString = NSAttributedString(string: text as String, attributes: [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: color, NSAttributedStringKey.paragraphStyle:paragraph])

        let size = sizeOfAttributeString(attributedString, maxWidth: maxWidth)
        UIGraphicsBeginImageContextWithOptions(size, false , 0.0)
        attributedString.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image!
    }




    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }



}

enter image description here enter image description here

Yellow is barbackgroundView. Black opacity is BarContentView.

And I removed BarContentView's backgroundColor.

enter image description here

That's It.