Reputation: 799
Is there a straightforward way of overriding the titleView of the current navigation bar item in a navigation bar within a navigation controller? I've tried creating a new UIView and replacing the titleView property of topView with my own UIVIew with no success.
Basically, I want a multi-line title for the navigation bar title. Any suggestions?
Upvotes: 61
Views: 44956
Reputation: 6021
Swift 5+ https://stackoverflow.com/a/68739808/6881070
very easy and smooth solution in one function link is mention
Upvotes: 0
Reputation: 129
In Swift 5,
let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width * 0.75, height: 44))
let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width * 0.75, height: 44))
label.backgroundColor = .clear
label.numberOfLines = 2
label.font = UIFont.boldSystemFont(ofSize: 16.0)
label.textAlignment = .center
label.textColor = .white
label.text = "multi line text"
wrapperView.addSubview(label)
self.navigationItem.titleView = wrapperView
Upvotes: 3
Reputation: 14722
Swift 4
extension UINavigationItem {
@objc func setTwoLineTitle(lineOne: String, lineTwo: String) {
let titleParameters = [NSAttributedStringKey.foregroundColor : UIColor.white,
NSAttributedStringKey.font : UIFont.boldSystemFont(ofSize: 17)] as [NSAttributedStringKey : Any]
let subtitleParameters = [NSAttributedStringKey.foregroundColor : UIColor.flatWhite(),
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 12)] as [NSAttributedStringKey : Any]
let title:NSMutableAttributedString = NSMutableAttributedString(string: lineOne, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: lineTwo, attributes: subtitleParameters)
title.append(NSAttributedString(string: "\n"))
title.append(subtitle)
let size = title.size()
let width = size.width
let height = CGFloat(44)
let titleLabel = UILabel(frame: CGRect.init(x: 0, y: 0, width: width, height: height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
titleView = titleLabel
}
}
Font, color, and navigation bar height are hardcoded here.
Upvotes: 3
Reputation: 1081
for Swift:
let label = UILabel(frame: CGRectMake(0, 0, UIScreen.main.bounds.width, 44))
label.backgroundColor = UIColor.clearColor()
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.Center
label.text = "multiline string"
self.navigationItem.titleView = label
for swift 4:
let label = UILabel(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: 44.0))
label.backgroundColor = UIColor.clear
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.center
label.text = "first line\nsecond line"
self.navigationItem.titleView = label
Upvotes: 30
Reputation: 2503
Most of the solutions, except the one from @gbk, use hardcoded height 44pt for the UIView (wrapper view) and UILabel. All are created by codes. I overlooked @gbk solution which dynamically read height of navigation bar.
I ran into problem when orientation = landscape
on iOS 11 (iPhone 5s). The label's height won't adjust and when I set one line of text for landscape, the text align to bottom of navigation bar.
Somehow I found that I can add the UILabel in Storyboard and create an IBOutlet for that. Isn't that nicer?
Navigation Item
on the scene panel (on left).IBOutlet
on your view controller and you can use it as usual. To have different size of text, use attribute string (lots of solutions above).I tested on iPhone 5s with iOS 11.2.6 and the text just position in center with no problem, work fine on portrait and landscape.
Upvotes: 1
Reputation: 7932
Here's a Swift 4 way of doing it-
let upperTitle = NSMutableAttributedString(string: "\(text1)", attributes: [NSAttributedStringKey.font: UIFont(name: "SFProDisplay-Heavy", size: 17)!])
let lowerTitle = NSMutableAttributedString(string: "\n\((text2)!)", attributes: [NSAttributedStringKey.font: UIFont(name: "SFProText-Light", size: 11)! , NSAttributedStringKey.foregroundColor: UIColor(hex: "#607D8B")])
upperTitle.append(lowerTitle)
let label1 = UILabel(frame: CGRect(x: 0, y: 0, width: 400, height:44))
label1.numberOfLines = 0
label1.textAlignment = .center
label1.attributedText = upperTitle //assign it to attributedText instead of text
self.navigationItem.titleView = label1
Upvotes: 3
Reputation: 457
Here is a Swift 3 version of handling a multiline title:
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
label.backgroundColor = .clear
label.numberOfLines = 0
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 14.0)
label.text = "This is a Multi-Line title of UINavigationBar"
self.navigationItem.titleView = label
}
Upvotes: 3
Reputation: 910
What to do when label is not centered
If you encounter same issue as me - that label is not centered in navigationItem because of back button, embed your UILabel to UIView. UILabel is then not forced to grow with it's text, but stop growing when it's width raise view's width. More about this issue you can find here: Can't set titleView in the center of navigation bar because back button ( Darren's answer )
Not centered:
- (void)setTwoLineTitle:(NSString *)titleText color:(UIColor *)color font:(UIFont *)font {
CGFloat titleLabelWidth = [UIScreen mainScreen].bounds.size.width/2;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleLabelWidth, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = font;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.textColor = color;
label.text = titleText;
self.navigationItem.titleView = label;
}
Centered:
- (void)setTwoLineTitle:(NSString *)titleText color:(UIColor *)color font:(UIFont *)font {
CGFloat titleLabelWidth = [UIScreen mainScreen].bounds.size.width/2;
UIView *wrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, titleLabelWidth, 44)];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleLabelWidth, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = font;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.textColor = color;
label.text = titleText;
[wrapperView addSubview:label];
self.navigationItem.titleView = wrapperView;
}
Upvotes: 5
Reputation: 11184
Swift solution:
2 lines in NavigationBar
:
private func setupTitleView() {
let topText = NSLocalizedString("key", comment: "")
let bottomText = NSLocalizedString("key", comment: "")
let titleParameters = [NSForegroundColorAttributeName : UIColor.<Color>(),
NSFontAttributeName : UIFont.<Font>]
let subtitleParameters = [NSForegroundColorAttributeName : UIColor.<Color>(),
NSFontAttributeName : UIFont.<Font>]
let title:NSMutableAttributedString = NSMutableAttributedString(string: topText, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: bottomText, attributes: subtitleParameters)
title.appendAttributedString(NSAttributedString(string: "\n"))
title.appendAttributedString(subtitle)
let size = title.size()
let width = size.width
guard let height = navigationController?.navigationBar.frame.size.height else {return}
let titleLabel = UILabel(frame: CGRectMake(0,0, width, height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .Center
navigationItem.titleView = titleLabel
}
2 line in BarButton
let string = NSLocalizedString("key", comment: "")
let attributes = [NSForegroundColorAttributeName : UIColor.<Color>,
NSFontAttributeName : UIFont.<Font>]
let size = (string as NSString).sizeWithAttributes(attributes)
guard let height = navigationController?.navigationBar.frame.size.height else {return}
let button:UIButton = UIButton(frame: CGRectMake(0, 0, size.width, height))
button.setAttributedTitle(NSAttributedString(string: string, attributes: attributes), forState: .Normal)
button.addTarget(self, action: #selector(<SELECTOR>), forControlEvents: .TouchUpInside)
button.titleLabel?.numberOfLines = 0
button.titleLabel?.textAlignment = .Right
let rightBarButton = UIBarButtonItem(customView: button)
navigationItem.rightBarButtonItem = rightBarButton
result -
Upvotes: 25
Reputation: 4345
After a lot of tweaking, I still couldn't get petert's solution to work for me in iOS 8. Here's a copy-paste-able solution for iOS 8/9. Credit goes to Matt Curtis's github post
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if(!self.navigationItem.titleView){
self.navigationItem.titleView = ({
UILabel *titleView = [UILabel new];
titleView.numberOfLines = 0;
titleView.textAlignment = NSTextAlignmentCenter;
titleView.attributedText = [[NSAttributedString alloc] initWithString:@"2\nLINES" attributes:
self.navigationController.navigationBar.titleTextAttributes
];
[titleView sizeToFit];
// You'll need to set your frame otherwise if your line breaks aren't explcit.
titleView;
});
}
}
Upvotes: 7
Reputation: 6692
Set the titleView
property of the UINavigationItem
. For example, in the view controller's viewDidLoad
method you could do something like:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 480, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = [UIFont boldSystemFontOfSize: 14.0f];
label.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5];
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.text = @"This is a\nmultiline string";
self.navigationItem.titleView = label;
#if !__has_feature(objc_arc)
[label release];
#endif
It shows up like this:
Remember the titleView
property is ignored if leftBarButtonItem
is not nil
.
Upvotes: 86