创建自定义“titleLabel”的最佳方法是继承UIView 和UINavigationController。这样做的演示是here。
主要部分是导航控制器。基本上,您希望将title 属性留空(一个空字符串或不设置它),并且由于UINavigationController 只是UIViewController 的一个子类,作为具有推送/弹出功能的容器视图,工作以这种方式与您的子类。在我的演示中,我的子类导航控制器类称为NavigationController。
在我看来,子类化UINavigationController 的主要优势是使用它的推送/弹出功能...虽然您可以使用 segues,但它们需要故事板,而您可以使用UIView 动画,对我来说只是感觉不那么“流畅”。
(1) 如果您不使用 Storyboard,请记住在 AppDelegate 中将此子类设置为您的根视图控制器:
var navController: NavigationController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
navController = NavigationController(nibName: nil, bundle: nil)
self.window!.rootViewController = navController
self.window?.makeKeyAndVisible()
return true
}
(2) 虽然还有其他方法可以做到这一点,但我更喜欢在我的子类中实例化视图控制器,根据需要公开推送/弹出方法:
class NavigationController:UINavigationController {
let redVC = RedViewController()
let greenVC = GreenViewController()
let titleView = TitleView()
var titleViewHeightConstraint:NSLayoutConstraint!
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
pushViewController(redVC, animated: false) // display the first VC
}
func popGreenVC() {
popViewController(animated: true)
}
func pushGreenVC() {
pushViewController(greenVC, animated: true)
}
}
(3) 在其他 VC 中,根据需要设置左/右按钮并根据需要按下/弹出。我喜欢使用 UTF-8 字符的自定义“箭头”:
class RedViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
let showGreenVC = UIBarButtonItem(title: "Green \u{25B6}", style: .plain, target: self, action: #selector(pushGreenVC))
self.navigationItem.rightBarButtonItem = showGreenVC
}
@objc func pushGreenVC() {
let navController = self.navigationController as! NavigationController
navController.pushGreenVC()
}
}
class GreenViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
let showRedVC = UIBarButtonItem(title: "\u{25C0} Red", style: .plain, target: self, action: #selector(popGreenVC))
self.navigationItem.leftBarButtonItem = showRedVC
}
@objc func popGreenVC() {
let navController = self.navigationController as! NavigationController
navController.popGreenVC()
}
}
请注意,我将视图的 naviagtionController? 属性强制转换为我的子类类型。需要强制转换才能访问其中的自定义方法。
(4) 接下来,包含您的自定义“titleView”。出于演示目的,我创建了一个 50/50 黄色/蓝色的。请注意,它使用自动布局锚点:
class TitleView: UIView {
let yellowView = UIView()
let blueView = UIView()
convenience init() {
self.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
yellowView.translatesAutoresizingMaskIntoConstraints = false
blueView.translatesAutoresizingMaskIntoConstraints = false
yellowView.backgroundColor = UIColor.yellow
blueView.backgroundColor = UIColor.blue
self.addSubview(yellowView)
self.addSubview(blueView)
yellowView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
yellowView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
yellowView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
yellowView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
blueView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
blueView.leadingAnchor.constraint(equalTo: yellowView.trailingAnchor).isActive = true
blueView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
blueView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
}
(5) 在导航控制器的初始化中,添加代码以添加您的标题视图。请注意,我使用 两个 的自动布局。首先,我将宽度设置为状态栏宽度的 1/3。您可以进行实验 - 使用图像设置恒定宽度可能会更好。其次,注意名为titleViewHeightConstraint 的“命名”约束的使用。我将在下一步中解释这一点。
titleViewHeightConstraint = titleView.heightAnchor.constraint(equalToConstant: 0)
titleViewHeightConstraint.isActive = true
titleView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
titleView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3).isActive = true
(5) 使标题标签的高度动态化:
记住,UINavigationController 确实是一个容器视图控制器,是所有方面的。它的“根”视图是全屏的,所以如果你将标题标签锚定到底部,你会得到一个从上到下垂直大小的标签。更糟糕的是,有时会出现状态栏,尤其是纵向显示时。但其他时候(默认情况)不是。因为我使用 both 的safeAreaLayoutGuide 导航控制器(检测是否存在状态栏)和其中的视图控制器(检测高度导航栏加上状态栏)。
(5a) 将此方法添加到您的导航控制器:
func changeTitleViewHeight(to:CGFloat) {
titleViewHeightConstraint.constant = to - view.safeAreaInsets.top
}
(5b) 并将这个覆盖添加到子类导航控制器内的所有视图控制器:
override func viewSafeAreaInsetsDidChange() {
let navController = self.navigationController as! NavigationController
navController.changeTitleViewHeight(to: view.safeAreaInsets.top)
}