Добавьте субтитры под заголовком в панель навигации контроллера в Xcode

Поэтому я хочу добавить "подзаголовок" под заголовком на панели навигации в контроллере навигации.

В основном все, что я смотрю до сих пор, хочет, чтобы я использовал CGRect. Я не очень много знаю, что это такое, и звучит так, будто я хочу, чтобы я создал совершенно новый взгляд, а это не то, чего я хочу.

У меня вопрос, есть ли точечный метод для добавления представления субтитров легко?

Самая близкая вещь, которую я нашел, была размещена на переполнении стека, и вот ссылка:

Создать субтитр на панели навигации

Видимо в прошлом году это сработало, но теперь я получаю ошибки, и это в моем viewDidLoad...

Я попробовал это:

self.navigationController?.navigationItem.prompt = "Субтитры здесь"

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

С другой стороны, Swift является предпочтительным. Спасибо!

11 ответов

Решение

Хотя есть решение, но у него есть известные проблемы

Решение - написать такую ​​функцию

func setTitle(title:String, subtitle:String) -> UIView {
    let titleLabel = UILabel(frame: CGRectMake(0, -2, 0, 0))

    titleLabel.backgroundColor = UIColor.clearColor()
    titleLabel.textColor = UIColor.grayColor()
    titleLabel.font = UIFont.boldSystemFontOfSize(17)
    titleLabel.text = title
    titleLabel.sizeToFit()

    let subtitleLabel = UILabel(frame: CGRectMake(0, 18, 0, 0))
    subtitleLabel.backgroundColor = UIColor.clearColor()
    subtitleLabel.textColor = UIColor.blackColor()
    subtitleLabel.font = UIFont.systemFontOfSize(12)
    subtitleLabel.text = subtitle
    subtitleLabel.sizeToFit()

    let titleView = UIView(frame: CGRectMake(0, 0, max(titleLabel.frame.size.width, subtitleLabel.frame.size.width), 30))
    titleView.addSubview(titleLabel)
    titleView.addSubview(subtitleLabel)

    let widthDiff = subtitleLabel.frame.size.width - titleLabel.frame.size.width

    if widthDiff < 0 {
        let newX = widthDiff / 2
        subtitleLabel.frame.origin.x = abs(newX)
    } else {
        let newX = widthDiff / 2
        titleLabel.frame.origin.x = newX
    }

    return titleView
}

Использование этой функции для пользовательского просмотра заголовка в viewDidLoad

self.navigationItem.titleView = setTitle("Title", subtitle: "SubTitle")

Единственная известная проблема состоит в том, что, если субтитры становятся очень большими, происходит неправильное размещение.

Окончательный результат введите описание изображения здесь

Источник: https://gist.github.com/nazywamsiepawel/0166e8a71d74e96c7898

Вот моя версия, использующая вид стека на расширение.

extension UINavigationItem {



     func setTitle(title:String, subtitle:String) {

            let one = UILabel()
            one.text = title
            one.font = UIFont.systemFont(ofSize: 17)
            one.sizeToFit()

            let two = UILabel()
            two.text = subtitle
            two.font = UIFont.systemFont(ofSize: 12)
            two.textAlignment = .center
            two.sizeToFit()



            let stackView = UIStackView(arrangedSubviews: [one, two])
            stackView.distribution = .equalCentering
            stackView.axis = .vertical

            let width = max(one.frame.size.width, two.frame.size.width)
            stackView.frame = CGRect(x: 0, y: 0, width: width, height: 35)

            one.sizeToFit()
            two.sizeToFit()



            self.titleView = stackView
        }
    }

Расширение Swift 4 @iosjillian прекрасно работает, добавив немного больше, чтобы учитывать внешний вид панели и пользовательские настройки шрифта:

import UIKit

extension UINavigationItem {

  func setTitle(_ title: String, subtitle: String) {
    let appearance = UINavigationBar.appearance()
    let textColor = appearance.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor ?? .black

    let titleLabel = UILabel()
    titleLabel.text = title
    titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
    titleLabel.textColor = textColor

    let subtitleLabel = UILabel()
    subtitleLabel.text = subtitle
    subtitleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.subheadline)
    subtitleLabel.textColor = textColor.withAlphaComponent(0.75)

    let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
    stackView.distribution = .equalCentering
    stackView.alignment = .center
    stackView.axis = .vertical

    self.titleView = stackView
  }
}

Большое спасибо за ответ! @RajanMaheshwari

Ваше кодирование работает отлично, за исключением оператора if, который вы сделали с widthDiff.

Я немного подкорректировал, и все работало гладко.

if widthDiff < 0 {
        let newX = widthDiff / 2
        subtitleLabel.frame.origin.x = abs(newX)
    } else {
        let newX = widthDiff / 2
        titleLabel.frame.origin.x = newX
    }

Еще раз спасибо за Ваш ответ!

Мне очень понравился ответ @user2325031, но я обнаружил, что подбирать метки по размеру и устанавливать рамку не нужно. Я также установил выравнивание stackView на.center согласно предложению @GerardoMR.

extension UINavigationItem {

    func setTitle(_ title: String, subtitle: String) {
        let titleLabel = UILabel()
        titleLabel.text = title
        titleLabel.font = .systemFont(ofSize: 17.0)
        titleLabel.textColor = .black

        let subtitleLabel = UILabel()
        subtitleLabel.text = subtitle
        subtitleLabel.font = .systemFont(ofSize: 12.0)
        subtitleLabel.textColor = .gray

        let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
        stackView.distribution = .equalCentering
        stackView.alignment = .center
        stackView.axis = .vertical

        self.titleView = stackView
    }

}

В случае, если кто-то ищет код Objective-C вышеупомянутого решения

    UILable *title = [[UILabel alloc]init];
    UILabel *subtitle = [[UILabel alloc]init];

   [title setFont:[UIFont systemFontOfSize:12]];
   [title setTextColor:[UIColor whiteColor]];
   [title setFont:[UIFont systemFontOfSize:17]];
   [title sizeToFit];
   title.text = @"Title";

   [subtitle setTextColor:[UIColor whiteColor]];
   [subtitle setFont:[UIFont systemFontOfSize:12]];
   [subtitle setTextAlignment:NSTextAlignmentCenter];
   [subtitle sizeToFit];
   subtitle.text = @"Subtitle Title";

   UIStackView *stackVw = [[UIStackView alloc]initWithArrangedSubviews:@[title,subtitle]];
   stackVw.distribution = UIStackViewDistributionEqualCentering;
   stackVw.axis = UILayoutConstraintAxisVertical;
   stackVw.alignment =UIStackViewAlignmentCenter;


   [stackVw setFrame:CGRectMake(0, 0, MAX(title.frame.size.width, subtitle.frame.size.width), 35)];
   self.navigationItem.titleView = stackVw;

Другое решение, использующее только одну метку и NSAttributedString вместо этого различать заголовок и субтитры (с разными размерами шрифта, весом, цветами и т. д.). Устраняет проблему различного выравнивания меток.

extension UIViewController {
    func setTitle(_ title: String, subtitle: String) {
        let rect = CGRect(x: 0, y: 0, width: 400, height: 50)
        let titleSize: CGFloat = 20     // adjust as needed
        let subtitleSize: CGFloat = 15

        let label = UILabel(frame: rect)
        label.backgroundColor = .clear
        label.numberOfLines = 2
        label.textAlignment = .center
        label.textColor = .black

        let text = NSMutableAttributedString()
        text.append(NSAttributedString(string: title, attributes: [.font : UIFont.boldSystemFont(ofSize: titleSize)]))
        text.append(NSAttributedString(string: "\n\(subtitle)", attributes: [.font : UIFont.systemFont(ofSize: subtitleSize)]))
        label.attributedText = text
        self.navigationItem.titleView = label
    }
}

Пользовательский titleView частично основан на /questions/25390934/zagolovok-mnogostrochnoj-navigatsii/25390949#25390949

Свифт 4:

import UIKit

class NavigationTitleView: UIView {

    private var contentStackView = UIStackView()
    private var titleLabel = UILabel()
    private var subTitleLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)

        viewConfig()
        addViewsConfig()
        layoutViewsConfig()

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


    func set(title: String, subTitle: String){

        self.titleLabel.text = title
        self.subTitleLabel.text = subTitle

    }

    private func viewConfig() {

        contentStackView.axis = .vertical
        contentStackView.alignment = .center
        contentStackView.distribution  = .fill
        contentStackView.spacing = 5


        self.backgroundColor = .clear
        self.titleLabel.textColor = .white
        self.self.subTitleLabel.textColor = .white

    }

    private func addViewsConfig() {

        contentStackView.addArrangedSubview(subTitleLabel)
        contentStackView.addArrangedSubview(titleLabel)
        self.addSubview(contentStackView)

    }

    private func layoutViewsConfig(){

        contentStackView.translatesAutoresizingMaskIntoConstraints = false
        contentStackView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0.0).isActive = true
        contentStackView.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true

    }

}

Использование:

import UIKit

class  ViewController: UIViewController {

    private var navigationTitleView = NavigationTitleView()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.titleView = navigationTitleView
        navigationTitleView.set(title: "title", subTitle: "subTitle")

    }

}

введите описание изображения здесь

Спасибо за ответ @RajanMaheshwari

Если у кого-то возникает проблема, из-за которой заголовок смещается, когда текст субтитров длиннее, чем текст заголовка, я добавил следующий код к ответу Раджана выше чуть ниже, где создается экземпляр subtitleLabel:

// Fix incorrect width bug
if (subtitleLabel.frame.size.width > titleLabel.frame.size.width) {
    var titleFrame = titleLabel.frame
    titleFrame.size.width = subtitleLabel.frame.size.width
    titleLabel.frame = titleFrame
    titleLabel.textAlignment = .center
}

Надеюсь, это поможет кому-то, кто столкнулся с той же проблемой, что и я

Простое исправление для iOS 16. Следуя приблизительному подходу @iosjillian / @Dan, вызов layoutSubviews() в представлении стека делает свое дело.

Рабочее решение для iOS 16 . Свифт 5.7

С библиотекой SnapKit. Если вы не используете библиотеку SnapKit, просто сделайте оба представления (titleLabel и subtitleLabel) translatesAutoresizingMaskIntoConstraints = false и замените ограничения SnapKit собственными ограничениями.

      func setTitle(title: String, subtitle: String, view: UIView) -> UIView {
    let appearance = UINavigationBar.appearance()
    let titleColor = appearance.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor ?? .black

    let titleLabel = UILabel()

    titleLabel.backgroundColor = UIColor.clear
    titleLabel.textColor = titleColor
    titleLabel.adjustsFontSizeToFitWidth = false
    titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
    titleLabel.lineBreakMode = .byTruncatingTail
    titleLabel.textAlignment = .center
    titleLabel.text = title
    
    let subtitleLabel = UILabel()
    subtitleLabel.backgroundColor = UIColor.clear
    subtitleLabel.textColor = UIColor.init(hexString: "#808890")
    subtitleLabel.adjustsFontSizeToFitWidth = false
    subtitleLabel.lineBreakMode = .byTruncatingTail
    subtitleLabel.textAlignment = .center
    subtitleLabel.font = UIFont.systemFont(ofSize: 11)
    subtitleLabel.text = subtitle
    
    let titleView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 30))
    titleView.addSubview(titleLabel)
    titleView.addSubview(subtitleLabel)
    
    titleLabel.snp.makeConstraints { make in
        make.horizontalEdges.equalToSuperview()
        make.top.equalToSuperview().offset(-20)
        make.height.equalTo(20)
    }

    subtitleLabel.snp.makeConstraints { make in
        make.horizontalEdges.equalToSuperview()
        make.top.equalTo(titleLabel.snp.bottom).offset(4)
        make.height.equalTo(10)
    }
    
    
    
    return titleView
}
Другие вопросы по тегам