iOS में नेविगेशन बटन कंट्रोलर में बैक बटन कॉलबैक


102

मैंने नेविगेशन कंट्रोलर पर एक दृश्य को धक्का दिया है और जब मैं बैक बटन दबाता हूं तो यह स्वचालित रूप से पिछले दृश्य में चला जाता है। जब स्टैक से दृश्य को पॉप करने से पहले बैक बटन दबाया जाता है तो मैं कुछ चीजें करना चाहता हूं। बैक बटन कॉलबैक फ़ंक्शन कौन सा है?



यह [समाधान] [1] चेकआउट करें जो बैक बटन शैली को भी बनाए रखता है। [१]: stackoverflow.com/a/29943156/3839641
सर्वसंरग्लट

जवाबों:


162

विलियम जोकस का जवाब इस समस्या को आसान चाल से हल करता है।

-(void) viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
       // back button was pressed.  We know this is true because self is no longer
       // in the navigation stack.  
    }
    [super viewWillDisappear:animated];
}

32
जब उपयोगकर्ता बैक बटन टैप करता है तो यह कोड न केवल निष्पादित होता है, बल्कि प्रत्येक घटना में दृश्य पॉपअप होता है (उदाहरण के लिए जब दाईं ओर एक बटन या सेव बटन होता है)।
अर्थ-मामले

7
या जब एक नए दृष्टिकोण के लिए आगे बढ़ रहा है।
गायब्रश थ्रीपवुड 10

इसे तब भी कहा जाता है जब उपयोगकर्ता बाएं किनारे से (इंटरेक्टिव पोपग्योर्योर रिकॉग्निज़र) करता है। मेरे मामले में, मैं विशेष रूप से उस समय की तलाश कर रहा हूं जब उपयोगकर्ता बाएं किनारे से पैनिंग नहीं करता है।
काइल क्लेग

2
इसका मतलब यह नहीं है कि बैक बटन इसका कारण था। उदाहरण के लिए एक आराम से बहस हो सकती है।
मुस्कानबोट

1
मुझे संदेह है, हमें यह देखने में क्यों नहीं चाहिए
JohnVanDijk

85

मेरी राय में सबसे अच्छा समाधान।

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    if (![parent isEqual:self.parentViewController]) {
         NSLog(@"Back pressed");
    }
}

लेकिन यह केवल iOS5 + के साथ काम करता है


3
यह तकनीक एक बैक बटन टैप और एक सिक्योर लीग के बीच अंतर नहीं कर सकती है।
मुस्कानबोट

WillMoveToParentViewController और viewWillDisappear विधि नियंत्रक को नष्ट नहीं करना चाहिए समझा नहीं करता है, doMoveToParentViewController सही है
हांक

27

यह शायद बैकबटन को ओवरराइड करने के लिए बेहतर है ताकि आप घटना को पहले संभाल सकेंउपयोगकर्ता की पुष्टि जैसी चीजों के लिए दृश्य पॉपअप ।

देखने में डिडलॉड एक UIBarButtonItem बनाएँ और self.navigationItem.leftBarButtonItem को इसमें सेट करें

- (void) viewDidLoad
{
// change the back button to cancel and add an event handler
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”back
style:UIBarButtonItemStyleBordered
target:self
action:@selector(handleBack:)];

self.navigationItem.leftBarButtonItem = backButton;
[backButton release];

}
- (void) handleBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];

}

फिर आप कार्रवाई की पुष्टि करने के लिए UIAlertView बढ़ा सकते हैं, फिर व्यू कंट्रोलर को पॉप कर सकते हैं, आदि।

या एक नया बैकबटन बनाने के बजाय, आप बैक बटन दबाए जाने पर कार्रवाई करने के लिए UINavigationController प्रतिनिधि विधियों के अनुरूप हो सकते हैं।


UINavigationControllerDelegateतरीकों कि जब बैक बटन उपयोग किया जाता है कहा जाता है नहीं है।
अर्थ-मामले

यह तकनीक दृश्य नियंत्रक के डेटा और नेविगेशन नियंत्रक के बैक बटन से सशर्त वापसी की पुष्टि करने की अनुमति देती है।
gjpc

यह समाधान iOS 7+ के बढ़त स्वाइप फीचर को तोड़ता है
Liron Yahdav

9

यह इसका पता लगाने का सही तरीका है।

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        //do stuff

    }
}

जब दृश्य को धक्का दिया जाता है तो इस विधि को कहा जाता है। तो माता-पिता की जाँच == nil स्टैक से व्यू कंट्रोलर को पॉप करने के लिए है


9

मैं इस समाधान के साथ समाप्त होता हूं। जैसा कि हम बैक बटन व्यूडीडिसैपियर मेथड को टैप करते हैं। हम कॉल करके जाँच कर सकते हैंमूविंगफ़ॉर्मपैरेंट व्यूकंट्रोलर चयनकर्ता जो सच लौटाता है। हम डेटा को वापस भेज सकते हैं (प्रतिनिधि का उपयोग करके)। यह किसी की मदद करें।

-(void)viewDidDisappear:(BOOL)animated{

    if (self.isMovingToParentViewController) {

    }
    if (self.isMovingFromParentViewController) {
       //moving back
        //pass to viewCollection delegate and update UI
        [self.delegateObject passBackSavedData:self.dataModel];

    }
}

मत भूलो[super viewDidDisappear:animated]
सैम

9

शायद थोड़ी देर हो गई है, लेकिन मैं भी पहले जैसा ही व्यवहार चाहता था। और जिस समाधान के साथ मैं गया था, वह ऐप स्टोर में वर्तमान में एक ऐप में काफी अच्छी तरह से काम करता है। चूंकि मैंने किसी को भी इसी तरह की विधि के साथ नहीं देखा है, इसलिए मैं इसे यहां साझा करना चाहूंगा। इस समाधान का नकारात्मक पक्ष यह है कि इसके लिए उपवर्ग की आवश्यकता होती है 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
    }
}

आप यहां मेरे डेमो को देख सकते हैं

यहां छवि विवरण दर्ज करें


यह एक भयानक समाधान है और इसे एक ब्लॉगपोस्ट में बेक किया जाना चाहिए! जो मैं अभी खोज रहा हूं, उसके लिए ओवरकिल होना लगता है, लेकिन अन्य परिस्थितियों में, यह निश्चित रूप से कोशिश करने लायक है।
ASSeeger

6

"पहले से स्टैक से दृश्य को पॉप करने के लिए":

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        NSLog(@"do whatever you want here");
    }
}

5

व्यूकंट्रोलर्स से पूछने की तुलना में अधिक उपयुक्त तरीका है। आप अपने नियंत्रक को नेविगेशनबार का प्रतिनिधि बना सकते हैं जिसमें पीछे बटन है। यहाँ एक उदाहरण है। नियंत्रक के कार्यान्वयन में जहां आप बैक बटन के प्रेस को संभालना चाहते हैं, यह बताएं कि यह UINavigationBarDelegate प्रोटोकॉल को लागू करेगा:

@interface MyViewController () <UINavigationBarDelegate>

तब आपके इनिशियलाइज़ेशन कोड में कहीं (शायद viewDidLoad में) आपके कंट्रोलर को उसके नेविगेशन बार का प्रतिनिधि बनाता है:

self.navigationController.navigationBar.delegate = self;

अंत में, shouldPopItem पद्धति को लागू करें। बैक बटन दबाने पर यह तरीका सही कहलाता है। यदि आपके पास ढेर में कई नियंत्रक या नेविगेशन आइटम हैं, तो आप संभवतः यह जांचना चाहेंगे कि उन नेविगेशन आइटमों में से कौन सा पॉपअप हो रहा है (आइटम पैरामीटर), ताकि आप केवल अपने कस्टम सामान को तब करें जब आप अपेक्षा करते हैं। यहाँ एक उदाहरण है:

-(BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    NSLog(@"Back button got pressed!");
    //if you return NO, the back button press is cancelled
    return YES;
}

4
मेरे लिए काम नहीं किया .. क्योंकि यह दुबला है। : " 'स्वयं एक UINavigationBar एक नियंत्रक द्वारा प्रबंधित पर प्रतिनिधि सेट नहीं कर सकता।' *** न आया हुआ अपवाद 'NSInternalInconsistencyException', कारण की वजह से एप्लिकेशन समाप्त"
DynamicDan

यह दुर्भाग्य से UINavigationController के साथ काम नहीं करेगा, इसके बजाय, आपको इसमें UINavigationBar के साथ एक मानक UIViewController की आवश्यकता होगी। इसका मतलब यह है कि आप ऑटोमैटिक व्यूकंट्रोलर के कई लाभ नहीं उठा सकते हैं जो कि नेविगेशनकंट्रोलर आपको देता है। माफ़ करना!
कार्लोस गुज़मैन 15

मैंने सिर्फ नेविगेशनबार्कर कंट्रोलर के बजाय UINavigationBar का उपयोग किया और फिर यह ठीक काम करता है। मुझे पता है कि सवाल नेविगेशनबार्ककंट्रोलर के बारे में है, लेकिन यह समाधान दुबला है।
appunited

3

यदि आप "viewWillDisappear" या इसी तरह की विधि का उपयोग नहीं कर सकते हैं, तो UINavigationController को उप-करने की कोशिश करें। यह शीर्ष लेख वर्ग है:

#import <Foundation/Foundation.h>
@class MyViewController;

@interface CCNavigationController : UINavigationController

@property (nonatomic, strong) MyViewController *viewController;

@end

कार्यान्वयन वर्ग:

#import "CCNavigationController.h"
#import "MyViewController.h"

@implementation CCNavigationController {

}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    @"This is the moment for you to do whatever you want"
    [self.viewController doCustomMethod];
    return [super popViewControllerAnimated:animated];
}

@end

दूसरे हाथ में, आपको अपने कस्टम नेविगेशनकंट्रोलर से इस व्यू-कॉन्ट्रोलर को लिंक करने की आवश्यकता है, इसलिए, अपने नियमित व्यू-कॉन्ट्रोलर के लिए अपने व्यूडॉलड विधि में यह करें:

@implementation MyViewController {
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        ((CCNavigationController*)self.navigationController).viewController = self;
    }
}

3

यहाँ एक और तरीका है जिसे मैंने लागू किया है (इसे एक आराम से बहस के साथ परीक्षण नहीं किया है, लेकिन यह शायद अंतर नहीं करेगा, जैसा कि दूसरों ने इस पृष्ठ पर अन्य समाधानों के संबंध में कहा है) माता-पिता के नियंत्रक को बच्चे को धक्का देने से पहले कार्रवाई करने के लिए देखें। दृश्य स्टैक से पॉपप हो जाता है (मैंने इसे मूल UINavigationController से कुछ स्तर नीचे इस्तेमाल किया था)। इससे पहले कि चाइल्डवीसी को धक्का दिया जाता है, वह भी कार्रवाई करने के लिए इस्तेमाल किया जा सकता है। यह एक कस्टम UIBarButtonItem या UIButton बनाने के बजाय iOS सिस्टम बैक बटन के साथ काम करने का अतिरिक्त लाभ है।

  1. क्या आपके पैरेंट वीसी UINavigationControllerDelegateप्रोटोकॉल को अपनाते हैं और प्रतिनिधि संदेशों के लिए पंजीकरण करते हैं:

    MyParentViewController : UIViewController <UINavigationControllerDelegate>
    
    -(void)viewDidLoad {
        self.navigationcontroller.delegate = self;
    }
  2. इस UINavigationControllerDelegateउदाहरण विधि को इसमें लागू करें MyParentViewController:

    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
        // Test if operation is a pop; can also test for a push (i.e., do something before the ChildVC is pushed
        if (operation == UINavigationControllerOperationPop) {
            // Make sure it's the child class you're looking for
            if ([fromVC isKindOfClass:[ChildViewController class]]) {
                // Can handle logic here or send to another method; can also access all properties of child VC at this time
                return [self didPressBackButtonOnChildViewControllerVC:fromVC];
            }
        }
        // If you don't want to specify a nav controller transition
        return nil;
    }
  3. यदि आप उपरोक्त UINavigationControllerDelegateउदाहरण विधि में एक विशिष्ट कॉलबैक फ़ंक्शन निर्दिष्ट करते हैं

    -(id <UIViewControllerAnimatedTransitioning>)didPressBackButtonOnAddSearchRegionsVC:(UIViewController *)fromVC {
        ChildViewController *childVC = ChildViewController.new;
        childVC = (ChildViewController *)fromVC;
    
        // childVC.propertiesIWantToAccess go here
    
        // If you don't want to specify a nav controller transition
        return nil;

    }


1

स्विफ्ट में यह मेरे लिए काम करता है:

override func viewWillDisappear(_ animated: Bool) {
    if self.navigationController?.viewControllers.index(of: self) == nil {
        // back button pressed or back gesture performed
    }

    super.viewWillDisappear(animated)
}

0

यदि आप एक स्टोरीबोर्ड का उपयोग कर रहे हैं और आप एक पुश सेग से आ रहे हैं, तो आप भी ओवरराइड कर सकते हैं shouldPerformSegueWithIdentifier:sender:

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.