24

I am using a custom title view for a UINavigationBar with the following code:

// Set a label to the nav bar
THLabel *titleLabel = [[THLabel alloc] init];
titleLabel.text = @"Test";
titleLabel.font = [UIFont fontWithName:APP_FONT size:22.0];
titleLabel.frame = CGRectMake(0, 0, 100, 30);
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.textColor = CUSTOM_LIGHT_BLUE;
titleLabel.strokeColor = kStrokeColor;
titleLabel.strokeSize = kStrokeSize;
self.navigationItem.titleView = titleLabel;

The problem is that when presenting a new viewcontroller and then returning to the original view controller this custom view shifts and then re-centers itself. Please see the video for a demonstration of that.

Please see the video here: https://www.youtube.com/watch?v=961CCVQmpJM&feature=youtu.be

I have disabled autoresizing of every subview for the navigation controller with both the storyboard and in code for each view controller:

    // Set the navigation bar hidded on the log in view
    UINavigationController* mainViewController = (UINavigationController*)self.appDelegate.window.rootViewController;
    [mainViewController setNavigationBarHidden:YES];
    [[mainViewController navigationBar] setAutoresizesSubviews:NO];

However it still resizes! How can I stop this - what am I doing wrong? Thanks!

PhilBot
  • 748
  • 18
  • 85
  • 173
  • 1
    Where does that titleView code sit? Have you tried moving it to `viewDidLoad` or `init`? – Dima May 05 '14 at 01:32
  • 1
    I have tried this part of the code (since I don't have THLabel class) in my application and everything is working as expected, no weird movement of the title view: UILabel *titleLabel = [[UILabel alloc] init]; titleLabel.text = @"Test"; titleLabel.font = [UIFont fontWithName:@"Bakersville" size:22.0]; titleLabel.frame = CGRectMake(0, 0, 100, 30); titleLabel.textAlignment = NSTextAlignmentCenter; self.navigationItem.titleView = titleLabel; Could you provide the code of THLabel class so I'd check if I see something wrong there? – Barbara R May 05 '14 at 22:03
  • Thank you for your comment, Barbara. Here is the Github repo where THLabel is hosted: https://github.com/MuscleRumble/THLabel – PhilBot May 07 '14 at 15:20
  • I changed all my THLabels to UILabels and it still has this problem. – PhilBot May 08 '14 at 16:22

7 Answers7

32

It's reproducible for me only if I place setting titleView code in viewWillAppear. Moving it to viewDidLoad fixes the issue

Alex Peda
  • 4,210
  • 3
  • 22
  • 31
5

I would embed the label inside a UIView. Interface Builder doesn't like putting directly a UILabel in the titleView for some reason that may be related to your problem.

Also try to set the autoResizingMask to UIViewAutoresizingFlexibleTopMargin. In my experience any custom view in bars behaves better this way.

Rivera
  • 10,792
  • 3
  • 58
  • 102
  • I've tried all of these suggestions. I added the THLabel to a UIView and added that as the titleView, and changed the autoResizing mask... It is still moving when I change view controllers. – PhilBot May 14 '14 at 16:45
  • What about using a regular `UILabel` without customization? Even just a plain black `UIView` to narrow the possible causes. – Rivera May 15 '14 at 01:18
  • Setting the autoResizingMark as suggested appears to have solved my issue. I had an ImageView, within a standard View, set as the titleView. Declared in ViewDidLoad, and it kept jumping about when popping views. Thanks. – Luke Smith Jan 12 '15 at 14:58
  • Embedding label inside a view removed weird animations (Label was scaling up and down) when pushing and popping VC's. – Mocha Feb 12 '20 at 23:22
1

Expanding on @Alex Peda's answer above, I'm finding that on iOS7, outside of viewDidLoad, there appears to be a minimum title width for a custom title. Here's what I'm doing, below. Note there are a few methods below particular to my code.

#define MAX_TITLE_WIDTH 400
#define MIN_TITLE_WIDTH 150

- (void) setNavBarTitle: (NSString *) newTitle;
{
    if (!newTitle) newTitle = @"";
    NSMutableString *title = [newTitle mutableCopy];

    if (![_titleView.text isEqualToString:title]) {

        NSAttributedString *attrTitle = [UIAppearance attributedString:title withFontType:FontTypeTitle | FontTypeBold | FontTypeItalic size: 40.0 andOtherAttributes:@{NSForegroundColorAttributeName: [UIColor blackColor]}];
        _titleView.attributedText = attrTitle;
        [_titleView sizeToFit];

        // In iOS7, if you set the nav bar title with a custom view outside of viewDidLoad, there appears to be a minimum title width. Narrower custom view titles are not centered properly. I'm working around this by centering the text in the label, and setting the width of the label to the minimum width.
        if ([Utilities ios7OrLater]) {
            if (_titleView.frameWidth < MIN_TITLE_WIDTH) {
                _titleView.textAlignment = NSTextAlignmentCenter;
                _titleView.frameWidth = MIN_TITLE_WIDTH;
            }
        }

        if (_titleView.frameWidth > MAX_TITLE_WIDTH) {
            _titleView.frameWidth = MAX_TITLE_WIDTH;
        }
    }

    self.navigationItem.titleView = _titleView;
}
Chris Prince
  • 7,288
  • 2
  • 48
  • 66
1

This happened for me as well. Two things you could do :

1) make sure navigation setup is done in either viewDidLayoutSubviews or viewDidLoad as mentioned in the above answer

2) i had left and right bar button item as nil, but only called them after the title label was set. make sure to set the right and left bar button items as nil (if you are not using them of course) before setting the titlelabel to titleview.

hellorrr
  • 80
  • 1
  • 6
0

In my case it happened because I was setting UIBarButton before titleView. Setting titleView should be first. Works perfectly now.

Michał Kwiecień
  • 2,734
  • 1
  • 20
  • 23
0

What worked for me was to create a variable in the view controller that holds your desired title view then initialize it in viewDidLoad. Then you can set that view to the self.navigationItem.titleView in viewWillAppear and it should display properly. No need for setting autoResizeMask, or rightBarButtons, etc.

Example:

class ViewController {
    var myTitleImage: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        myTitleImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
        myTitleImage.contentMode = .scaleAspectFit
        myTitleImage.image = #imageLiteral(resourceName: "my_title_image")
        // etc...
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.navigationItem.titleView = self.myTitleImage
    }    
}
Alfonz
  • 1,010
  • 13
  • 26
0

I tried all the answers above, and ended up creating the view and label like this:

  1. I added a UIView variable for my VC:

    let titleView: UIView = {
        // A factory method that returns a customised `UILabel`
        let label = Factory.Label.title() 
    
        let view = UIView()
        view.addSubview(label)
        label.text = localized.mainMenuItemSettings()
        //I use EasyPeasy for my constraints, 
        //but essentially I set top, left, bottom and right anchors to fill my UIView
        label.easy.layout(Edges()) 
        return view
    }()
    
  2. Then in my viewWillAppear(animated:) method I set the view as the titleView of my navigationItem:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationItem.titleView = titleView
    }
    

I use EasyPeasy for constraints as I find them super easy to use and very compact.

Septronic
  • 1,156
  • 13
  • 32