शायद थोड़ी देर हो गई है, लेकिन मैं भी पहले जैसा ही व्यवहार चाहता था। और जिस समाधान के साथ मैं गया था, वह ऐप स्टोर में वर्तमान में एक ऐप में काफी अच्छी तरह से काम करता है। चूंकि मैंने किसी को भी इसी तरह की विधि के साथ नहीं देखा है, इसलिए मैं इसे यहां साझा करना चाहूंगा। इस समाधान का नकारात्मक पक्ष यह है कि इसके लिए उपवर्ग की आवश्यकता होती है UINavigationController
। हालांकि मेथड स्विज़लिंग का उपयोग करना करने से बचने में मदद सकती है, लेकिन मैं इससे दूर नहीं गया।
तो, डिफ़ॉल्ट बैक बटन वास्तव में द्वारा प्रबंधित किया जाता है UINavigationBar
। जब कोई उपयोगकर्ता बैक बटन पर टैप करता है, UINavigationBar
तो उसके प्रतिनिधि से पूछें कि क्या उसे UINavigationItem
कॉल करके शीर्ष पॉप करना चाहिए navigationBar(_:shouldPop:)
। UINavigationController
वास्तव में इसे लागू करते हैं, लेकिन यह सार्वजनिक रूप से घोषित नहीं करता है कि यह अपनाता है UINavigationBarDelegate
(क्यों !?)। इस घटना को रोकने के लिए, एक उपवर्ग बनाएं UINavigationController
, इसके अनुरूप होने UINavigationBarDelegate
और लागू करने की घोषणा करें navigationBar(_:shouldPop:)
। true
शीर्ष आइटम पॉपअप किया जाना चाहिए, तो वापस लौटें । false
अगर रहना चाहिए तो लौटो ।
दो समस्याएं हैं। पहला यह है कि आपको किसी बिंदु पर UINavigationController
संस्करण को कॉल करना होगा navigationBar(_:shouldPop:)
। लेकिन UINavigationBarController
सार्वजनिक रूप से इसे अनुरूप घोषित नहीं करता है UINavigationBarDelegate
, इसे कॉल करने की कोशिश करने से संकलन समय में त्रुटि होगी। जो समाधान मैं लेकर गया था, उसका उद्देश्य सीधे कार्यान्वयन प्राप्त करने और इसे कॉल करने के लिए ऑब्जेक्टिव-सी रनटाइम का उपयोग करना है। कृपया मुझे बताएं कि क्या किसी के पास बेहतर समाधान है।
दूसरी समस्या यह है कि उपयोगकर्ता navigationBar(_:shouldPop:)
द्वारा popViewController(animated:)
पीछे बटन पर टैप करने पर इसे पहले कहा जाता है । यदि दृश्य नियंत्रक को कॉल करके पॉपअप किया जाता है, तो क्रम उलट हो जाता है popViewController(animated:)
। इस मामले में, मैं यह पता लगाने के लिए बूलियन का उपयोग करता हूं कि popViewController(animated:)
क्या कहा जाता है, navigationBar(_:shouldPop:)
जिसका अर्थ है कि उपयोगकर्ता ने पीछे बटन पर टैप किया है।
इसके अलावा, मैं UIViewController
नेविगेशन कंट्रोलर को व्यू कंट्रोलर से यह बताने देता हूं कि अगर यूजर बैक बटन पर टैप करता है तो उसे पॉपअप किया जाना चाहिए। देखें नियंत्रक वापस आ सकते हैं false
और कोई भी आवश्यक कार्रवाई कर सकते हैं और popViewController(animated:)
बाद में कॉल कर सकते हैं ।
class InterceptableNavigationController: UINavigationController, UINavigationBarDelegate {
// If a view controller is popped by tapping on the back button, `navigationBar(_:, shouldPop:)` is called first follows by `popViewController(animated:)`.
// If it is popped by calling to `popViewController(animated:)`, the order reverses and we need this flag to check that.
private var didCallPopViewController = false
override func popViewController(animated: Bool) -> UIViewController? {
didCallPopViewController = true
return super.popViewController(animated: animated)
}
func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
// If this is a subsequence call after `popViewController(animated:)`, we should just pop the view controller right away.
if didCallPopViewController {
return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
}
// The following code is called only when the user taps on the back button.
guard let vc = topViewController, item == vc.navigationItem else {
return false
}
if vc.shouldBePopped(self) {
return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
} else {
return false
}
}
func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) {
didCallPopViewController = false
}
/// Since `UINavigationController` doesn't publicly declare its conformance to `UINavigationBarDelegate`,
/// trying to called `navigationBar(_:shouldPop:)` will result in a compile error.
/// So, we'll have to use Objective-C runtime to directly get super's implementation of `navigationBar(_:shouldPop:)` and call it.
private func originalImplementationOfNavigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
let sel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
let imp = class_getMethodImplementation(class_getSuperclass(InterceptableNavigationController.self), sel)
typealias ShouldPopFunction = @convention(c) (AnyObject, Selector, UINavigationBar, UINavigationItem) -> Bool
let shouldPop = unsafeBitCast(imp, to: ShouldPopFunction.self)
return shouldPop(self, sel, navigationBar, item)
}
}
extension UIViewController {
@objc func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
return true
}
}
और आप नियंत्रकों को देखते हैं, कार्यान्वित करते हैं shouldBePopped(_:)
। यदि आप इस विधि को कार्यान्वित नहीं करते हैं, तो उपयोगकर्ता के सामान्य बटन की तरह ही बैक बटन पर टैप करते ही डिफ़ॉल्ट व्यवहार दृश्य नियंत्रक को पॉप करने के लिए होगा।
class MyViewController: UIViewController {
override func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
let alert = UIAlertController(title: "Do you want to go back?",
message: "Do you really want to go back? Tap on \"Yes\" to go back. Tap on \"No\" to stay on this screen.",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { _ in
navigationController.popViewController(animated: true)
}))
present(alert, animated: true, completion: nil)
return false
}
}
आप यहां मेरे डेमो को देख सकते हैं ।