如何在UINavigationBar中将自定义标题居中显示?

18

我有一个自定义的UINavigationBar标题和一个自定义的返回按钮。我的问题是标题在iPhone上没有居中显示,好像我的返回按钮将标题推向了右侧。你有什么想法如何让它居中显示吗?

int height = self.navigationController.navigationBar.frame.size.height;
int width = self.navigationController.navigationBar.frame.size.width;


UILabel *navLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width + 300, 20)];
navLabel.backgroundColor = [UIColor whiteColor];
navLabel.textColor = [UIColor redColor];
navLabel.font = [UIFont fontWithName:@"Helvetica" size:30];
navLabel.textAlignment = UITextAlignmentCenter;
self.navigationItem.titleView = navLabel;
[navLabel release];
((UILabel *)self.navigationItem.titleView).text = self.title;

谢谢!

编辑 我删除了多余的代码并添加了这张图片:

标题居中的图片

请注意,标题被推到一边以适应按钮....


似乎是一个差异问题。我的标题垂直居中正确。但它的水平居中不正确(按钮将其移动)。如果我删除按钮,一切都完美地居中... - itgiawa
7个回答

42
iOS这样做是因为您初始化的框架总是大于300+宽度像素。它试图将完整框架居中,但该框架比要适配它的空间(因为有按钮)更大,因此标签被推到右侧。
你需要做的是给navLabel的框架提供它所需的最小尺寸。
因此,如果您的文本只有100像素宽,但框架却是400像素,则iOS会尝试将400像素居中放置在导航标题中,并且没有足够的空间。当您将所需的实际100像素大小设置为尺寸时,iOS将正确居中您的标题,因为有足够的空间来居中100像素。
下面的代码段应该帮助您检测框架所需的最小大小,具体取决于您尝试放入的字体和文本。 确保标签的框架尽可能小,但不要超过最大宽度(导航栏的宽度)。
UIFont* titleFont = [UIFont fontWithName:@"Helvetica" size:30];
CGSize requestedTitleSize = [titleText sizeWithAttributes:@{NSFontAttributeName: titleFont}]; 
CGFloat titleWidth = MIN(maxTitleWidth, requestedTitleSize.width);

UILabel *navLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleWidth, 20)];
navLabel.backgroundColor = [UIColor whiteColor];
navLabel.textColor = [UIColor redColor];
navLabel.font = [UIFont fontWithName:@"Helvetica" size:30];
navLabel.textAlignment = NSTextAlignmentCenter;
navLabel.text = titleText;
self.navigationItem.titleView = navLabel;

Swift 5.0

    let titleFont = UIFont.systemFont(ofSize: 17.0)
    let title = "My Title"
    let titleSize = title.size(withAttributes: [.font: titleFont])

    let frame = CGRect(x: 0, y: 0, width: titleSize.width, height: 20.0)
    let titleLabel = UILabel(frame: frame)
    titleLabel.font = titleFont
    titleLabel.textColor = .red
    titleLabel.textAlignment = .center
    titleLabel.text = title
    navigationItem.titleView = titleLabel

1
请注意,在iOS 6及以上版本中,UITextAlignmentCenter已被弃用。您可以使用NSTextAlignmentCenter代替。 - mohlman3
self.navigationItem.titleView - 完美! - hbk
但是如果有足够的文本使得width == maxTitleWidth,那么我们会再次遇到问题,对吧?真正的解决方案不是从frame的宽度中减去返回按钮的宽度(*2)吗? - User
@lxx:我不这么认为。iOS SDK会自动处理导航栏标题视图的居中,因此我们不需要担心框架和按钮大小。 - Bocaxica

5

我之前也遇到了同样的问题。我有一个带有左右按钮的UINavigationbar。 我想在UINavigationbar上居中一个图片。因此,我必须将图像放入一个UIView中,其width为0。 图像本身获得的x值是自身width的一半。

UINavigationItem *item = navigationController.topViewController.navigationItem;
UIView *backView =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 30)];
[img_logo setFrame:CGRectMake(-45, 5, 90, 20)];
[backView addSubview:img_logo];
item.titleView = backView;

惊讶于这个可以工作,但它对我很好用。有趣的技巧,能够在零宽度视图中“隐藏”一个视图。 - anorskdev

3

很好的答案!但是sizeWithFont现在已经被弃用了。现在你需要像这样做。

NSDictionary *textTitleOptions = [NSDictionary dictionaryWithObjectsAndKeys:  [UIFont fontWithName:@"Helvetica" size:30], NSFontAttributeName, nil];

CGSize requestedTitleSize = [[NSString stringWithFormat:@"Your String"] sizeWithAttributes:textTitleOptions];

CGFloat titleWidth = MIN(self.view.frame.size.width, requestedTitleSize.width);

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleWidth, 44)];

2
我使用自动布局约束来解决这个问题:
  1. 将UILabel添加到UIView中。
  2. 设置UILabel的centerX约束:

    self.titleLabelCenterXConstraint = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];

  3. 在UIView的layoutSubviews方法中调整centerX偏移值:

    - (void)layoutSubviews { [super layoutSubviews]; CGFloat screenCenter = CGRectGetMidX([UIScreen mainScreen].bounds); CGFloat diffCenter = screenCenter - CGRectGetMidX(self.frame); self.titleLabelCenterXConstraint.constant = diffCenter; }

  4. 将UIView设置为navigationItem的titleView


试过了,不起作用。标题没有移动。偏移量似乎是正确计算的。 - osxdirk
1
运行得非常好! - odm
更新至 Swift 5override func layoutSubviews() { super.layoutSubviews() let screenCenter = UIScreen.main.bounds.midX let diffCenter = screenCenter - self.frame.midX self.centerConstraint.constant = -diffCenter } - J T

1

@BHuelse的答案中的Swift 3:

let image = UIImage(named: "logo")
let logoView = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 30))
let imageView = UIImageView(frame: CGRect(x: -45, y: 5, width: 90, height: 20))
imageView.image = image
imageView.contentMode = .scaleAspectFit
logoView.addSubview(imageView)
self.navigationItem.titleView = logoView

1

Swift 2.3:

    let tlabel = UILabel(frame: CGRectMake(0, 0, 200, 40))
    tlabel.text = self.title
    tlabel.textColor = UIColor.whiteColor()
    tlabel.font = UIFont.boldSystemFontOfSize(17) //UIFont(name: "Helvetica", size: 17.0)
    tlabel.backgroundColor = UIColor.clearColor()
    tlabel.adjustsFontSizeToFitWidth = true
    tlabel.textAlignment = .Center
    self.navigationItem.titleView = tlabel

0

导航栏中的标题并不居中。它位于导航栏左侧按钮和右侧按钮之间的中心位置。这意味着如果您在左侧放置了一个大按钮,标题将向右移动。

您可以通过为标题添加居中约束并对其进行修改,使其真正成为导航栏的中心来更改中心位置:

// Position the title in the middle of the navbar and not in the middle of buttons
fileprivate func centerTitle () {
    if let navigation = self.navigationController {
        let titleMiddle = navigation.navigationBar.convert(titleViewLabel.frame, from: titleViewLabel.superview).midX
        let diff = navigation.navigationBar.center.x - titleMiddle
        constraintTitleViewCentered.constant += diff
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接