I want to place an image in the middle of a navigation bar that is bigger then the bar itself. So far I tried to use a UIView with a UIImageView inside and it works quite we
You could try to put the code in the viewWillAppear method. This way you will add the image to the bar everytime the view appears. However you should then remove the inageview within the viewDidDissappear method. If you need it in several views you could subclass the UIViewController and use this one.
The UINavigationBar has UIBarMetrics if you want a custom height but it's not fully interactive.
There's a bunch of code suggested by apple to actually develop nice things like iMessage app.
I found why you got this issue. It's because of a private view which has name _UINavigationBarContentView
. It's a subview of UINavigationBar
. navigationItem.titleView
is contained in this view.
At first time, when you change navigationItem.titleView
. _UINavigationBarContentView.clipsToBounds
is false
.But after you push another controller and pop back, _UINavigationBarContentView.clipsToBounds
is true
. That's why titleView
is cropped.
So i have a temporary solution. Each time when viewController
appears, find this view and change _UINavigationBarContentView.clipsToBounds
to false
and layout titleView
.
override func viewDidAppear(_ animated: Bool) {
for view : UIView in (navigationController?.navigationBar.subviews)! {
view.clipsToBounds = false;
}
navigationItem.titleView?.layoutIfNeeded()
}
override func viewWillAppear(_ animated: Bool) {
for view : UIView in (navigationController?.navigationBar.subviews)! {
view.clipsToBounds = false;
}
navigationItem.titleView?.layoutIfNeeded()
}
I tried and it works. But i think you shouldn't do something whit it because it's private view. Maybe Apple don't want us do anything with it.
Hope somehow my suggestion can help you. Good luck ;)
SOLUTION
Adding observer for _UINavigationBarContentView.clipsToBounds
, each time when it changes to false
, set to true
and update layout of titleView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let logo = UIImage(named: "Logo")
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
let imageView = UIImageView(image: logo)
imageView.frame = CGRect(x: 0, y: 0, width: titleView.frame.width, height: titleView.frame.height)
titleView.addSubview(imageView)
imageView.contentMode = .scaleAspectFit
imageView.image = logo
navigationItem.titleView = titleView
navigationController?.navigationBar.subviews[2].addObserver(self, forKeyPath: "clipsToBounds", options: [.old, .new], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if (navigationController?.navigationBar.subviews[2].isEqual(object))! {
DispatchQueue.main.async {
self.navigationController?.navigationBar.subviews[2].clipsToBounds = false
self.navigationItem.titleView?.layoutIfNeeded()
}
}
}
deinit {
navigationController?.navigationBar.subviews[2].removeObserver(self, forKeyPath: "clipsToBounds")
}
For more detail and easier, you can check my demo here https://github.com/trungducc/stackoverflow/tree/big-title-navigation-bar