4
votes

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

2
“By needing this I can't use something like in viewDidLoad” why can’t you? That’s the normal and right thing to do...matt
Sorry, I was ambiguous, and I edited the question. So, if I use navigationItem.titleView = titleView I cannot change the height of the titleView which is by default 44.Sebastian Corneliu Vîrlan

2 Answers

15
votes

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.

3
votes

These lines have no effect on the size of a title view:

let rect = CGRect(x: 0, y:0, width: 150, height: 88)
titleView = UIView(frame: rect)

Instead (or in addition) give your title view a width constraint and a height constraint. That is how the runtime knows what size you want.