Swift中通过运行时实现全屏的pop功能

最近碰见个小的功能需求,就是需要给push的视图控制器添加一个侧滑pop弹出的功能,找了点资料,发现一个比较不错的解决办法,用了相识已久,但是没用过的运行时功能来实现。

具体思路如下:

    1. push到 UINavigationController 中的控制器视图,系统默认是给添加了一个边缘侧滑pop回上一页的方法的,这个功能是 UINavigationController 通过属性 var interactivePopGestureRecognizer: UIGestureRecognizer? 实现的
  • 2.因为 UIGestureRecognizer 都是通过target 和action来实现具体的操作的,所以我们可以将系统的这个边缘策划pop的手势,通过运行时把他的target 和action取出来添加到我们自建的手势上,完活。

代码如下:

//通过 class_copyIvarList() 方法获得UIGestureRecognizer的所有变量 包括 property及{ }中的
override func viewDidLoad() {
        super.viewDidLoad()
        var count: UInt32 = 0
        let ivars = class_copyIvarList(UIGestureRecognizer.self, &count)!
        for i in 0..<count {
            //获得变量的名字
            let nameP = ivar_getName(ivars[Int(i)])!
            let name = String.init(cString: nameP)
            print(name)
        }
}
//输出如下
/*
_gestureFlags
_targets
_delayedTouches
_delayedPresses
_view
_lastTouchTimestamp
_state
_allowedTouchTypes
_initialTouchType
_internalActiveTouches
_forceClassifier
_requiredPreviewForceState
_touchForceObservable
_touchForceObservableAndClassifierObservation
_forceTargets
_forcePressCount
_beganObservable
_failureRequirements
_failureDependents
_delegate
_allowedPressTypes
_gestureEnvironment
*/

我们目测(当然要靠一点猜测了😜)_targets 这个变量 应该是负责 action 和target相关的属性,所以我们通过KVC来具体查看一下

 let tempTargets = interactivePopGestureRecognizer?.value(forKey: "_targets")
 print(tempTargets)
//输出如下
/*
Optional(<__NSArrayM 0x60000004be80>(
(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7f88db500670>)
)
)
*/

从输出中我们可以看出来确实有我们需要的action 和 target
我们可以通过KVC将对应的值取出来

        guard let targets = interactivePopGestureRecognizer?.value(forKey: "_targets") as? [NSObject] else {
            return
        }
        let targetObjct = targets.first
        let target = targetObjct?.value(forKey: "target")
        let action = targetObjct?.value(forKey: "action")

这样取出来会造成崩溃,提示没有action这个值(为啥还真不知道...),所以将action的获取更改如下:

//直接通过字符进行封装
let action = Selector(("handleNavigationTransition:"))

这样我们的目的就达到了,有了 target 和 action ,我们将target 和 action添加到对应的手势上就可以了
因为我们要求的是某一些固定push的页面都拥有这个功能,所以我们通过自定义UINavigationController,然后将这个手势添加在了UINavigationController的view上,这样所有的栈内的控制器都有这个全局pop的功能了,全部代码如下:

//UINavigationController 中
class CustomNavController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //通过运行时获得手势中的隐藏属性
        //var interactivePopGestureRecognizer: UIGestureRecognizer? UINavigationController自带的边缘pop手势
        var count: UInt32 = 0
        let ivars = class_copyIvarList(UIGestureRecognizer.self, &count)!
        for i in 0..<count {
            let nameP = ivar_getName(ivars[Int(i)])!
            let name = String.init(cString: nameP)
            //print(name)
        }
        
        let tempTargets = interactivePopGestureRecognizer?.value(forKey: "_targets")
       // print(tempTargets)
        
        guard let targets = interactivePopGestureRecognizer?.value(forKey: "_targets") as? [NSObject] else {
            return
        }
        let targetObjct = targets.first
        let target = targetObjct?.value(forKey: "target")
        let action = Selector(("handleNavigationTransition:"))
        view.addGestureRecognizer(UIPanGestureRecognizer(target: target, action: action))
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容