Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

custom background image with large titles NavigationBar in iOS 11

How do you set a custom background image for the large title NavigationBar in iOS 11? I'm using a custom subclass which I've assigned to the navigationControllers in the storyboard.

This is how I create my custom NavBar:

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.navigationBar.tintColor = UIColor(red:1, green:1, blue:1, alpha:0.6)
        self.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
        if #available(iOS 11.0, *) {
            self.navigationBar.prefersLargeTitles = true
            self.navigationItem.largeTitleDisplayMode = .automatic
            self.navigationBar.largeTitleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
            self.navigationBar.barTintColor = UIColor.green
        }
        self.navigationBar.isTranslucent = false
        self.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navigationBarBackground"), for: .default)
        self.navigationBar.shadowImage = #imageLiteral(resourceName: "navigationBarShadow")
    }
}

Strangely the setBackgroundImage(image, for: .default) doesn't work for the large titles. It worked before with iOS 10 and also if I rotate the iPhone (and activate the small NavBar) the background is back?

Edit: The backgroundImage is still rendered but somehow hidden. Only if you start scrolling and the "normal" Navigation Bar appears, the backgroundImage is visible. Also the barTintColor is completely ignored in this case. screenshot GIF

like image 960
alexkaessner Avatar asked Sep 13 '17 11:09

alexkaessner


4 Answers

I had the same issue, fixed it by

Remove setBackgroundImage and use barTint color with pattern image

let bgimage = imageWithGradient(startColor: UIColor.red, endColor: UIColor.yellow, size: CGSize(width: UIScreen.main.bounds.size.width, height: 1))
self.navigationBar.barTintColor = UIColor(patternImage: bgimage!)

Get image with gradient colors

func imageWithGradient(startColor:UIColor, endColor:UIColor, size:CGSize, horizontally:Bool = true) -> UIImage? {

    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
    if horizontally {
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
    } else {
        gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
    }

    UIGraphicsBeginImageContext(gradientLayer.bounds.size)
    gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}
like image 185
oldrinmendez Avatar answered Nov 13 '22 12:11

oldrinmendez


Finally I found solution!

Edit: Works on iOS 13 and higher


You can use it before view appears, eg: in viewDidLoad() method:

    override func viewDidLoad()
    {
        super.viewDidLoad()

        let largeTitleAppearance = UINavigationBarAppearance() 

        largeTitleAppearance.configureWithOpaqueBackground()
        largeTitleAppearance.backgroundImage = UIImage(named: "BackgroundImage.png")

        self.navigationBar.standardAppearance = largeTitleAppearance
        self.navigationBar.scrollEdgeAppearance = largeTitleAppearance
    }

All that you need is:

  1. Create UINavigationBarAppearance instance:

    let largeTitleAppearance = UINavigationBarAppearance() 
    

    Apple documentation:

    UINavigationBarAppearance - An object for customizing the appearance of a navigation bar.


  1. Configure it:

    largeTitleAppearance.configureWithOpaqueBackground()
    

    "Opaque" here because we want to set colorised image (but in practice it doesn't matter, what configure will you set)


  1. Set background image:

    largeTitleAppearance.backgroundImage = UIImage(named: "BackgroundImage.png") // Set here image that you need
    

  1. Assign our largeTitleAppearance object to both standardAppearance and scrollEdgeAppearance navigationBar's fields:

    self.navigationBar.standardAppearance = largeTitleAppearance // For large-navigationBar condition when it is collapsed
    self.navigationBar.scrollEdgeAppearance = largeTitleAppearance // For large-navigationBar condition when it is expanded
    

    Apple documentation:

    .standardAppearance - The appearance settings for a standard-height navigation bar.

    .scrollEdgeAppearance - The appearance settings to use when the edge of any scrollable content reaches the matching edge of the navigation bar.


This helped to me: https://sarunw.com/posts/uinavigationbar-changes-in-ios13/#going-back-to-old-style

like image 8
UnuSynth Avatar answered Nov 13 '22 12:11

UnuSynth


In iOS 11 you no more need set BackgroundImage(Remove its declaration) if you use large titles. Instead you need use BarTintColor.

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.navigationBar.tintColor = UIColor(red:1, green:1, blue:1, alpha:0.6)
        self.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
        if #available(iOS 11.0, *) {
            self.navigationBar.prefersLargeTitles = true
            self.navigationItem.largeTitleDisplayMode = .automatic
            self.navigationBar.largeTitleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
            self.navigationBar.barTintColor = UIColor(red:1, green:1, blue:1, alpha:1)
        }
        else {
            self.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navigationBarBackground"), for: .default)                
        }
        self.navigationBar.shadowImage = #imageLiteral(resourceName: "navigationBarShadow")
        self.navigationBar.isTranslucent = false
    }
}
like image 6
Pocheshire Avatar answered Nov 13 '22 11:11

Pocheshire


Try this code (Swift 4.0):

in viewDidLoad()

self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
if #available(iOS 11.0, *) {
    self.navigationController?.navigationBar.prefersLargeTitles = true
    self.navigationItem.largeTitleDisplayMode = .automatic
    self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
} else {
    //iOS <11.0
}
self.title = "Title"
self.navigationController?.navigationBar.barTintColor = UIColor(patternImage: #imageLiteral(resourceName: "nav_bg"))
self.navigationController?.navigationBar.isTranslucent = false
like image 3
TomikeKrasnay Avatar answered Nov 13 '22 12:11

TomikeKrasnay