NavigationAndStyle 1.1.8

NavigationAndStyle 1.1.8

SMBCheeky 维护。



  • 作者:
  • Stefan B.

NavigationAndStyle

CI Status Version License Platform

示例

要运行示例项目,请克隆仓库,然后从Example目录中首先运行pod install

安装

NavigationAndStyle通过CocoaPods提供。要安装它,只需将以下行添加到Podfile中:

pod 'NavigationAndStyle'

描述

NavigationAndStyle是UIViewController和UINavigationController的扩展,允许

  • 滑动返回功能,即使不使用苹果使用的默认返回按钮。
  • 快速轻松设置和自定义UINavigationBars的外观和内容。
  • 最小化对每个UIViewController进行子类化的需求,以实现这些功能。
  • 启用通过点击titleView/barButtonItem的自动消失,移除了样板代码。
  • 通过简单更改NavigationBarStyle并调用triggerNavigationBarStyleRefresh(),实现即时主题切换。

通用使用

要使用此扩展,请将以下代码添加到您的 UIViewController 中

import NavigationAndStyle

// ...

let cancelItem: UIBarButtonItemType = .systemItem(.cancel)

override func viewDidLoad() {
    super.viewDidLoad()

    // ...

    // Always call set() method in viewDidLoad() to configure view controller with color style and other important UI elements (see below)
    set(title: .button("Button", autoDismiss: true),
    leftItems: [.image(UIImage.NavigationAndStyle.backArrow, extendTapAreaBy: 32, autoDismiss: true)],
    rightItems: [cancelItem])

    // ...
}

override func navBarTitlePressed(with type: UINavigationBarItemType) {
    // Do something when titleView is pressed
}

override func navBarItemPressed(with type: UIBarButtonItemType, isLeft: Bool) {
    if type == cancelItem {
        // Do something when Cancel button is pressed
    }
}

override func willAutomaticallyDismiss() {
    // Screen was dismissed via the `autoDismiss: true` option set on any tappable element set
}

UINavigationBar 增强功能

首先,由于背景/标题视图动画不够平滑,UINavigationBar 默认设置为完全透明。要解决需要实心背景、细分隔线,甚至是背景阴影以显示动态内容的情形,这些元素是单独管理并添加到视图层次结构中的,当调用 set(...) 方法、在 viewDidLoad 中(建议只调用一次)时。

要访问这些元素,只需在控制器上调用 .navigationElements 即可。此变量将始终包含自定义背景元素,以及所有这些元素的底部锚点。

public weak var modalNavigationBar: UINavigationBar?
public weak var backgroundImageView: UIImageView?
public weak var hairlineSeparatorView: UIView?
public weak var shadowBackgroundView: UIImageView?

public var bottomAnchor: NSLayoutYAxisAnchor? {

    if hairlineSeparatorView?.backgroundColor?.isEqual(UIColor.clear) == true {
        return backgroundImageView?.bottomAnchor
    }
    return hairlineSeparatorView?.bottomAnchor
}

要将视图停靠到手动创建的导航栏,请在使用 set(..) 后使用此方法

func dockViewToNavigationBar(_ view: UIView, constant: CGFloat)

,或从 .navigationElements 中的底部锚点值。

即使 UIViewController 没有导航栏控制器,也会将 modalNavigationBar 添加到视图层次结构以保持相同的对齐、样式和功能。

要更改导航栏项或标题视图元素,请使用这些方法

func change(title type: UINavigationBarItemType)
func change(leftItems items: [UIBarButtonItemType], animated: Bool)
func change(rightItems items: [UIBarButtonItemType], animated: Bool)

默认情况下,大标题是禁用的。要在视图控制器中启用大标题,请在使用 set(..) 后使用此方法

func setLargeTitle(andDock view: UIView?)

由于我们将导航栏的背景处理为独立的视图,在滚动时缩小会导致大标题越界并显示在内容下方。要强制在滚动时缩小,请调用 setShrinkOnScroll(basedOn:)。通过一些设计更新(例如,更新导航栏下方内容的 textColor 为标题文本色)现有选项可能足够大多数情况使用。

当在大标题和普通标题之间切换时,要更改导航栏项的状态,请使用此示例(更改左侧项)

override func viewWillLayoutSubviews() {
    self.checkIfLargeTitleIsVisible { [weak self] largeTitleActive in
    if largeTitleActive, navigationItem.leftBarButtonItem?.barItemType != <saved large title item type> {
            change(leftItems: [<saved large title item type>])
        } else !largeTitleActive, navigationItem.leftBarButtonItem?.barItemType != <saved normal title item type> {
            change(leftItems: [<saved normal title item type>])
        }
    }
        
    // ...
    
    super.viewWillLayoutSubviews()
}

UINavigationBarItemType - 如何工作

此类被创建来定义此扩展中可以使用此扩展定义的标题视图元素类型。目前,我们可以选择

  • 本机标题文本
  • 标签
  • 按钮
  • 图片
  • 本机标题文本和标签
  • 本机标题文本和按钮
  • 本机标题文本和图片

未列出的其他选项,如带图像和文本的UISearchBar和UIButton,由开发者根据其使用情况进行处理。我们添加UIImageView仅作为方便之处,因为左右按钮项目容器需要具有相同的宽度,以免在视图控制器转换期间UI渲染没有动画。

点击按钮元素将调用navBarTitlePressed(with type: UINavigationBarItemType),除非使用autoDismiss参数。

设置标题视图使用的方法是

  • self.navigationItem.titleView = ...

UIBarButtonItemType - 工作原理

此类创建是为了定义此扩展可以使用此扩展的UIBarButtonItem类型。目前,我们可以选择以下类型

  • 带文本的按钮
  • 带图像的按钮
  • 带图像和文本的按钮
  • 系统项目

可以在UIBarButtonItem对象上调用.barItemType来获取它们类型。

未列出的其他选项(如自定义视图/按钮/原始barButtonItem),留由开发者根据其使用情况进行处理,因为为它们添加选项将限制它们的用途。

单击条目将调用navBarItemPressed(with type: UIBarButtonItemType, isLeft: Bool),除非使用autoDismiss参数。

设置条目的方法有

  • setLeftBarButtonItems([...], animated: animated)
  • setRightBarButtonItems([...].reversed, animated: animated)

请注意,右侧按钮项目列表已反转,以便实现从左到右的可读性。

其他注意事项

每个元素都有一个autoDismiss: Bool参数,允许在传入指定的点击方法之前自动关闭。这些元素将在关闭前调用willAutomaticallyDismiss(),允许开发者调用简单的操作。

将来可以添加其他选项,但由于导航栏的工作方式、动画和渲染元素,添加更多选项,保持平滑的动画以及代码体积小巧都十分困难。

NavigationBarStyle - 工作原理

NavigationBarStyle定义了状态栏和导航栏的样式

  • 状态栏管理(请参阅以下如何启用/自定义)
  • 背景图像和/或颜色
  • 细边分隔颜色
  • 背景阴影颜色 - 对于具有动态内容的透明导航栏很有用
  • 标题和按钮字体和颜色
  • 图像着色颜色
  • 所用的高亮透明度
  • 禁用元素的颜色

使用不同导航栏样式(NavigationBarStyles)的所有UIViewControllers,示例项目展示了如何使动画到/自新/先前控制器流畅且不间断。

可以通过init方法进行初始化(有关更多详细信息,请查看下面的Defaults结构)

init(statusBarStyle: UIStatusBarStyle = .default,
     backgroundColor: UIColor = Defaults.navigationBarBackgroundColor,
     backgroundImage: UIImage? = nil,
     backgroundMaskColor: UIColor = .clear,
     backgroundMaskImage: UIImage? = nil,
     backgroundMaskAlpha: CGFloat = 1.0,
     hairlineSeparatorColor: UIColor = .clear,
     shadow: UIColor = .clear,
     titleFont: UIFont = Defaults.titleFont,
     titleColor: UIColor = Defaults.darkTextColor,
     buttonFont: UIFont = Defaults.buttonFont,
     buttonTitleColor: UIColor = Defaults.blueColor,
     imageTint: UIColor = Defaults.blueColor,
     highlightAlpha: CGFloat = Defaults.highlightAlpha,
     disabledColor: UIColor = Defaults.disabledColor)

,或通过使用便捷变量 NavigationBarStyle.default(尽可能接近默认的Apple外观)和 NavigationBarStyle.transparent(简化版的init方法)。

要从当前样式快速创建另一个NavigationBarStyle,请使用便捷的实例方法 .new(...)

可以个性化导航栏样式,并直接设置为UINavigationController或每个UIViewController。这是通过将以下代码添加到您选择的UIViewController/UINavigationController中实现的

override func getNavigationBarStyle() -> NavigationBarStyle {
    return NavigationBarStyle.transparent
}

默认情况下,getNavigationBarStyle 方法如下

extension UIViewController: CanHaveNavigationBarStyle {
    open func getNavigationBarStyle() -> NavigationBarStyle {
        return navigationController?.getNavigationBarStyle() ?? NavigationBarStyle.global
    }
}

静态变量 NavigationBarStyle.global 的默认值是 NavigationBarStyle.default,可以更改以允许不同的默认样式。

状态栏更新处理

要允许根据设置在UIViewController上的NavigationBarStyle自动更新状态栏,请添加以下代码

override var preferredStatusBarStyle: UIStatusBarStyle {
    return getNavigationBarStyle().statusBarStyle
}

注意:基于视图控制器的状态栏外观UIViewControllerBasedStatusBarAppearance 在Info.plist中的条目需要移除或设置为YES,才能使此功能生效。

资源和通用自定义

结构体 UIImage.NavigationAndStyle 中包含了5个图像形式的资源

  • 返回图标
  • 前进图标
  • 设置图标
  • 关闭图标
  • 用于导航栏背景的背景阴影图像

UIBarButtonItemType.generic 下定义了6个通用条按钮项:(自动消失 = true)

  • 返回
  • 关闭

(自动消失 = false)

  • noAutoDismissBack
  • noAutoDismissClose
  • 前进
  • 设置

NavigationBarStyle使用位于结构体 NavigationBarStyle.Defaults 中的默认值,声明为静态变量

  • highlightAlpha: CGFloat = 0.66
  • titleFont: UIFont = .boldSystemFont(ofSize: 17)
  • buttonFont: UIFont = .systemFont(ofSize: 17)
  • disabledColor: UIColor = .lightGray
  • blueColor: UIColor = UIButton(frame: .zero).tintColor
  • darkTextColor: UIColor = .darkText
  • navigationBarBackgroundColor = UIColor(red: 0.97, green: 0.97, blue: 0.97, alpha: 1)
  • hairlineSeparatorColor = UIColor(red: 0.11, green: 0.11, blue: 0.11, alpha: 0.13)
  • heightOfHairlineSeparator: CGFloat = 1
  • backgroundShadow = UIImage.NavigationAndStyle.backgroundShadow

更多自定义

所有在UIViewController和UINavigationController上重写的方法都被编写得如此之好,以至于如果开发人员需要重写它并更改其行为,他们也可以添加默认实现。所有包含默认实现的方法都以 "...Action" 结尾。例如: triggerNavigationBarStyleRefreshtriggerNavigationBarStyleRefreshAction

作者

Stefan B., [email protected]

许可

NavigationAndStyle 可在 MIT 许可下使用。有关更多信息,请参阅 LICENSE 文件。