美文网首页iOS Developer
关于应用push界面的全屏pop

关于应用push界面的全屏pop

作者: 村长大人tardis_cxx | 来源:发表于2017-05-15 22:37 被阅读58次

iOS pop push UINavigationController


相信现如今大多数应用的架构属于主流架构,即在一个UITabBarController里面添加多个UINavigationController,每一个UINavigationController里面又添加多个UIViewController或者其子类。
在从次级界面开始,每个UIViewController中,iOS系统默认在左侧边缘,用手指向右滑动,就可以pop到上一级界面,如果不是很细致的人,绝对是想不到有这功能的,如果使用过的人,想必也对这个功能感觉有些鸡肋,那么是否可以把这个手势添加到全屏,使之可以从全屏任意右滑,以实现全屏pop的功能呢?
答案当然是肯定的,以下是实现思路:

  • 实现全屏pop我有两种思路,而且也曾都实现,第一种思路是,给UINavigationController添加子类,给view添加拖动手势UIPanGestureRecognizer,这样做比较麻烦,计算过多,不是很好的一种选择。
  • 另一种思路是通过KVC获取UINavigationController成员属性interactivePopGestureRecognizer的私有属性,获得targetaction,这两个属性的值。
  • 如果获得上面两个私有属性对应的值,然后传入到UIPanGestureRecognizer(target: target, action: action)自定义拖拽手势中,就可以实现了
  • 那么如何获取私有属性呢?这就是我们已经比较常见的runtime即运行时了

实现

runtime,我们可以获取某个类的成员变量:

var count: UInt32 = 0
let ivars = class_copyIvarList(UIGestureRecognizer.self, &count)
for i in 0..<count {
    guard let ivar = ivars![Int(i)] else {
        continue
    }
    let nameP = ivar_getName(ivar)
    let name = String(cString: nameP!)
    print(name)
}

打印之后,我们就会看到有一个成员变量为_targets,那么这个成员变量是否是interactivePopGestureRecognizer的私有变量呢?那么就用KVC尝试下:

guard let targetsValue = interactivePopGestureRecognizer?.value(forKeyPath: "_targets") as? [NSObject] else {
     return
}
print(targetsValue)

打印之后,我们会得到:

[(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fa44df158c0>)]
6

这貌似是我们需要的结果!那么就获得他们,转为我们熟悉的对象:

// 取出对象
guard let targetObjc = targetsValue.first else {
     return
}
        
// 从对象中取出target 和 action
let target = targetObjc.value(forKeyPath: "target")
let action = targetObjc.value(forKeyPath: "action") as? Selector

// 创建自己的手势
let pan = UIPanGestureRecognizer(target: target, action: action)
view.addGestureRecognizer(pan)

运行,结果是让人失望的,程序直接奔溃,打印如下:

Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIGestureRecognizerTarget 0x608000220e00> valueForUndefinedKey:]: this class is not key value coding-compliant for the key action.

很显然是因为没有action这个key,但我们在打印中明显看到有action的,那是什么原因呢?答案我猜是系统重写了description方法!既然如此,但其value实不会变的,那么何不直接复制其方法使用呢?

guard let targetsValue = interactivePopGestureRecognizer?.value(forKeyPath: "_targets") as? [NSObject] else {
     return
}

guard let targetObjc = targetsValue.first else {
     return
}

// 从对象中取出target 和 action
let target = targetObjc.value(forKeyPath: "target")
// 只有直接复制 函数 包装成selector
let action = Selector(("handleNavigationTransition:"))
        
// 创建自己的手势
let pan = UIPanGestureRecognizer(target: target, action: action)
view.addGestureRecognizer(pan)

如此,就可以轻松愉快地实现了全屏pop功能了,是不是很简单呢?看起来是很简单,其实也不容易,在这个过程中,更重要的是一种思想,有时候使用runtime就可以很轻松实现我们需要的功能。

效果图.png

相关文章

网友评论

    本文标题:关于应用push界面的全屏pop

    本文链接:https://www.haomeiwen.com/subject/rrrkxxtx.html