iOS 16.4带返回按钮的导航栏多行标题实现方案咨询
iOS 16.4 带返回按钮的导航栏添加多行标题解决方案
问题说明
在iOS 16.4环境下,需要为带有返回按钮的导航栏实现多行标题显示。此前尝试Stack Overflow上@krunal、@Kaiv Mata等人的方案,但在存在返回按钮时均无法生效;@Ankur Lahiry提出的私有API方案item.setValue(true, forKey: "__largeTitleTwoLineMode")虽能实现效果,但会导致App审核被拒,还会破坏下拉刷新功能,无法采用。
当前使用的代码如下:
guard let navigationBar = self.navigationController?.navigationBar else { return } navigationController?.navigationBar.subviews.forEach({ subview in subview.subviews.forEach { subsubview in guard let label: UILabel = subsubview as? UILabel else { return } label.text = "Multiline title with back button that not fit in one line" label.backgroundColor = .yellow label.font = UIFont.systemFont(ofSize: 16) label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping label.sizeToFit() navigationBar.frame.size.height = 50 + label.frame.height navigationItem.title = label.text } })
已尝试的操作均未解决问题:
- 在
ViewDidAppear()和ViewWillAppear()中执行上述代码 - 设置
extendedLayoutIncludesOpaqueBars = false - 设置
tableView.contentInset = UIEdgeInsets(top: 100, left: 0, bottom: 0, right: 0)
当前问题:第二行标题被遮挡;预期效果:标题完整多行显示且不影响返回按钮布局。
有效解决方案(非私有API)
方法1:自定义导航栏标题视图
直接为navigationItem.titleView设置自定义多行UILabel,同时调整导航栏高度约束,确保标题完整显示且不与返回按钮冲突。
代码示例:
override func viewDidLoad() { super.viewDidLoad() // 创建自定义多行标题Label let titleLabel = UILabel() titleLabel.text = "Multiline title with back button that not fit in one line" titleLabel.font = UIFont.systemFont(ofSize: 16) titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping titleLabel.textAlignment = .center titleLabel.backgroundColor = .yellow // 计算Label适配尺寸,预留返回按钮及边距空间 let maxWidth = UIScreen.main.bounds.width - 80 let labelSize = titleLabel.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)) titleLabel.frame = CGRect(x: 0, y: 0, width: maxWidth, height: labelSize.height) // 设置为导航栏标题视图 navigationItem.titleView = titleLabel // 调整导航栏高度约束 guard let navigationBar = navigationController?.navigationBar else { return } // 移除原有高度约束 navigationBar.constraints.forEach { constraint in if constraint.firstAttribute == .height { constraint.isActive = false } } // 设置新高度(基础高度44 + 上下边距) let newBarHeight = max(44, labelSize.height + 16) navigationBar.heightAnchor.constraint(equalToConstant: newBarHeight).isActive = true // 适配TableView内容偏移,避免被导航栏遮挡 if let tableView = view as? UITableView { let extraInset = newBarHeight - 44 tableView.contentInset = UIEdgeInsets(top: extraInset, left: 0, bottom: 0, right: 0) tableView.scrollIndicatorInsets = tableView.contentInset } }
方法2:使用UINavigationBarAppearance(iOS 13+)
通过UINavigationBarAppearance统一配置导航栏样式,实现多行标题自适应:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let appearance = UINavigationBarAppearance() appearance.configureWithDefaultBackground() // 创建多行标题Label let titleLabel = UILabel() titleLabel.text = "Multiline title with back button that not fit in one line" titleLabel.font = UIFont.systemFont(ofSize: 16) titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping titleLabel.textAlignment = .center let maxWidth = UIScreen.main.bounds.width - 80 let labelSize = titleLabel.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)) titleLabel.frame = CGRect(x: 0, y: 0, width: maxWidth, height: labelSize.height) // 将自定义Label设置到Appearance中 appearance.titleView = titleLabel // 应用样式到导航栏 navigationController?.navigationBar.standardAppearance = appearance navigationController?.navigationBar.scrollEdgeAppearance = appearance // 调整导航栏高度 let newBarHeight = max(44, labelSize.height + 16) navigationController?.navigationBar.frame.size.height = newBarHeight // 适配TableView内容偏移 if let tableView = view as? UITableView { let extraInset = newBarHeight - 44 tableView.contentInset = UIEdgeInsets(top: extraInset, left: 0, bottom: 0, right: 0) } }
注意事项
- 避免直接遍历导航栏子视图修改内部控件,iOS不同版本导航栏结构可能变化,导致代码失效
- 调整导航栏高度后,必须同步修改scrollView(如UITableView)的
contentInset和scrollIndicatorInsets,防止内容被遮挡 - 自定义标题视图时需预留返回按钮的宽度空间,避免标题与返回按钮重叠
内容的提问来源于stack exchange,提问作者Aysen Eremeev
相关产品推荐
相关产品推荐

