Добавьте субтитры под заголовком в панель навигации контроллера в 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
}