1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
//
// CustomNavigationController.swift
// MullvadVPN
//
// Created by pronebird on 17/09/2020.
// Copyright © 2020 Mullvad VPN AB. All rights reserved.
//
import Foundation
import UIKit
enum NavigationPopTrigger {
case backButton
case interactiveGesture
}
protocol ConditionalNavigation: class {
func shouldPopNavigationItem(_ navigationItem: UINavigationItem, trigger: NavigationPopTrigger) -> Bool
}
class CustomNavigationController: UINavigationController, UINavigationBarDelegate {
private static let classInit: Void = {
swizzleMethod(
aClass: CustomNavigationController.self,
originalSelector: #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:)),
newSelector: #selector(customNavigationController_navigationBar(_:shouldPop:))
)
}()
private var popGestureRecognizerDelegate: CustomPopGestureRecognizerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
_ = Self.classInit
popGestureRecognizerDelegate = CustomPopGestureRecognizerDelegate(navigationController: self, systemGestureRecognizerDelegate: interactivePopGestureRecognizer?.delegate)
// Replace the system interactive gesture recognizer
interactivePopGestureRecognizer?.delegate = popGestureRecognizerDelegate
}
@objc dynamic func customNavigationController_navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
var shouldPop = true
if let conformingViewController = topViewController as? ConditionalNavigation {
shouldPop = conformingViewController.shouldPopNavigationItem(item, trigger: .backButton)
}
// Only call super implementation when we want to pop the controller
if shouldPop {
// Call super implementation
return customNavigationController_navigationBar(navigationBar, shouldPop: item)
} else {
return shouldPop
}
}
}
private class CustomPopGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate {
private let systemGestureRecognizerDelegate: UIGestureRecognizerDelegate?
private weak var navigationController: UINavigationController?
init(navigationController: UINavigationController, systemGestureRecognizerDelegate: UIGestureRecognizerDelegate?) {
self.navigationController = navigationController
self.systemGestureRecognizerDelegate = systemGestureRecognizerDelegate
}
override func responds(to aSelector: Selector!) -> Bool {
if Self.instancesRespond(to: aSelector) {
return true
} else {
return systemGestureRecognizerDelegate?.responds(to: aSelector) ?? false
}
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
let shouldForward = systemGestureRecognizerDelegate?.responds(to: aSelector) ?? false
if shouldForward {
return systemGestureRecognizerDelegate
} else {
return nil
}
}
// MARK: - UIGestureRecognizerDelegate
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let shouldBegin = systemGestureRecognizerDelegate?.gestureRecognizerShouldBegin?(gestureRecognizer) ?? true
guard let navigationController = navigationController,
let topItem = navigationController.navigationBar.topItem,
let conformingViewController = navigationController.topViewController as? ConditionalNavigation else {
return shouldBegin
}
return shouldBegin && conformingViewController.shouldPopNavigationItem(topItem, trigger: .interactiveGesture)
}
}
|