iOS11下如何调整大标题UINavigationBar高度以仿Apple Music样式
嘿,我刚好折腾过类似的需求——想用iOS11+原生的大标题导航栏(保留缩放特性),同时在大标题上方加个小文本(比如日期),还得解决自动布局约束冲突的问题。给你几个可行的方案:
方案一:自定义NavigationBar子类,调整内部布局(推荐)
这个方案通过自定义导航栏子类,手动添加日期标签,然后调整系统大标题视图的布局约束,既保留原生缩放特性,又能稳定显示顶部小文本。
步骤1:创建自定义NavigationBar
import UIKit class CustomLargeTitleNavBar: UINavigationBar { // 顶部小文本标签 private let topLabel = UILabel() override init(frame: CGRect) { super.init(frame: frame) setupTopLabel() } required init?(coder: NSCoder) { super.init(coder: coder) setupTopLabel() } private func setupTopLabel() { topLabel.text = "2024年5月20日" topLabel.font = .systemFont(ofSize: 14, weight: .regular) topLabel.textColor = .secondaryLabel topLabel.translatesAutoresizingMaskIntoConstraints = false addSubview(topLabel) // 先设置基础约束,后续在layoutSubviews中调整关联 NSLayoutConstraint.activate([ topLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), topLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), topLabel.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 8) ]) } override func layoutSubviews() { super.layoutSubviews() // 找到系统的大标题视图(这里通过类名匹配,若担心私有API风险,可通过子视图特征遍历) guard let largeTitleView = subviews.first(where: { $0.className == "_UINavigationBarLargeTitleView" }) else { return } // 强制关闭大标题视图的自动布局掩码,重新设置约束 largeTitleView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ // 让大标题视图贴在顶部标签下方 largeTitleView.topAnchor.constraint(equalTo: topLabel.bottomAnchor, constant: 6), largeTitleView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), largeTitleView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), largeTitleView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) ]) // 更新导航栏的固有高度,确保能容纳顶部标签+大标题 let totalHeight = topLabel.frame.height + 6 + largeTitleView.frame.height + 16 intrinsicContentSize = CGSize(width: intrinsicContentSize.width, height: totalHeight) setNeedsLayout() } }
步骤2:在ViewController中使用
class DemoViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 设置导航栏为大标题模式 navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "我的音乐" // 强制始终显示大标题(不滚动隐藏) navigationItem.largeTitleDisplayMode = .always // 替换为自定义导航栏 if let navController = navigationController { navController.navigationBar = CustomLargeTitleNavBar(frame: navController.navigationBar.frame) } } }
方案二:修改系统约束优先级(规避私有API风险)
如果担心方案一中的私有类名匹配被拒,可以通过遍历大标题视图的约束,修改其优先级后添加自定义约束:
在自定义NavigationBar的layoutSubviews中替换这部分代码:
override func layoutSubviews() { super.layoutSubviews() // 通过子视图特征找大标题视图(比如包含UILabel的视图) guard let largeTitleView = subviews.first(where: { $0.subviews.contains(where: { $0 is UILabel }) && $0.frame.height > 40 }) else { return } // 找到系统给大标题视图设置的顶部约束,降低其优先级 largeTitleView.constraints.forEach { constraint in if constraint.firstAttribute == .top && constraint.secondItem == self { constraint.priority = .defaultHigh // 999,低于系统默认的1000 } } // 添加顶部标签与大标题的关联约束 NSLayoutConstraint.activate([ topLabel.bottomAnchor.constraint(equalTo: largeTitleView.topAnchor, constant: -6) ]) // 更新导航栏高度 let totalHeight = topLabel.frame.height + 6 + largeTitleView.frame.height + 16 intrinsicContentSize = CGSize(width: intrinsicContentSize.width, height: totalHeight) setNeedsLayout() }
注意事项
- 确保导航栏的
isTranslucent属性设置正确,若需要不透明效果,记得设置barTintColor并关闭半透明:navigationBar.isTranslucent = false - 若使用UITableView/UICollectionView,记得调整
contentInset避免内容被导航栏遮挡 - 测试不同设备(尤其是刘海屏)的布局适配,可通过
safeAreaLayoutGuide调整约束常数
内容的提问来源于stack exchange,提问作者wj313
相关产品推荐
相关产品推荐

