- 从中心弹出视图控制器,手势类似于 Instagram 或 Telegram(许多类似)。
- 使用右边缘滑动手势重新打开弹出的视图控制器(类似于 Safari 的向前页面操作)。
0 行代码
只需将您的 UINavigationController
替换为 EZNavigationController
,使用默认值即可。
功能
Pop
左屏幕边缘 用户可以始终使用默认的从屏幕左边缘进行的 UINavigationController
手势,无论嵌入到导航控制器中的视图控制器的内容如何。
Pan-to-Pop
中心屏幕 在那些没有与导航控制器冲突的水平滑动手势的视图控制器部分,用户将从屏幕中心 Pan-to-pop 来弹出视图控制器,就像在 Instagram 或 Telegram 应用程序中那样熟悉。
滑至弹出
(在scrollViews上方)
居中屏幕 在含有水平scrollView的viewController部分,当scrollView处于起始位置(内容最左侧)即contentOffset.x = 0
时,用户将从屏幕中心滑至弹出
。否则,滚动将像往常一样工作。
弹出
(默认禁用)
屏幕右侧边缘 当NavigationController
从栈中弹出ViewController
后,可以通过从屏幕右侧边缘的滑动操作来实现此ViewController
的弹出
。这与在safari中返回页面的行为相同:您可以通过从屏幕右侧边缘向左滑动来返回到刚刚关闭的页面。
要启用
此行为,您需要创建一个带有EZUnpopConfiguration
的EZNavigationConfiguration
,并以下列方式操作:
- 传递给
EZNavigationControllerTransitionHelper
以使其为特定的NavigationController使用此配置 - 替换
EZNavigationConfiguration
的静态defaulConfiguration
,以使得每个EZNavigationController
都使用它
在一行中启用unpop
EZNavigationConfiguration.defaultConfiguration = EZNavigationConfiguration(unpop: EZUnpopConfiguration(ttl: 30, stackDepth: 5))
您可以将以下行代码放在AppDelegate.application(_:didFinishLaunchingWithOptions:)
中,以在所有此后创建的EZNavigationController
上启用unpop
。
安装
Cocoapods
只需在podfile中添加您想要的版本的EZCustomNavigation
依赖项
pod 'EZCustomNavigation', '1.1.2'
用法
故事板
只需在导航控制器自定义类标签中插入类名 EZNavigationController
。
注意:如果您通过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 中更改默认配置来默认启用所有 EZNavigationController
的 unpop
。
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文件