我正在为一个应用程序的导航栏添加一个大标题。问题是标题有点长,因此我需要在大标题中添加两行。如何在导航栏中添加包含两行的大标题呢?
这不是默认的导航栏标题!这是关于 iOS 11 中引入的大标题。所以请确保您考虑到大标题后再提出建议。谢谢
我正在为一个应用程序的导航栏添加一个大标题。问题是标题有点长,因此我需要在大标题中添加两行。如何在导航栏中添加包含两行的大标题呢?
这不是默认的导航栏标题!这是关于 iOS 11 中引入的大标题。所以请确保您考虑到大标题后再提出建议。谢谢
基于@krunal的答案,这对我是有效的:
extension UIViewController {
func setupNavigationMultilineTitle() {
guard let navigationBar = self.navigationController?.navigationBar else { return }
for sview in navigationBar.subviews {
for ssview in sview.subviews {
guard let label = ssview as? UILabel else { break }
if label.text == self.title {
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
UIView.animate(withDuration: 0.3, animations: {
navigationBar.frame.size.height = 57 + label.frame.height
})
}
}
}
}
在UIViewController中:
override func viewDidLoad() {
super.viewDidLoad()
self.title = "This is a multiline title"
setupNavigationMultilineTitle()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupNavigationMultilineTitle()
}
对于设置大标题的字体和颜色:
navigation.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: .red, NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 30)]
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationItem.largeTitleDisplayMode = .automatic
self.title = "This is multiline title for navigation bar"
self.navigationController?.navigationBar.largeTitleTextAttributes = [
NSAttributedStringKey.foregroundColor: UIColor.black,
NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: .largeTitle)
]
for navItem in(self.navigationController?.navigationBar.subviews)! {
for itemSubView in navItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.text = self.title
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
}
}
}
这是结果:
UINavigationController
UINavigationBarDelegate
添加到类定义中navigationBar(_:shouldPush:)
item.setValue(true, forKey: "__largeTitleTwoLineMode")
激活两行模式navigationController.navigationBar.prefersLargeTitles = true
navigationItem.setValue(1, forKey: "__largeTitleTwoLineMode")
,所以你不需要使用UINavigationBarDelegate
。 - Oleksandr Vaker当存在后退按钮时,换行方案似乎存在问题。因此,我没有采用断行方式,而是使标签自动调整字体。
func setupLargeTitleAutoAdjustFont() {
guard let navigationBar = navigationController?.navigationBar else {
return
}
// recursively find the label
func findLabel(in view: UIView) -> UILabel? {
if view.subviews.count > 0 {
for subview in view.subviews {
if let label = findLabel(in: subview) {
return label
}
}
}
return view as? UILabel
}
if let label = findLabel(in: navigationBar) {
if label.text == self.title {
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.7
}
}
}
然后需要在viewDidLayoutSubviews()中调用它,以确保标签可以被找到,并且我们只需要调用一次:
private lazy var setupLargeTitleLabelOnce: Void = {[unowned self] in
if #available(iOS 11.0, *) {
self.setupLargeTitleAutoAdjustFont()
}
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let _ = setupLargeTitleLabelOnce
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 11.0, *) {
setupLargeTitleAutoAdjustFont()
}
}
(编辑于7月13日:我注意到这个解决方案不支持scrollView,所以现在我正在研究)
我在Swift5上找到了一个完美的解决方案。
但是抱歉我的英语很差,因为我是日本学生。
首先,在viewDidLoad
中正常设置大标题的导航设置。
//Set largeTitle
navigationItem.largeTitleDisplayMode = .automatic
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.largeTitleTextAttributes = [.font: UIFont.systemFont(ofSize: (fontSize + margin) * numberOfLines)]//ex) fontSize=26, margin=5, numberOfLines=2
//Set title
title = "multiple large\ntitle is working!"
largeTitleTextAttributes
中的字体大小应该等于实际字体大小(+边距)乘以行数。
因为导航栏属性的默认规范可能只能显示一行的大标题。viewDidAppear
中直接进行标签设置://Find label
navigationController?.navigationBar.subviews.forEach({ subview in
subview.subviews.forEach { subsubview in
guard let label: UILabel = subsubview as? UILabel else { return }
//Label settings on direct.
label.text = title
label.font = UIFont.systemFont(ofSize: fontSize)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
}
})
import UIKit
class ViewController: UIViewController {
private let fontSize: CGFloat = 26, margin: CGFloat = 5
private let numberOfLines: CGFloat = 2
override func viewDidLoad() {
super.viewDidLoad()
setUpNavigation()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setMultipleLargeTitle()
}
private func setUpNavigation() {
//Set largeTitle
navigationItem.largeTitleDisplayMode = .automatic
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.largeTitleTextAttributes = [.font: UIFont.systemFont(ofSize: (fontSize + margin) * numberOfLines)]
//Set title
title = "multiple large\ntitle is working!"
}
private func setMultipleLargeTitle() {
//Find label
navigationController?.navigationBar.subviews.forEach({ subview in
subview.subviews.forEach { subsubview in
guard let label: UILabel = subsubview as? UILabel else { return }
//Label settings on direct.
label.text = title
label.font = UIFont.systemFont(ofSize: fontSize)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
}
})
}
}
SWIFT 5 这个UIViewController扩展帮了我。我的情况是混合启用和禁用大标题所以首先启用大标题,然后调用这个方法。在viewDidLoad中调用它,我发现通过滑动并释放触摸来窥视回退时出现了错误,因为某种原因当前导航标题变成了上一个导航标题。
extension UIViewController {
/// Sets two lines for navigation title if needed
/// - Parameter animated: used for changing titles on one controller,in that case animation is off
func multilineNavTitle(_ animated:Bool = true) {
if animated {
// setting initial state for animation of title to look more native
self.navigationController?.navigationBar.transform = CGAffineTransform.init(translationX: .screenWidth/2, y: 0)
self.navigationController?.navigationBar.alpha = 0
}
//Checks if two lines is needed
if self.navigationItem.title?.forTwoLines() ?? false {
// enabling multiline
navigationItem.setValue(true,
forKey: "__largeTitleTwoLineMode")
} else {
// disabling multiline
navigationItem.setValue(false,
forKey: "__largeTitleTwoLineMode")
}
// laying out title without animation
UIView.performWithoutAnimation {
self.navigationController?.navigationBar.layoutSubviews()
self.navigationController?.view.setNeedsLayout()
self.navigationController?.view.layoutIfNeeded()
}
if animated {
//animating title
UIView.animate(withDuration: 0.3) {
self.navigationController?.navigationBar.transform = CGAffineTransform.identity
self.navigationController?.navigationBar.alpha = 1
}
}
}
}
fileprivate extension String {
/// Checks if navigation title is wider than label frame
/// - Returns: `TRUE` if title cannot fit in one line of navigation title label
func forTwoLines() -> Bool {
let fontAttributes = [NSAttributedString.Key.font: SomeFont]
let size = self.size(withAttributes: fontAttributes)
return size.width > CGFloat.screenWidth - 40 //in my case
}
}
func setMultilineNavigationBar(topText: String, bottomText : String) {
let topTxt = NSLocalizedString(topText, comment: "")
let bottomTxt = NSLocalizedString(bottomText, comment: "")
let titleParameters = [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16, weight: .semibold)]
let subtitleParameters = [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 13, weight: .regular)]
let title:NSMutableAttributedString = NSMutableAttributedString(string: topTxt, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: bottomTxt, attributes: subtitleParameters)
title.append(NSAttributedString(string: "\n"))
title.append(subtitle)
let size = title.size()
let width = size.width
guard let height = navigationController?.navigationBar.frame.size.height else {return}
let titleLabel = UILabel(frame: CGRect.init(x: 0, y: 0, width: width, height: height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
self.navigationItem.titleView = titleLabel
}
Swift 4:即使句子很短也可以多行显示
title = "You're \nWelcome"
for navItem in(self.navigationController?.navigationBar.subviews)! {
for itemSubView in navItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.text = self.title
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
}
}
}
viewController.navigationItem
.setValuesForKeys(["__largeTitleTwoLineMode": true])
class MyNavigationViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
navigationBar.delegate = self
}
}
extension MyNavigationViewController: UINavigationBarDelegate {
func navigationBar(_ navigationBar: UINavigationBar, shouldPush item: UINavigationItem) -> Bool {
item.setValuesForKeys([
"__largeTitleTwoLineMode": true
])
return true
}
}