UINavigationBar TitleView с субтитрами

Я хочу titleView внутри моего UINavigationBar, который имеет две строки текста вместо одной

Моя текущая реализация работает хорошо, когда у меня есть кнопка «Назад» и кнопка «Редактировать», потому что она выглядит почти по центру. Проблема в том, когда у меня есть только одна из кнопок. Это выглядит действительно странно и неправильно, потому что представление центрируется на доступном пространстве.

UINavigationBar с левой и правой кнопкой

UINavigationBar только с одной кнопкой

Это моя текущая реализация:

CGRect frame = self.navigationController.navigationBar.frame;

UIView *twoLineTitleView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetWidth(frame), 0, CGRectGetWidth(frame), CGRectGetHeight(frame))];

UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 8, CGRectGetWidth(frame), 14)];
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
titleLabel.textAlignment = UITextAlignmentCenter;
titleLabel.text = self.title;
[twoLineTitleView addSubview:titleLabel];

UILabel *subTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 23, CGRectGetWidth(frame), 14)];
subTitleLabel.backgroundColor = [UIColor clearColor];
subTitleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
subTitleLabel.textAlignment = UITextAlignmentCenter;
subTitleLabel.text = self.subTitle;
[twoLineTitleView addSubview:subTitleLabel];

self.navigationItem.titleView = twoLineTitleView;

Ответы на вопрос(9)

 lostintranslation12 мая 2016 г., 21:12
@ Бобнобл правильно и согласен. ser попросил субтитры, а это не то, что Apple намеревается при использовании подсказки
 lostintranslation12 мая 2016 г., 02:25
За исключением подсказки отображается в верхней части заголовка навигации.
 bobnoble12 мая 2016 г., 19:42
@lostintranslation Я предполагаю, что вы имеете в виду над заголовком навигации ... да, это то, что делает приглашение. Если вы ищете что-то еще, то подсказка не является правильным решением для вас.
 Michael Peterson22 авг. 2014 г., 06:20
Да. Использование строки приглашения является хорошим решением для многих основных случаев использования. Абсолютно бесплатно и просто работает.

https://www.reddit.com/r/swift/comments/3izq7b/style_guidelines_for_navigation_bar_prompts/


    //Make the title 2 lines with a title and a subtitle
    func addTitleAndSubtitleToNavigationBar (title: String, subtitle: String) {
        var label = UILabel(frame: CGRectMake(10.0, 0.0, 50.0, 40.0))
        label.font = UIFont.boldSystemFontOfSize(14.0)
        label.numberOfLines = 2
        label.text = "\(title)\n\(subtitle)"
        label.textColor = UIColor.blackColor()
        label.sizeToFit()
        label.textAlignment = NSTextAlignment.Center
        self.titleView = label
    }
}

результата:

let subtitleAttribute = [NSForegroundColorAttributeName: UIColor.grayColor() , NSFontAttributeName: UIFont.systemFontOfSize(12.0)]
let titleString = NSMutableAttributedString(string: "title" + "\n", attributes: [NSFontAttributeName: UIFont.boldSystemFontOfSize(17.0)])
let subtitleString = NSAttributedString(string: "subtitle", attributes: subtitleAttribute)
titleString.appendAttributedString(subtitleString)

let label = UILabel(frame: CGRectMake(0, 0, titleString.size().width, 44))
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.Center
label.attributedText = titleString
self.navigationItem.titleView = label

Свифт 3

let subtitleAttribute = [NSForegroundColorAttributeName: UIColor.gray , NSFontAttributeName: UIFont.systemFont(ofSize: 12.0)]
let titleString = NSMutableAttributedString(string: "title" + "\n", attributes: [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 17.0)])
let subtitleString = NSAttributedString(string: "subtitle", attributes: subtitleAttribute)
titleString.append(subtitleString)

let label = UILabel(frame: CGRect(x: 0, y: 0, width: titleString.size().width, height: 44))
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.center
label.attributedText = titleString
self.navigationItem.titleView = label

ответ, но тот факт, что он не усекает длинные заголовки, стал проблемой. Я сделал версию, которая измеряет, сколько места остается для заголовка между элементами панели кнопок, а затем вручную обрезает заголовок, чтобы уместить его в этом пространстве. Код усечения был адаптирован изэтот ответ, и это немного путаница - может быть, кто-то может помочь найти способ использовать встроенные методы усечения UILabel (т.е. .ByTruncatingTail). Я уже потратил слишком много времени на это, хотя.

Вот что у меня есть:

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
        }
    }
}

Используйте sizeToFit для каждого из ваших двух ярлыков.Установите ширину вида контейнера на максимальную ширину ваших двух меток.Сосредоточьте меньшую из двух меток на представлении.

Этот код должен работать для вас:

    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
    titleLabel.backgroundColor = [UIColor clearColor];
    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.font = [UIFont boldSystemFontOfSize:20];
    titleLabel.text = @"Your Title";
    [titleLabel sizeToFit];

    UILabel *subTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 22, 0, 0)];
    subTitleLabel.backgroundColor = [UIColor clearColor];
    subTitleLabel.textColor = [UIColor whiteColor];
    subTitleLabel.font = [UIFont systemFontOfSize:12];
    subTitleLabel.text = @"Your subtitle";
    [subTitleLabel sizeToFit];

    UIView *twoLineTitleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MAX(subTitleLabel.frame.size.width, titleLabel.frame.size.width), 30)];
    [twoLineTitleView addSubview:titleLabel];
    [twoLineTitleView addSubview:subTitleLabel];

    float widthDiff = subTitleLabel.frame.size.width - titleLabel.frame.size.width;

    if (widthDiff > 0) {
        CGRect frame = titleLabel.frame;
        frame.origin.x = widthDiff / 2;
        titleLabel.frame = CGRectIntegral(frame);
    }else{
        CGRect frame = subTitleLabel.frame;
        frame.origin.x = abs(widthDiff) / 2;
        subTitleLabel.frame = CGRectIntegral(frame);
    }

    self.navigationItem.titleView = twoLineTitleView;

Swift 3 вариант:

    let titleLabel = UILabel.init(frame: CGRect.zero)
    titleLabel.backgroundColor = UIColor.clear
    titleLabel.textColor = UIColor.schBlack
    titleLabel.font = UIFont.schTextStyle2Font()
    titleLabel.text = titleString;
    titleLabel.sizeToFit()

    let subTitleLabel = UILabel.init(frame: CGRect.init(x: 0, y: 22, width: 0, height: 0))
    subTitleLabel.backgroundColor = UIColor.clear
    subTitleLabel.textColor = UIColor.schBrownishGrey
    subTitleLabel.font = UIFont.schTextStyle3Font()
    subTitleLabel.text = subTitleString;
    subTitleLabel.sizeToFit()

    let twoLineTitleView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.frame.width-40, height: 30))
    twoLineTitleView.addSubview(titleLabel)
    twoLineTitleView.addSubview(subTitleLabel)

    /*

    float widthDiff = subTitleLabel.frame.size.width - titleLabel.frame.size.width;
    if (widthDiff > 0) {
        CGRect frame = titleLabel.frame;
        frame.origin.x = widthDiff / 2;
        titleLabel.frame = CGRectIntegral(frame);
    }else{
        CGRect frame = subTitleLabel.frame;
        frame.origin.x = abs(widthDiff) / 2;
        subTitleLabel.frame = CGRectIntegral(frame);
    }
    */

    self.navigationItem.titleView = twoLineTitleView;
 Mbt92504 мая 2017 г., 15:25
Можно ли использовать Auto Layout для этих двух меток?
 Ahmad20 апр. 2018 г., 01:35
@ Mbt925 (для людей, гуглящих это). Вы можете использовать метки, хотя вид стека, вероятно, является лучшим выбором для оболочки, вы можете установить выравнивание и распределение на .fill и установить ограничения высоты для меток. Размер стека будет изменен с помощью панели навигации.

полностью заменив заголовок. Конечный результат выглядит так:

Это может быть достигнуто с помощью следующего кода вviewDidLoad метод.

UIView * containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 220, 40)];

UILabel * titleLabel = [[UILabel alloc] init];
titleLabel.text = @"Ttitle";
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.font = [UIFont boldSystemFontOfSize:titleLabel.font.pointSize];

[containerView addSubview:titleLabel];
titleLabel.keepInsets.equal = 0;
titleLabel.keepBottomInset.equal = 10;

UILabel * subtitleLabel = [[UILabel alloc] init];
subtitleLabel.textAlignment = NSTextAlignmentCenter;
subtitleLabel.font = [UIFont italicSystemFontOfSize:12.0];
subtitleLabel.textColor = [UIColor lightGrayColor];

[containerView addSubview:subtitleLabel];

subtitleLabel.keepHeight.equal = 15;
subtitleLabel.keepWidth.equal = 200;
subtitleLabel.keepBottomInset.equal = 0;
subtitleLabel.keepHorizontalCenter.equal = 0.5;

subtitleLabel.text = @"Subtitle";

[self.navigationItem setTitleView:containerView];

Для этого я использовал отличныйKeepLayout библиотека. Это дает такой результат:

GitHub! Вот.

Я обнаружил, что когда мои субтитры были меньше, чем мой заголовок, они не могли правильно их отформатировать, вот правильный код.

//Usage:
self.navigationItem.titleView = setTitle(varDec.currentDate(), subtitle: varDec.currentDay())

 func setTitle(title:String, subtitle:String) -> UIView {
    //Create a label programmatically and give it its property's
    let titleLabel = UILabel(frame: CGRectMake(0, -5, 0, 0)) //x, y, width, height where y is to offset from the view center
    titleLabel.backgroundColor = UIColor.clearColor()
    titleLabel.textColor = UIColor.blackColor()
    titleLabel.font = UIFont.boldSystemFontOfSize(17)
    titleLabel.text = title
    titleLabel.sizeToFit()

    //Create a label for the Subtitle
    // x, y, width, height where x is set to be half the size of the title (100/4 = 25%) as it starts all the way left.
    let subtitleLabel = UILabel(frame: CGRectMake(titleLabel.frame.size.width / 4, 18, 0, 0)) 
    subtitleLabel.backgroundColor = UIColor.clearColor()
    subtitleLabel.textColor = UIColor.lightGrayColor()
    subtitleLabel.font = UIFont.systemFontOfSize(12)
    subtitleLabel.text = subtitle
    subtitleLabel.sizeToFit()

    /*Create a view and add titleLabel and subtitleLabel as subviews setting 
     * its width to the bigger of both
     * this will crash the program if subtitle is bigger then title
     */
    let titleView = UIView(frame: CGRectMake(0, 0, max(titleLabel.frame.size.width, subtitleLabel.frame.size.width), 30))
    titleView.addSubview(titleLabel)
    titleView.addSubview(subtitleLabel)


    return titleView
}

чтобы лучше централизовать заголовок и субтитры.

func setTitle(title:String, subtitle:String) -> UIView {
    //Create a label programmatically and give it its property's
    let titleLabel = UILabel(frame: CGRectMake(0, 0, 0, 0)) //x, y, width, height where y is to offset from the view center
    titleLabel.backgroundColor = UIColor.clearColor()
    titleLabel.textColor = UIColor.blackColor()
    titleLabel.font = UIFont.boldSystemFontOfSize(17)
    titleLabel.text = title
    titleLabel.sizeToFit()

    //Create a label for the Subtitle
    let subtitleLabel = UILabel(frame: CGRectMake(0, 18, 0, 0))
    subtitleLabel.backgroundColor = UIColor.clearColor()
    subtitleLabel.textColor = UIColor.lightGrayColor()
    subtitleLabel.font = UIFont.systemFontOfSize(12)
    subtitleLabel.text = subtitle
    subtitleLabel.sizeToFit()

    // Create a view and add titleLabel and subtitleLabel as subviews setting
    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)

    return titleView
}
 Luan Castro23 февр. 2017 г., 21:54
Работает идеально для меня! Спасибо!
 Utku Dalmaz03 мая 2016 г., 22:12
Когда я вызываю эту функцию внутри замыкания, она показывает заголовок и субтитры слева, затем перемещает их в центр за секунду, что выглядит плохо, как это исправить?
 Phlippie Bosman01 авг. 2016 г., 08:43
Я хотел бы, чтобы решение, которое обращается к двум проблемам, упомянутым в комментариях
 Craig.Pearce06 мар. 2016 г., 06:21
Я нашел это решение работает лучше всего. Другие решения здесь потерпят неудачу, когдаtitleLabel длиннее определенного количества символов и будет отбрасыватьсяsubtitleLabel по какой-то причине. Это решение хоть и не идеальное (долгоtitleLabels будет убегать с экрана) это лучшее, что я видел до сих пор.

я добавил кладочную реализацию.

CGFloat width = 200;
CGFloat titleHeight = 25;
CGFloat containerHeight = 40;

UIView * containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, containerHeight)];
UILabel * titleLabel = [[UILabel alloc] init];
titleLabel.text = @"Title";
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.font = [UIFont boldSystemFontOfSize:titleLabel.font.pointSize];

[containerView addSubview:titleLabel];
[titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.mas_equalTo(0);
    make.left.mas_equalTo(0);
    make.right.mas_equalTo(0);
    make.width.mas_equalTo(width);
    make.height.mas_equalTo(titleHeight);

}];

UILabel * subtitleLabel = [[UILabel alloc] init];
subtitleLabel.textAlignment = NSTextAlignmentCenter;
subtitleLabel.font = [UIFont systemFontOfSize:12];

[containerView addSubview:subtitleLabel];

[subtitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.mas_equalTo(0);
    make.right.mas_equalTo(0);
    make.top.mas_equalTo(titleLabel.mas_bottom);
    make.width.mas_equalTo(width);
    make.height.mas_equalTo(containerHeight - titleHeight);

}];

subtitleLabel.text = @"Subtitle";

[self.navigationItem setTitleView:containerView];

Ваш ответ на вопрос