Search code examples
iosswiftxcodeuinavigationcontrolleruinavigationbar

Custom Navigation Title in iOS 12


I am trying to implement a custom Navigation Title on an iOS app.

The StoryBoard looks like this:

enter image description here

The place that I want to have the custom Navigation Title is the last view ( the message view ), and because I use an image and text this means that I need to have custom width and height. By needing this if I do in viewDidLoad:

let rect = CGRect(x: 0, y:0, width: 150, height: 88)
titleView = UIView(frame: rect)
......
titleView?.addSubview(imageView)
......
titleView?.addSubview(label)
navigationItem.titleView = titleView

The height of the title is blocked to 44pt.

But how I managed to do it is adding the subViews to the navigation bar:

var navigationBar: MessagesNavigationBar? {
    guard let navigationBar = navigationController?.navigationBar as? MessagesNavigationBar else {
        return nil
    }
    return navigationBar
}

And in viewDidLoad

let rect = CGRect(x: 0, y:0, width: 150, height: 88)
titleView = UIView(frame: rect)
......
titleView?.addSubview(imageView)
......
titleView?.addSubview(label)
navigationBar?.addSubview(titleView!)

But the problem is that I have to remove the subviews when I leave the view, otherwise whatever I add there will be present in the table view as well.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if navigationBar != nil {
        titleView?.removeFromSuperview()
    }
}

Which kinda makes me feel that I'm not doing the right thing and I find difficult to add a fade out animation to those subViews when I leave the conversation. (i.e. native messages app on iOS).

So what is the right way of creating a custom Title Navigation Bar in iOS 12?

Scenes

enter image description here


Solution

  • Creating your custom titleView and assigning it to navigationItem.titleView is what you want. On older systems (pre iOS 11) you just might need to call sizeToFit() on the titleView.

    This way you can create this titleView

    Swift

    override func viewDidLoad() {
        super.viewDidLoad()
    
    
        let imageView = UIImageView()
        NSLayoutConstraint.activate([
            imageView.heightAnchor.constraint(equalToConstant: 20),
            imageView.widthAnchor.constraint(equalToConstant: 20)
        ])
        imageView.backgroundColor = .red
        
        let titleLabel = UILabel()
        titleLabel.text = "Custom title"
        
        let hStack = UIStackView(arrangedSubviews: [imageView, titleLabel])
        hStack.spacing = 5
        hStack.alignment = .center
        
        navigationItem.titleView = hStack
    }
    

    Obj-C

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIImageView *imageView = [[UIImageView alloc] init];
        [NSLayoutConstraint activateConstraints:@[
            [imageView.heightAnchor constraintEqualToConstant:20],
            [imageView.widthAnchor constraintEqualToConstant:20]
        ]];
        imageView.backgroundColor = [UIColor redColor];
    
        UILabel *titleLabel = [[UILabel alloc] init];
        titleLabel.text = @"Custom title";
    
        UIStackView *hStack = [[UIStackView alloc] initWithArrangedSubviews:@[imageView, titleLabel]];
        hStack.spacing = 5;
        hStack.alignment = UIStackViewAlignmentCenter;
        
        self.navigationItem.titleView = hStack;
    }
    

    title view

    You might also need to have the right set of autolayout constraints or use UIStackView.