Can't set titleView in the center of navigation bar because back button

IosObjective CUinavigationbar

Ios Problem Overview


I'm using an image view to display an image in my nav bar. The problem is that I can't set it to the center correctly because of the back button. I checked the related questions and had almost the same problem earlier that I solved, but this time I have no idea.

Earlier I solved this problem with fake bar buttons, so I tried to add a fake bar button to the right (and left) side, but it doesn't helped.

- (void) searchButtonNavBar {
    
    
    CGRect imageSizeDummy = CGRectMake(0, 0, 25,25);
    
    UIButton *dummy = [[UIButton alloc] initWithFrame:imageSizeDummy];
    
    UIBarButtonItem
    *searchBarButtonDummy =[[UIBarButtonItem alloc] initWithCustomView:dummy];
    self.navigationItem.rightBarButtonItem = searchBarButtonDummy;
  
}


- (void)setNavBarLogo {
    
    [self setNeedsStatusBarAppearanceUpdate];
    CGRect myImageS = CGRectMake(0, 0, 44, 44);
    UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
    [logo setImage:[UIImage imageNamed:@"color.png"]];
    logo.contentMode = UIViewContentModeScaleAspectFit;
    self.navigationItem.titleView = logo;
    [[UIBarButtonItem appearance] setTitlePositionAdjustment:UIOffsetMake(0.0f, 0.0f) forBarMetrics:UIBarMetricsDefault];
    
}

I think it should be workin fine because in this case the titleView has bar buttons on the same side. Is there any explanation why it worked with bar buttons that was created programmatically but doesn't works with the common back button?

Ios Solutions


Solution 1 - Ios

UINavigationBar automatically centers its titleView as long as there is enough room. If the title isn't centered that means that the title view is too wide to be centered, and if you set the backgroundColor if your UIImageView you'll see that's exactly what is happening.

The title view is too wide because that navigation bar will automatically resize the title to hold its content, using -sizeThatFits:. This means that your title view will always be resized to the size of your image.

Two possible fixes:

  1. The image you're using is way too big. Use a properly sized 44x44 pt image with 2x and 3x versions.

  2. Wrap UIImageView inside of a regular UIView to avoid resizing.

Example:

UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"test.jpeg"]];
imageView.contentMode = UIViewContentModeScaleAspectFit;

UIView* titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
imageView.frame = titleView.bounds;
[titleView addSubview:imageView];

self.navigationItem.titleView = titleView;

Solution 2 - Ios

An example in Swift 3 version of Darren's second way:

let imageView = UIImageView(image: UIImage(named: "test"))
    imageView.contentMode = UIViewContentMode.scaleAspectFit
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
    imageView.frame = titleView.bounds
    titleView.addSubview(imageView)

self.navigationItem.titleView = titleView

Solution 3 - Ios

I suggest you Override the function - (void)setFrame:(CGRect)fram like this:

- (void)setFrame:(CGRect)frame  { 
  
    [super setFrame:frame]; //systom function

    self.center = CGPointMake(self.superview.center.x, self.center.y);   //rewrite function 

}

so that the titleView.center always the right location

Solution 4 - Ios

Don't use titleView.

Just add your image to navigationController.navigationBar

CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:@"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
logo.center = CGPointMake(self.navigationController.navigationBar.width / 2.0, self.navigationController.navigationBar.height / 2.0);
logo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.navigationController.navigationBar addSubview:logo];

Solution 5 - Ios

Qun Li's worked perfectly for me. Here's the swift 2.3 code:

override var frame: CGRect {
    set(newValue) {
        super.frame = newValue
        
        if let superview = self.superview {
            self.center = CGPoint(x: superview.center.x, y: self.center.y)
        }
    }
    
    get {
        return super.frame
    }
}

Solution 6 - Ios

If you're using a custom view from a nib, be sure to disable auto-layout on the nib file.

Solution 7 - Ios

I created a custom UINavigationController that after dropping in, the only thing you have to do is call showNavBarTitle(title:font:) when you want to show and removeNavBarTitle() when you want to hide:

class NavigationController: UINavigationController {
    
    private static var mTitleFont = UIFont(name: <your desired font (String)> , size: <your desired font size -- however, font size will automatically adjust so the text fits in the label>)!
    private static var mNavBarLabel: UILabel = {
        
        let x: CGFloat = 60
        let y: CGFloat = 7
        let label = UILabel(frame: CGRect(x: x, y: y, width: UIScreen.main.bounds.size.width - 2 * x, height: 44 - 2 * y))

        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = 0.5
        label.font = NavigationController.mTitleFont
        label.numberOfLines = 0
        label.textAlignment = .center
        
        return label
    }()
    
    func showNavBarLabel(title: String, font: UIFont = mTitleFont) {
        NavigationController.mNavBarLabel.text = title
        NavigationController.mNavBarLabel.font = font
        navigationBar.addSubview(NavigationController.mNavBarLabel)
    }
    
    func removeNavBarLabel() {
        NavigationController.mNavBarLabel.removeFromSuperview()
    }
}

I find the best place to call showNavBarTitle(title:font:) and removeNavBarTitle() are in the view controller's viewWillAppear() and viewWillDisappear() methods, respectively:

class YourViewController: UIViewController {
    
    func viewWillAppear() {
        (navigationController as! NavigationController).showNavBarLabel(title: "Your Title")
    }

    func viewWillDisappear() {
        (navigationController as! NavigationController).removeNavBarLabel()
    }
}

Solution 8 - Ios

  1. You can try setting your image as UINavigationBar's background image by calling

    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"color.png"] forBarMetrics:UIBarMetricsDefault];

inside the viewDidLoad method.

That way it will be always centered, but if you have back button with long title as left navigation item, it can appear on top of your logo. And you should probably at first create another image with the same size as the navigation bar, then draw your image at its center, and after that set it as the background image.

  1. Or instead of setting your image view as titleView, you can try simply adding at as a subview, so it won't have the constraints related to right and left bar button items.

Solution 9 - Ios

In Swift, this is what worked for me however it´s not the best solution (basically, add it up to navigationBar):

    let titleIV = UIImageView(image: UIImage(named:"some"))
        titleIV.contentMode = .scaleAspectFit
        titleIV.translatesAutoresizingMaskIntoConstraints = false
   
      if let navigationController = self.navigationController{
            navigationController.navigationBar.addSubview(titleIV)
            titleIV.centerXAnchor.constraint(equalTo:  
navigationController.navigationBar.centerXAnchor).isActive = true
            titleIV.centerYAnchor.constraint(equalTo: navigationController.navigationBar.centerYAnchor).isActive = true
      }
      else{
            view.addSubview(titleIV)
            titleIV.topAnchor.constraint(equalTo: view.topAnchor, constant: UIApplication.shared.statusBarFrame.height).isActive = true
            titleIV.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

      }

Solution 10 - Ios

Extending Darren's answer, a fix for me was to return a sizeThatFits with the UILabel size. It turns out that this is called after layoutSubViews so the label has a size.

override func sizeThatFits(_ size: CGSize) -> CGSize {
    return CGSize(width: titleLabel.frame.width + titleInset*2, height: titleLabel.frame.height)
}

Also note that I have + titleInset*2 because Im setting the horizontal constraints like so:

titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: titleInset),
titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -titleInset)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionriheView Question on Stackoverflow
Solution 1 - IosDarrenView Answer on Stackoverflow
Solution 2 - IospedrouanView Answer on Stackoverflow
Solution 3 - IosQun LiView Answer on Stackoverflow
Solution 4 - IosPowHuView Answer on Stackoverflow
Solution 5 - IosMikeBView Answer on Stackoverflow
Solution 6 - IosjoslinmView Answer on Stackoverflow
Solution 7 - IosshoeView Answer on Stackoverflow
Solution 8 - IosbzzView Answer on Stackoverflow
Solution 9 - IosFrancisco José García VillaránView Answer on Stackoverflow
Solution 10 - IosrichyView Answer on Stackoverflow