EZCustomNavigation 1.2.0

EZCustomNavigation 1.2.0

Enrico Zannini 维护。



  • 作者:
  • Enrico Zannini

Logo 易用自定义导航控制器,可以

  • 从中心弹出视图控制器,手势类似于 Instagram 或 Telegram(许多类似)。
  • 使用右边缘滑动手势重新打开弹出的视图控制器(类似于 Safari 的向前页面操作)。

0 行代码

只需将您的 UINavigationController 替换为 EZNavigationController,使用默认值即可。

功能

左屏幕边缘 Pop

用户可以始终使用默认的从屏幕左边缘进行的 UINavigationController 手势,无论嵌入到导航控制器中的视图控制器的内容如何。

中心屏幕 Pan-to-Pop

在那些没有与导航控制器冲突的水平滑动手势的视图控制器部分,用户将从屏幕中心 Pan-to-pop 来弹出视图控制器,就像在 Instagram 或 Telegram 应用程序中那样熟悉。

居中屏幕 滑至弹出 (在scrollViews上方)

在含有水平scrollView的viewController部分,当scrollView处于起始位置(内容最左侧)即contentOffset.x = 0时,用户将从屏幕中心滑至弹出。否则,滚动将像往常一样工作。

GIF Pan-to-pop demo

屏幕右侧边缘 弹出 (默认禁用)

NavigationController从栈中弹出ViewController后,可以通过从屏幕右侧边缘的滑动操作来实现此ViewController弹出。这与在safari中返回页面的行为相同:您可以通过从屏幕右侧边缘向左滑动来返回到刚刚关闭的页面。

启用此行为,您需要创建一个带有EZUnpopConfigurationEZNavigationConfiguration,并以下列方式操作:

  • 传递给EZNavigationControllerTransitionHelper以使其为特定的NavigationController使用此配置
  • 替换EZNavigationConfiguration的静态defaulConfiguration,以使得每个EZNavigationController都使用它

GIF Unpop demo

在一行中启用unpop

EZNavigationConfiguration.defaultConfiguration = EZNavigationConfiguration(unpop: EZUnpopConfiguration(ttl: 30, stackDepth: 5))

您可以将以下行代码放在AppDelegate.application(_:didFinishLaunchingWithOptions:)中,以在所有此后创建的EZNavigationController上启用unpop

安装

Cocoapods

只需在podfile中添加您想要的版本的EZCustomNavigation依赖项

pod 'EZCustomNavigation', '1.1.2'

用法

故事板

只需在导航控制器自定义类标签中插入类名 EZNavigationController

Storyboard

注意:如果您通过Cocoapods安装,请确保添加包含EZNavigationController类的模块(EZCustomNavigation

程序化

如果您不使用故事板,则只需在显示时使用 EZNavigationController 而不是 UINavigationController

let navigationController = EZNavigationController(rootViewController: SomeOtherUIViewController())
self.present(navigationController, animated: true, completion: nil)

或者在子类化时

import EZCustomNavigation

class CustomNavigationController: EZNavigationController {
    
}

自定义

UIScrollView行为

为了防止滚动干扰EZNavigationController的拖动手势(左侧反跳),默认情况下,任何水平滚动的UIScrollView,如果嵌套在EZNavigationController中,都被视为有资格避免左侧反跳功能。

此行为定义在默认的静态实现中,即UIScrollView.shouldAvoidLeftBounceBlock,该值为

public static var shouldAvoidLeftBounceBlock: ((UIScrollView)->(Bool))? = { scrollView in
        // Returns true if scrollView has a EZNavigationController in it's responder chain, false otherwise
        return scrollView.isDescendantOfClass(EZNavigationController.self)
    }

您可以通过替换此静态块以自己的实现来更改每个scrollView的行为。

如果您想为单个scrollView覆盖此行为,只需将该单个scrollView的块属性shouldAvoidLeftBounceBlock设置为,返回您想在特定时间对该特定scrollView使用的行为。

let someScrollView = UIScrollView()
someScrollView.shouldAvoidLeftBounceBlock = {
    return true // or false, if you want to remove our custom implementation for that scrollView at that time
}

这些块将在滚动事件发生时调用scrollView,如果它们返回true,则阻止左侧反跳行为以允许拖动弹出

  • 如果为单个scrollView提供了块,则将忽略静态块。
  • 如果没有为单个scrollView提供块,则使用静态块。
  • 如果静态块也为nil(表示您已删除默认块),则将其视为false,整个功能将被移除。

UINavigationController 扩展

为了允许自定义某些行为,您可以使用 UINavigationController 扩展的方法,而不是预制的 EZNavigationController

public func addCustomTransitioning(_ transitionHelper: EZNavigationControllerTransitionHelper = EZNavigationControllerTransitionHelper())

public func removeCustomTransitioning()

您可以这样做有几个原因

  • 如果由于某种原因您不能覆盖某些自定义的 UINavigationController 实现(例如,您有另一个库 subclasses 该库并且您无法更改其实现)
  • 如果您想自定义某些动画或某些交互。因此,您需要使用自定义参数或子类来调用这些方法。
  • 如果您想更改配置,使其变为该 NavigationController 的特定工具。特别有用,如果您想启用 unpop 行为。

与其他 UINavigationController 库一起使用

例如,这里提供了一些其他库的示例,这些库提供了 NavigationController 实现(派生于 UINavigationController,并希望与该库不冲突)

class MyNavigationImplementation: SomeOtherLibrariesImplementationOfUINavigationController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addCustomTransitioning()
    }
    
    deinit {
        removeCustomTransitioning()
    }
}

自定义动画/交互

这里提供了一些自定义动画器的示例

class MyCustomAnimatorNavigationImplementation: UINavigationController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let helper = EZNavigationControllerTransitionHelper(transitionCoordinator:
            EZTransitionCoordinator(presentingAnimator: SomePresentingAnimator(),
                                    dismissingAnimator: SomeDismissingAnimator(),
                                    interactionController: SomeInteractionController()))
        addCustomTransitioning(helper)
    }
    
    deinit {
        removeCustomTransitioning()
    }
}

注意:如果您不使用默认的 EZNavigationController,则默认的 Scroll Behavior 不会正常工作。如果您使用其他 UINavigationController 子类,您必须提供自己的滚动行为实现(但根据默认示例并不难)。

启用 Unpop 行为

Unpop 默认禁用,有多个原因

  • 向后兼容性:之前的用户可能不希望在仅从 1.0.0 升级到 1.1.0 时启用此行为
  • 情境性:您不一定总是希望启用此行为。有时您想关闭视图控制器而不允许某人通过 unpopping 重新打开它
  • 控制器回收延迟:由于视图控制器被保存在内部的 unpopStack 中,它们可能不会在你预期的时刻被回收。这意味着内存将比预期晚释放,并且与 deinit 相关的其他逻辑也可能稍后发生。当你决定使用这种情况时应格外小心,以便为一个或所有 EZNavigationControllers 启用 unpop

作为一个为什么你不应该使用它的例子,你可以考虑有一个包含视频播放器的控制器。你预期当你从导航控制器中弹出这个视图控制器时,它会自动释放播放器并停止播放。但如果有这个视图控制器被 unpopStack 保持活跃(一段时间或永久),在用户导航应用程序的其余部分时,播放可能仍在继续。在这种情况下,例如,你可能需要在 ViewController.willDisappear() 方法中停止播放器,或者类似的方法。

为什么应该启用它

简单来说,有时候用户在忙于某事时,不小心将视图控制器弹出。也许他们正在写一篇非常重要的文字,也许他们正在编辑一幅酷炫的图片。如果他们可以取消弹出后恢复视图控制器,难道不是很好吗?

例如,每个编辑应用都既有“撤销”又有“重做”操作。甚至 Safari 用户也可以从屏幕左侧边缘滑动来撤销导航操作(或后退),从屏幕右侧边缘滑动来重做导航操作(前进)。

当然,还有一些更安全的方式来避免意外关闭(比如,考虑一个弹出消息询问你是否真的想要丢失所有工作),但这并不总是你想要的(safari 例如就不这样做)。

另外,有时候用户只是想回到以前的屏幕浏览一下,然后再回到他们正在的其他屏幕中的操作。而这样使用 unpop 行为就做得很好。

简而言之,这就是一个“重做”选项,一旦你回到先前的视图控制器,就可以再次回到你刚刚关闭的最新位置,所有未保存的工作都会在那里。

如何启用它

你可以在 AppDelegate 中更改默认配置来默认启用所有 EZNavigationControllerunpop

EZNavigationConfiguration.defaultConfiguration = EZNavigationConfiguration(unpop: EZUnpopConfiguration(ttl: 30, stackDepth: 5))

如果你这样做,所有在此配置更改之后创建的 EZNavigationController 都将启用 unpop 行为。


或者,你可以创建自己的 NavigationController 子类,你希望在需要时使用它,并将配置传递给你想要添加自定义切换的过渡助手。

class MyUnpopEnabledNavigationImplementation: UINavigationController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let config = EZNavigationConfiguration(unpop: EZUnpopConfiguration(ttl: 30, stackDepth: 5))
        let coordinator = EZTransitionCoordinator()
        let helper = EZNavigationControllerTransitionHelper(transitionCoordinator: coordinator,
                                                            configuration: config)
        addCustomTransitioning(helper)
    }
    
    deinit {
        removeCustomTransitioning()
    }
}

快速导航配置

目前,您只能配置unpop行为。如果您传递UnpopConfiguration,则启用它。您可以设置2个属性

  • ttl:被弹出视图控制器在unpopStack中保留的秒数
  • stackDepth:可以存储在堆栈中并可潜在退出的视图控制器的最大数量

TTL也可以是nil,尽管不推荐。如果存在,则必须大于0。StackDepth也必须大于0。该参数越大,视图控制器在堆栈中保留的时间和天数就越多。

因此,建议不要使用任意大的数字。根据您的实际需求选择stackDepth和TTL。

贡献

如果您发现有任何错误或者可以改进的地方,请随时提出问题或提交pull请求。

许可证

该项目使用MIT许可证 - 有关详细信息,请参阅LICENSE.txt文件