本文主要是收集关于UINavigationController相关的常用功能,对于UINavigationController的介绍,下面的文章介绍的很详细了:
- # 学习UINavigationController(1)
- # 学习UINavigationController(2)
- # 学习UINavigationController(3)
- # 学习UINavigationController(4)
页面结构
UINavigationController的页面结构图如下:
UINavigationController
|-UINavigationBar
|-controllers
|-UIViewController ~> UINavigationItem
|-UIViewController ~> UINavigationItem
|-UIViewController ~> UINavigationItem
|...
- UINavigationController 只有一个 UINavigationBar,对UINavigationBar修改会影响到所有页面的显示
- UINavigationController有 viewControllers 对象,UINavigationBar有 items 对象,每一个 Controller 有自己的 UINavigationItem,可以对自己的UINavigationItem修改
- UINavigationController提供backItem和 leftItem,leftItem 优先级更高
常见用法
通常情况下,我们会在项目中创建UINavigationController的基类,以下就以BaseNavigationController 举例:
- 隐藏
UITabBar
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
// viewController是即将要 push 的控制器
if viewControllers.count > 0 {
viewController.hidesBottomBarWhenPushed = true
}
super.pushViewController(viewController, animated: animated)
}
- 隐藏系统导航栏,使用自定义导航栏
- 创建
BaseViewController,作为所有控制器的基类 - 通过设置透明图片来隐藏系统导航栏,但是保留系统导航栏的交互效果
- 创建自定义导航栏方法 示例代码如下:
- 创建
override func viewDidLoad() {
super.viewDidLoad()
// 隐藏默认返回按钮的文字,并修改返回按钮图标颜色
navigationItem.backBarButtonItem = UIBarButtonItem.init(title: "", image: UIImage(), primaryAction: nil, menu: nil)
navigationItem.backBarButtonItem?.tintColor = .black
// 设置导航栏透明
navigationController?.navigationBar.setBackgroundImage(UIImage(), for .default)
navigationController?.navigationBar.shadowImage = UIImage()
// 添加自定义导航栏背景
addNavBar(.white)
}
// MARK: 添加自定义导航栏方法
// 使用背景色填充导航栏
func addNavBar(_ color: UIColor) {
let size = CGSize(width: view.bounds.width, height: kStatusH + kNavBarH)
let navImageView = UIImageView(image: UIImage(size: size, color: color)) view.addSubview(navImageView)
}
// 使用自定义视图填充导航栏
func addCustomNavNar(_ view: UIView) {
let size = CGSize(width: view.bounds.width, height: kNavBarH)
view.frame = CGRectMake(0, 0, size.width, size.height)
navigationController?.navigationBar.addSubview(view)
}
通过颜色创建UIImage :
extension UIImage {
convenience init(size: CGSize, color: UIColor, scale: CGFloat = UIScreen.main.scale) {
let renderer = UIGraphicsImageRenderer(size: size, format: UIGraphicsImageRendererFormat.default())
let image = renderer.image { ctx in
color.setFill()
ctx.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
}
guard let cgImage = image.cgImage else {
fatalError("Failed to create CGImage from UIImage")
}
self.init(cgImage: cgImage, scale: scale, orientation: .up)
}
}
视图尺寸相关代码:
// 状态栏高度
var kStatusBarH: CGFloat {
if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
return window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
return UIApplication.shared.statusBarFrame.height
}
}
// 导航栏高度
var kNavBarH: CGFloat {
UINavigationController().navigationBar.frame.size.height
}
- 隐藏系统导航栏,并保留系统侧滑手势 隐藏系统导航栏后,对应的侧滑返回也没有了,如果想要保留系统的侧滑返回手势,在对应的控制器中添加如下代码:
// 在控制器中添加以下代码
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 隐藏系统导航栏后,系统的侧滑手势就没有了
navigationController?.setNavigationBarHidden(true, animated: true)
// 保留系统侧滑手势
navigationController?.interactivePopGestureRecognizer?.delegate = self
navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}
- 实现全屏侧滑手势
实现思路:
- 获取系统的侧滑手势
- 获取侧滑手势的视图
- 获取侧滑手势的 target&action
- 创建自定义手势,并添加到系统侧滑手势视图中,添加 target&action
获取系统侧滑手势和视图
// 获取系统侧滑手势
let popGesture = interactivePopGestureRecognizer
// 获取手势视图
guard let gestureView = popGesture?.view else { return }
通过 runtime 获取系统手势的所有属性
var count: UInt32 = 0
// 获取系统手势的所有属性
guard let ivars = class_copyIvarList(UIGestureRecognizer.self, &count) else { return }
for i in 0..<count {
let ivar = ivars[Int(i)]
let name = ivar_getName(ivar)!
print(String(cString: name))
}
从打印的属性中获取到我们要找到的属性_targets,使用 KVC 获取
let targets = popGesture?.value(forKey: "_targets") as? [NSObject]
打印出来是这样的一个数组
(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x101b37930>)
获取 target&action
guard let targetObj = targets?.first else { return }
// 取出 target 和 action
guard let target = targetObj.value(forKey: "target") else { return }
let action = Selector(("handleNavigationTransition:"))
添加自定义手势
// 创建自己的pan手势
let panGesture = UIPanGestureRecognizer()
gestureView.addGestureRecognizer(panGesture)
panGesture.addTarget(target, action: action)
这样就借助系统的侧滑手势完成了全屏返回功能