UINavigation​Controller 的详解(基于 API )

闲谈

最近做项目涉及到一个UINavigation​bar 的设置,网上给出的方法大多都很不全面,于是乎决定看 API 来研究一下 UINavigationController。至于我遇到的问题在本篇文章中会写到。

认识 UINavigationController

官方 API 给的解释是: The UINavigation​Controller class implements a specialized view controller that manages the navigation of hierarchical content. This navigation interface makes it possible to present your data efficiently and makes it easier for the user to navigate that content. You generally use this class as-is but you may also subclass to customize the class behavior. 理解起来也很简单: UINavigationController 类实现了管理分层内容导航的专用视图控制器,该导航界面可以有效地呈现数据,并且可以使用户更容易浏览该内容。 通常可以直接在使用,也可以使用子类自定义。

管理方式:

UINavigationController通过栈的方式管理控制器的切换,控制入栈和出栈来展示各个视图控制器。栈中的第一个视图控制器是根视图控制器。最后一个视图控制器是当前正在显示的视图控制器。 整个过程就像是向弹夹里压子弹,弹夹就可以看成是一个栈,子弹就是每一个被管理的试图控制器。不停地向弹夹里压子弹,最后一个压进去的就会在最上边,我们看到的就是最上边那个,也就是说我们看到的视图永远是栈顶的那个。

navigationController 管理位于界面顶部的 navigation bar和位于界面底部的一个可选的 toolbar。很多人知道 navigation bar 但是不知道 toolbar。 navigation bar始终显示,并且是由 navigation controller 自身来管理的,并且可以根据栈中的视图控制器来更新它的内容(像 UINavigationBar 的一下属性)。 toolbar 的显示是依据于属性isToolbarHidden的。该属性的默认值是 true。所以一般来说我们使用 UINavigationController的时候是看不到他的。当这个属性值为 false 的时候,navigation controller也是根据栈顶的viewController的内容来更新他的。

初始化方法

UINavigationController 两种初始化方法:

  @available(iOS 5.0, *)
    public init(navigationBarClass: Swift.AnyClass?, toolbarClass: Swift.AnyClass?)

    public init(rootViewController: UIViewController) 
复制代码

首先第二种就不做解释了,如果不会,那么你该去面壁了。 ~***但是要提出的一点是rootViewController不能是 UITab​Bar​Controller 的实例。***~ 那么让我们来看第一种吧,第一种初始化方法有两个参数,这两个参数要求都是:AnyClass。但是官方 API 对参数的要求是: navigation​Bar​Class 指定要使用的自定义UINavigationBar子类,或指定使用标准UINavigationBar类的nil。 toolbar​Class 指定要使用的自定义UIToolbar子类,或指定使用标准UIToolbar类的nil。

下面给出一种实现方法:(代码) 1.自定义 UINavigationBar的子类

class YHYNavigationBar: UINavigationBar {

	override init(frame: CGRect) {
		super.init(frame: frame)
		// 你可以在上边加一个 UIView 的子类
		let image = UIImageView(frame: CGRect(x: 8, y: 5, width: 22, height: 22))
		self.addSubview(image)
		image.image = UIImage(named: "arrow_44")
	}
	
	required init?(coder aDecoder: NSCoder) {
		super.init(coder: aDecoder)
	}
 
    override func draw(_ rect: CGRect) {
      // 你可以用 UIBezierPath 画一个图形
      let bezier = UIBezierPath()
		bezier.move(to: CGPoint(x: 0, y: 0))
		bezier.addLine(to: CGPoint(x: 0, y: 32))
		bezier.addQuadCurve(to: CGPoint(x:UIScreen.main.bounds.width,y:32), controlPoint: CGPoint(x: UIScreen.main.bounds.width / 2, y: 56))
		bezier.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: 32))
		bezier.addLine(to: CGPoint(x: UIScreen.main.bounds.width, y: 0))
		UIColor.yellow.setFill()
		bezier.fill()
    }
}
复制代码
  1. 定义一个UIToolbar子类
class YHYToolbar: UIToolbar {

	override init(frame: CGRect) {
		super.init(frame: frame)
	
		self.barTintColor = UIColor.white
		self.isTranslucent = false
		self.setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: .default)
		self.setShadowImage(UIImage(), forToolbarPosition: .any)
		
		let image = UIImageView(frame: CGRect(x: 20, y: 8, width: 28, height: 28))
		self.addSubview(image)
		image.image = UIImage(named: "arrow_44")
		
	}
	
	required init?(coder aDecoder: NSCoder) {
		super.init(coder: aDecoder)
	}

}
复制代码

结果的样子:

写到这里是不是有很多小伙伴就准备用这个方法来自定义自己想要的导航栏了呢? 其实这算是一种方法,但是官方 API 并不支持使用这一种方法,而是推荐使用的方法是这样的。 官方 API 给出的建议是 To customize the overall appearance of a navigation bar, use UIAppearance APIs instead of this method. If you use this initialization method to create a navigation bar that uses custom bar subclasses, you are responsible for pushing and setting view controllers before presenting the navigation controller onscreen. 大概意思是: 要定制导航栏的整体外观,请使用UIAppearance API而不是此方法。 如果您使用此初始化方法创建使用自定义栏子类的导航栏,则在将屏幕上显示导航控制器之前,您有责任推送和设置视图控制器。 具体使用可以看这里,下面我们给出一些关于UINavigationBar使用UIAppearance的示例: (更全面的你可以看这里)


	func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
		
		// UINavigationBar的填充颜色
		UINavigationBar.appearance().barTintColor = UIColor.brown
		// UINavigationBar 的 title 的字体颜色,字体大小,是一个字典类型的赋值,更多属性,你可以 command+鼠标左键 字典中的任何一个键值去看
		UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName : UIFont.systemFont(ofSize: 24), NSForegroundColorAttributeName: UIColor.white]
		// UINavigationBar 的背景图片 注意:这个设置之后 barTintColor 就会失效,原因也很简单,都是用来做背景的
		//	UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
		//  这个关系到 UINavigationBar底部的那条黑线
		//  UINavigationBar.appearance().shadowImage = UIImage()
	
		return true
	}

复制代码

访问Navigation Stack中的元素

此时大家都知道UINavigationController通过栈的方式来管理视图控制器的,那么怎么访问栈中存在的视图控制器呢?

// UINavigationController的一个属性 ,可以访问到栈顶端的那个试图控制器,一般是正在显示的那个
 open var topViewController: UIViewController? { get } 

 open var visibleViewController: UIViewController? { get } 
    
// UINavigationController的一个属性 ,栈中所有的试图控制器,你可以打印一下看出当前      
   UINavigationController管理了多少个试图控制器
 open var viewControllers: [UIViewController] 

 @available(iOS 3.0, *)
    open func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) 
复制代码

视图的切换方法

视图控制器的切换也是一个重要的部分,这些方法也都很常见,很简单,只做部分注释,不做代码演示了。

// 向栈中推进一个 viewController 用于显示出这个试图控制器
 open func pushViewController(_ viewController: UIViewController, animated: Bool) 
  
// 推出栈顶的那个 viewController,返回到之前的页面  
 open func popViewController(animated: Bool) -> UIViewController? 

// 退回到指定的试图控制器,这下之前的viewControllers属性就有用处了,你可以根据viewControllers中的视图控制器 指定退回到哪一个
 open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? 

// 直接将栈中除了最底端的根视图控制器之外的所有试图控制器推出,从而显示根试图控制器,也就是最早进入栈中的试图控制器
 open func popToRootViewController(animated: Bool) -> [UIViewController]? 
复制代码

隐藏导航栏

UINavigationController 提供了几种属性来分别对待不同情况下对NavigationBar的隐藏,都比较简单:

      //  隐藏UINavigationBar
		//  self.navigationController?.isNavigationBarHidden = true
		
		
		//	self.navigationController?.hidesBarsWhenVerticallyCompact = true
		
		//  键盘弹出时隐藏UINavigationBar
		//	self.navigationController?.hidesBarsWhenKeyboardAppears = true
		
		//  轻拍手势隐藏UINavigationBar
		//  self.navigationController?.hidesBarsOnTap = true
		
		//  根据滑动手势来隐藏其导航条
		//	self.navigationController?.hidesBarsOnSwipe = true

复制代码

UINavigation​Controller​Delegate

使用导航控制器的delegate(实现此协议的自定义对象)来修改从UINavigation Controller对象的导航堆栈中推送或弹出视图控制器时的行为。

public protocol UINavigationControllerDelegate : NSObjectProtocol {

   // 比较常用的是这两个,看到就很容易理解也就不多讲了
    @available(iOS 2.0, *)
    optional public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool)

    @available(iOS 2.0, *)
    optional public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool)

}
复制代码

到这里 UINavigationController 的使用才仅仅是冰山一角,最重要的角色还在幕后,是谁呢?自然是UINavigationBar,主角的光辉需要一个独立的舞台尽情发挥。那么!

UINavigationBar请看下一集,精彩不可错过!

其实UINavigationController还有一些转场动画的实现,因为目前所探讨的都是一些基础属性的使用,所以转场动画会在以后分享出来!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值