综合技术

UINavigationBar简单解析

微信扫一扫,分享到朋友圈

UINavigationBar简单解析
0

在开始写 UINavigationBar 之前,了解几个导航栏中用到的知识,将会更有利于理解。

可单独使用

首先需要明确 UINavigationBar 是可以脱离 UINavigationConroller 单独作为控件的。只是 UINavigationConroller 创建的 navigationBar 的代理 UINavigationBarDelegate 是 navigationConroller 自身。对于代码或 IB 直接创建的 navigationBar,代理则需要自己指定。

TintColor

相信大家对 tintColor 这个东西肯定不会陌生,这里就不再累述,只记录一下本人之前的一个疑惑: UILabel 为什么不受 tintColor 的影响?有位大佬在这里比较详细的讲解了,我就大概记录下自己的理解:

Apple 避免在可交互元素上使用边框和渐变,取而代之使用 tintColor ,那么 tintColor 的核心思想就是区分元素是否可以响应触摸。显而易见的, UILabel 是不可交互元素,即便你设置它的 tintColor 也不会被绘制。

VisualEffect

系统有三个关于高斯模糊效果的类,父类: UIVisualEffect ,两个子类: UIBlurEffectUIVibrancyEffect

UIVisualEffectView 就是展示这些效果的视图,文档里说:

Depending on the desired effect, the effect may affect content layered behind the view or content added to the visual effect view’s contentView.

对于 UIVisualEffectView ,根据想要的 effect,

  • UIBlurEffect 只是简单的给 UIVisualEffectView 后面的视图添加高斯模糊效果,对于添加到 UIVisualEffectViewcontentView 中的视图则不会产生模糊效果。

  • UIVibrancyEffect 不会给 UIVisualEffectView 后面的视图产生模糊,只会使添加到 contentView 中的视图更加生动。

  • 对于 UIBlurEffectUIVisualEffectView ,若它的 contentView 中又包含了一个 UIVibrancyEffectUIVisualEffectView 。则显示效果又有模糊效果,又有生动效果。

lazy var blurContainVibrancyView: UIVisualEffectView = {
   let vibrancyEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .light)))
   let label = self.creatLabel(withText: "而卒莫消长也")
   label.center = vibrancyEffectView.contentView.center
   vibrancyEffectView.contentView.addSubview(label)

   let blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
   blurEffectView.contentView.addSubview(vibrancyEffectView)
   vibrancyEffectView.frame = blurEffectView.frame
   vibrancyEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
   return blurEffectView
}()
复制代码
blurContainVibrancyView

文档指出不要给 UIVisualEffectView 或者它的父视图设置小于 1 的 alpha 值,否则 effect 可能显示不正确,或者根本不显示。但是可以设置 contentView 子视图的 alpha (在已经尝试过的实际运用中的时候,设置 UIVisualEffectViewalpha 小于 1 时,Xcode 会报警告但是透明和模糊效果都存在。设置它父视图的 alpha 小于 1 则没有警告但是只有透明效果)。

外观

导航栏样式 barStyle

enum UIBarStyle : Int {    
    case`default`
    case black
}
复制代码

默认白底黑字,black 样式为黑底白字。且这两种样式都默认半透明( isTranslucent = true )。

barTintColor、tintColor

barTintColor :用来导航栏背景色,不要使用 backgroundColor

  • 对于半透明导航栏,设置 backgroundColor (蓝色),颜色显示不正确:
半透明设置背景色
  • 对于不透明导航栏,设置 backgroundColor ,颜色完全不显示:
不透明设置背景色

tintColor :影响 bar 的子视图颜色。

标题文字样式

  • titleTextAttributes :常见的 NSAttributedString 设置。
  • setTitleVerticalPositionAdjustment(CGFloat, for: UIBarMetrics) :标题竖直方向偏移量。

isTranslucent

影响 navigationBar 的半透明效果,默认为 true

  • 对于没有明确设置 isTranslucentnavigationBar ,如果背景图 alpha < 1 ,则 isTranslucent = true 。反之为 false
  • 对于明确设置 isTranslucent = true 的,如果背景图为不透明,则会为背景图会被添加小于 1 的系统定义的 alpha
  • 对于明确设置 isTranslucent = false 的,如果背景图 alpha < 1 ,会根据 barStylebarTintColor 为该图片添加一个相应颜色的不透明背景。

背景图和阴影图

只有在设置过背景图片的情况下,阴影图片才会生效。单独设置阴影图片没有效果。

shadowImage 的位置实际上是超出了它的父视图的,设置 navigationBar.clipsToBounds = true 也可以隐藏。

假设 isTranslucent = true

  • 如果没有背景图片, navigationBar 的子视图中将会包含一个 visualEffectView 用来产生模糊效果。
默认视图层级
  • 如果设置了背景图片, navigationBar 的子视图中将不会包含 visualEffectView ,而是直接生成一个半透明的背景图。
设置背景图后的视图层级

代理

导航栏位置 barPosition

public enum UIBarPosition : Int {
    case any // 未指明的
    case bottom // 指定 bar 在父视图的底部,各种阴影都会被绘制在 bar 顶部
    case top // 指定 bar 在父视图的顶部,各种阴影都会被绘制在 bar 底部
    case topAttached // 指定 bar 和父视图都在屏幕的顶部,并且 bar 的背景会穿透状态栏
}
复制代码

barPosition 其实是协议 UIBarPositioning 中定义的属性, UINavigationBar 默认遵守了该协议,值为 .top

开篇就说到, UINavigationConroller 创建的 navigationBar,代理为 navigationConroller 自身。其默认实现为 .topAttached

如果自己创建一个 navigationBar 并将其添加到当前控制器视图中,指定代理为当前控制器。并实

UINavigationBarDelegate

func position(for bar: UIBarPositioning) -> UIBarPosition {
    return .topAttached
}
复制代码

可以得到和原生同样的效果(图中系统 iOS 10,高度为自定义,iOS 11 显示效果不一样哟):

自己创建UINavigationBar

拦截返回操作

在项目中时常有点击导航栏返回按钮,弹出确认返回的提示,此时就需要拦截返回事件。

自定义一个 NavigationBarShouldPopProtocol 将是否可以 pop 的控制权限交给当前控制器,再修改 UINavigationController 的默认实现,每次都询问 topViewController 是否可以 pop。且我们可以在 shouldPopWhenClickBackButton 方法中做一些额外操作(比如返回 false ,弹出提示框)。

protocol NavigationBarShouldPopProtocol {
    func shouldPopWhenClickBackButton() -> Bool
}

// 点击 navigationBar 的 backButton 是否 pop,默认为 true
extension UIViewController: NavigationBarShouldPopProtocol {
    @objc func shouldPopWhenClickBackButton() -> Bool {
        return true
    }
}
复制代码
extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        guard let items = navigationBar.items else {
            return false
        }
        
        if viewControllers.count < items.count {
            return true
        }
        
        var shouldPop = true
        if let controller = topViewController, controller.responds(to: #selector(UIViewController.shouldPopWhenClickBackButton)) {
			// 询问是否可以 pop
            shouldPop = controller.shouldPopWhenClickBackButton()
        }
        
        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for view in navigationBar.subviews {
                if view.alpha > 0 && view.alpha < 1 {
                    view.alpha = 1
                }
            }
        }
        
        return false
    }
}
复制代码
阅读原文...

微信扫一扫,分享到朋友圈

UINavigationBar简单解析
0
稀土掘金

索尼官方爆料 PS5:性能更强、支持光追、向下兼容 PS4

上一篇

vue客户端渲染首屏优化之道

下一篇

评论已经被关闭。

插入图片

热门分类

往期推荐

UINavigationBar简单解析

长按储存图像,分享给朋友