I was using Rodrigo Pedroso's answer, but the fact that it doesn't truncate long titles became a problem. I made a version that measures how much space there is for the title between the bar button items, and then manually truncates the title to fit in that space. The truncation code was adapted from this answer, and is a bit of a mess - maybe someone can help figure out a way to use UILabel's built-in truncation methods (i.e. .ByTruncatingTail). I've already spent too much time on this though.
Here's what I've got:
extension UIViewController {
private var availableWidthForTitle: CGFloat {
get {
var total = UIScreen.mainScreen().bounds.width
if let navigationBar = navigationController?.navigationBar {
var maxYLeft: CGFloat = 0
var minYRight: CGFloat = 0
for subview in navigationBar.subviews.dropFirst()
where !subview.hidden {
if subview.frame.origin.x < total / 2 {
let rightEdge = subview.frame.origin.x + subview.frame.size.width
if rightEdge < total / 2 {
maxYLeft = max(maxYLeft, rightEdge)
}
} else {
let leftEdge = subview.frame.origin.x
if leftEdge > total / 2 {
minYRight = min(minYRight, total - leftEdge)
}
}
}
total = total - maxYLeft - minYRight
}
return total
}
}
func setTitle(title:String, subtitle:String?) {
if (subtitle == nil) {
self.title = title.uppercaseString
} else {
let titleLabel = UILabel(frame: CGRectMake(0, -2, 0, 0))
titleLabel.backgroundColor = UIColor.clearColor()
titleLabel.textColor = MaterialColor.white
titleLabel.font = RobotoFont.boldWithSize(17)
titleLabel.textAlignment = .Center
let availableWidth = availableWidthForTitle
let titleUppercase = NSString(string: title.uppercaseString)
if titleUppercase.boundingRectWithSize(
// if title needs to be truncated
CGSize(width: CGFloat.max, height: CGFloat.max),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName : titleLabel.font],
context: nil).width > availableWidth {
let truncTitle: NSMutableString = ""
for nextChar in title.uppercaseString.characters {
if truncTitle.boundingRectWithSize(
CGSize(width: CGFloat.max, height: CGFloat.max),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName : titleLabel.font],
context: nil).width > availableWidth - 16 { // for ellipsis + some margin
truncTitle.appendString("...")
break
} else {
truncTitle.appendString(String(nextChar))
}
}
titleLabel.text = truncTitle as String
} else {
// use title as-is
titleLabel.text = titleUppercase as String
}
titleLabel.sizeToFit()
let subtitleLabel = UILabel(frame: CGRectMake(0, 18, 0, 0))
subtitleLabel.backgroundColor = UIColor.clearColor()
subtitleLabel.textColor = MaterialColor.white
subtitleLabel.font = MaterialFont.italicSystemFontWithSize(10)
subtitleLabel.text = subtitle
subtitleLabel.sizeToFit()
let titleView = UIView(frame: CGRectMake(0, 0, max(titleLabel.frame.size.width, subtitleLabel.frame.size.width), 30))
// Center title or subtitle on screen (depending on which is larger)
if titleLabel.frame.width >= subtitleLabel.frame.width {
var adjustment = subtitleLabel.frame
adjustment.origin.x = titleView.frame.origin.x + (titleView.frame.width/2) - (subtitleLabel.frame.width/2)
subtitleLabel.frame = adjustment
} else {
var adjustment = titleLabel.frame
adjustment.origin.x = titleView.frame.origin.x + (titleView.frame.width/2) - (titleLabel.frame.width/2)
titleLabel.frame = adjustment
}
titleView.addSubview(titleLabel)
titleView.addSubview(subtitleLabel)
self.navigationItem.titleView = titleView
}
}
}