作者: 无彦主
笔者一般在 ViewController 的 viewWillAppear 中处理导航条的 UI 变化。比如是否隐藏导航栏、改变状态栏颜色等。但是最近发现在 viewWillAppear 中改变 navigationBar 的 titleTextAttributes
属性却在 pop 时出现了问题:「 从 SecondViewController 点击导航栏返回到 FirstViewController 时并没有生效,而使用滑动手势返回却可以生效。」
import UIKit
class FirstViewController: UIViewController {
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavgationBarTitleTextAttributes(
color: .nav_purple,
font: .nav_regular
)
}
...
}
在 iOS 系统 10 以后,UIKit 已经更新,统一后台管理 UINavigationBar,UITabBar 和 UIToolbar。特别是,对这些视图的背景属性(例如背景或阴影图像或设置条形样式)的更改可能会启动条形码的布局传递以解析新的背景外观。特别地,这意味着,试图改变的内部这些条的背景外观
layoutSubviews,
-[UIView updateConstraints] ,
viewWillLayoutSubviews,
viewDidLayoutSubviews,
updateViewConstraints
或响应布局而调用的任何其他方法都可能导致布局循环。布局更改调用的 viewWillAppear 似乎触发了所提到的布局循环。
比较简单的处理方法是在 SecondViewController 中重写 willMove(:)
方法,在这里将 titleAttribute 赋值回去,但这样的方式不够彻底,它显然不能处理两种或两种以上的状态变化。更为稳妥的的方法是重写自定义 UINavigationController 中的 popViewController(:)
方法。
// NavBarTitleChangeable.swift
import UIKit
public protocol NavBarTitleChangeable: class {
var preferrdTextAttributes: [NSAttributedStringKey: AnyObject] { get }
}
...
// Custom UINavigationController
import UIKit
class FunNavigationViewController: UINavigationController {
private var topViewControllerNavBarTitleAttributes: [NSAttributedStringKey: AnyObject]? {
return (topViewController as? NavBarTitleChangeable)?.preferrdTextAttributes
}
private func setNavBarTitleAttributes(_ attributes: [NSAttributedStringKey: AnyObject]) {
navigationBar.titleTextAttributes = attributes
}
override func viewDidLoad() {
super.viewDidLoad()
if let attributes = topViewControllerNavBarTitleAttributes {
setNavBarTitleAttributes(attributes)
}
}
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
super.pushViewController(viewController, animated: animated)
if let attributes = topViewControllerNavBarTitleAttributes {
setNavBarTitleAttributes(attributes)
}
}
override func popViewController(animated: Bool) -> UIViewController? {
let popViewController = super.popViewController(animated: animated)
if let attributes = topViewControllerNavBarTitleAttributes {
setNavBarTitleAttributes(attributes)
}
transitionCoordinator?.animate(alongsideTransition: nil) { [weak self] _ in
if let attributes = self?.topViewControllerNavBarTitleAttributes {
self?.setNavBarTitleAttributes(attributes)
}
}
return popViewController
}
}
// MyViewController.swift
import UIKit
class FirstViewController: UIViewController, NavBarTitleChangeable {
var preferrdTextAttributes: [NSAttributedStringKey : AnyObject] {
let item = FunNavTitleTextAttributesItem(color: .nav_purple, font: .nav_regular)
return getNavgationBarTitleTextAttributes(with: item)
}
...
}
这里简单展示处理思路,具体代码还是要根据项目需求定制。