नेविगेशन कंट्रोलर में बैक बटन के लिए एक्शन सेट करना


180

मैं नेविगेशन कंट्रोलर में बैक बटन की डिफ़ॉल्ट कार्रवाई को ओवरराइट करने की कोशिश कर रहा हूं। मैंने लक्ष्य बटन को कस्टम बटन पर कार्रवाई प्रदान की है। विचित्र बात यह है कि इसे असाइन करते समय हालांकि बैकबटन विशेषता यह उन पर ध्यान नहीं देती है और यह केवल वर्तमान दृश्य को पॉप करता है और रूट पर वापस जाता है:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                                  initWithTitle: @"Servers" 
                                  style:UIBarButtonItemStylePlain 
                                  target:self 
                                  action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;

जैसे ही मैंने इसे इसके माध्यम से सेट leftBarButtonItemकिया, navigationItemयह मेरी कार्रवाई कहता है, हालांकि तब बटन एक तीर के बजाय एक सादे दौर की तरह दिखता है:

self.navigationItem.leftBarButtonItem = backButton;

रूट दृश्य पर वापस जाने से पहले मुझे अपनी कस्टम क्रिया को कॉल करने के लिए कैसे मिल सकता है? क्या डिफ़ॉल्ट बैक एक्शन को अधिलेखित करने का एक तरीका है, या क्या कोई ऐसा तरीका है जो हमेशा एक दृश्य को छोड़ते समय कहा जाता है ( viewDidUnloadऐसा नहीं करता है)?


कार्रवाई: @selector (घर)]; जरूरत है: चयनकर्ता कार्रवाई के बाद: @selector (होम :)]; अन्यथा यह काम नहीं करेगा
9

7
@PartySoft यह सच नहीं है जब तक कि विधि को बृहदान्त्र के साथ घोषित नहीं किया जाता है। यह बटन कॉल चयनकर्ताओं को पूरी तरह से मान्य है जो किसी भी पैरामीटर को नहीं लेते हैं।
mbm29414 13:14


3
क्यों Apple एक शैली प्रदान नहीं करेगा बटन के साथ एक बटन के आकार का? बहुत स्पष्ट लगता है।
जॉनके

जवाबों:


363

इसे व्यू कंट्रोलर में डालने का प्रयास करें जहाँ आप प्रेस का पता लगाना चाहते हैं:

-(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];
}

1
यह एक चालाक, स्वच्छ, अच्छी और बहुत अच्छी तरह से सोचा समाधान नहीं है
Boliva

6
+1 महान हैक, लेकिन पॉप के एनीमेशन पर नियंत्रण प्रदान नहीं करता है
मैट

3
मेरे लिए काम नहीं करता है अगर मैं एक बटन के माध्यम से प्रतिनिधि को एक संदेश भेजता हूं और प्रतिनिधि नियंत्रक को पॉप करता है - यह अभी भी आग लगाता है।
SAHM

21
एक अन्य समस्या यह है कि यदि उपयोगकर्ता बैक बटन दबाता है या यदि आप प्रोग्राम को [self.navigationController popViewControllerAnimated: YES] कहते हैं, तो आप अंतर नहीं कर सकते हैं
चेस रॉबर्ट्स

10
बस एक FYI करें: स्विफ्ट संस्करण:if (find(self.navigationController!.viewControllers as! [UIViewController],self)==nil)
hEADcRASH

177

मैंने UIViewController-BackButtonHandler एक्सटेंशन लागू किया है । इसे किसी भी चीज़ को उप-वर्ग करने की आवश्यकता नहीं है, बस इसे अपनी परियोजना में डालें और कक्षा navigationShouldPopOnBackButtonमें विधि को ओवरराइड करें UIViewController:

-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controler
}

नमूना एप्लिकेशन डाउनलोड करें


16
यह सबसे अच्छा समाधान है जिसे मैंने देखा है, अपने स्वयं के कस्टम UIButton का उपयोग करने से बेहतर और सरल। धन्यवाद!
ramirogm

4
यह navigationBar:shouldPopItem:एक निजी पद्धति नहीं है क्योंकि यह UINavigationBarDelegateप्रोटोकॉल का एक हिस्सा है ।
onegray

1
लेकिन क्या UINavigationController पहले से ही प्रतिनिधि को लागू करता है (वापस लौटने के लिए YES)? या भविष्य में होगा? उपवर्ग शायद एक सुरक्षित विकल्प है
सैम

8
मैंने अभी iOS 7.1 में इसे (बहुत बढ़िया बीटीडब्ल्यू) लागू किया है और देखा है कि NOवापस जाने के बाद बटन अक्षम अवस्था में रहता है (नेत्रहीन, क्योंकि यह अभी भी प्राप्त करता है और घटनाओं को छूने के लिए प्रतिक्रिया करता है)। मैंने नेविगेशन बार साक्षात्कारों के माध्यम से चेक और साइक्लिंग के elseलिए एक बयान जोड़कर shouldPopऔर alphaएनीमेशन ब्लॉक के अंदर ज़रूरत पड़ने पर 1 पर मान वापस सेट करने के लिए इसके चारों ओर काम किया : gist.github.com/idevsoftware/9754057
boliva

2
यह सबसे अच्छा विस्तार में से एक है जिसे मैंने कभी देखा है। बहुत बहुत धन्यवाद।
श्रीकांत

42

इसके विपरीत Amgrammer ने कहा, यह संभव है। आपको अपना उपवर्ग करना होगा navigationController। मैंने यहां सब कुछ समझाया (उदाहरण कोड सहित)।


Apple के प्रलेखन ( developer.apple.com/iphone/library/documentation/UIKit/… ) का कहना है कि "यह वर्ग उपवर्ग के लिए अभिप्रेत नहीं है"। हालांकि मुझे यकीन नहीं है कि वे इससे क्या मतलब रखते हैं - उनका मतलब हो सकता है "आपको सामान्य रूप से ऐसा करने की आवश्यकता नहीं होनी चाहिए", या उनका मतलब हो सकता है "यदि आप हमारे नियंत्रक के साथ गड़बड़ करते हैं तो हम आपके ऐप को अस्वीकार कर देंगे" ...
Kuba Suder

यह निश्चित रूप से ऐसा करने का एकमात्र तरीका है। काश मैं आपको अधिक अंक हंस सकता है!
एडम एबेरबेक

1
क्या आप वास्तव में इस पद्धति का उपयोग करके किसी दृश्य को बाहर निकलने से रोक सकते हैं? यदि आप दृश्य से बाहर नहीं निकलना चाहते थे तो आप popViewControllerAnimated पद्धति को क्या लौटाएंगे?
जोसेफ 17

1
हां तुम कर सकते हो। बस अपने कार्यान्वयन में सुपरक्लास पद्धति को न कहें, जागरूक रहें! आपको ऐसा नहीं करना चाहिए, उपयोगकर्ता नेविगेशन में वापस जाने की उम्मीद करता है। आप क्या कर सकते हैं एक पुष्टिकरण के लिए पूछना है। सेब के दस्तावेज के अनुसार popViewController रिटर्न: "व्यू कंट्रोलर जो स्टैक से पॉप किया गया था।" इसलिए जब कुछ भी नहीं पॉप अप किया जाता है, तो आपको वापस लौटना चाहिए;
हंसपिन्कर 19

1
@HansPickaers मुझे लगता है कि एक दृश्य को बाहर निकलने से रोकने के बारे में आपका जवाब कुछ गलत हो सकता है। यदि मैं popViewControllerAnimated के उपवर्ग कार्यान्वयन से एक 'पुष्टिकरण' संदेश प्रदर्शित करता हूं :, तो नेविगेशनबेर अभी भी पेड़ में एक स्तर को एनिमेट करता है, चाहे मैं वापस लौटूं। ऐसा इसलिए लगता है क्योंकि नेवी बार पर बैक बटन कॉल्स को क्लिक करना चाहिए। मैं सिफारिश के रूप में अपने उपवर्ग विधि से शून्य लौट रहा हूं।
दीपविनर

15

स्विफ्ट संस्करण:

( https://stackoverflow.com/a/19132881/826435 से )

आपके विचार नियंत्रक में आप बस एक प्रोटोकॉल के अनुरूप होते हैं और आपको जो भी कार्रवाई की आवश्यकता होती है वह करते हैं:

extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}

फिर एक क्लास बनाएं, कहें NavigationController+BackButtonऔर बस नीचे दिए गए कोड को कॉपी-पेस्ट करें:

protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}

शायद मैं कुछ याद किया, लेकिन यह मेरे लिए काम नहीं करता है, विधि performSomeActionOnThePressOfABackButton के विस्तार को कभी नहीं कहा जाता है
टर्वी

@FlorentBreton शायद एक गलतफहमी है? shouldPopOnBackButtonPressजब तक कोई बग न हो, तब तक बुलाया जाना चाहिए। performSomeActionOnThePressOfABackButtonकेवल एक बना-बनाया तरीका है जो मौजूद नहीं है।
किलकेडिस

मुझे यह समझ में आया, इसीलिए मैंने performSomeActionOnThePressOfABackButtonअपने कंट्रोलर में एक विधि बनाई, जब किसी विशिष्ट क्रिया को अंजाम देने के लिए जब बैक बटन दबाया जाता है, लेकिन इस विधि को कभी भी कॉल नहीं किया जाता है, तो यह क्रिया एक सामान्य बैक रिटर्न है
टर्वी नोव

1
न मेरे लिए काम कर रहा है। कंधे की विधि कभी नहीं कहा जाता है। क्या आपने एक प्रतिनिधि को कहीं रखा था?
टिम ऑटिन

@TimAutin मैंने अभी इसे फिर से परीक्षण किया है और ऐसा लगता है कि कुछ बदल गया है। मुख्य भाग यह समझने के लिए कि UINavigationController, navigationBar.delegateनेविगेशन नियंत्रक के लिए सेट है। इसलिए तरीकों को बुलाया जाना चाहिए। हालाँकि, स्विफ्ट में, मैं उन्हें बुलाया नहीं जा सकता, यहां तक ​​कि एक उपवर्ग में भी। हालाँकि, मैंने उन्हें ऑब्जेक्टिव-सी में बुलाया जाना था, इसलिए मैं अभी के लिए ऑब्जेक्टिव-सी संस्करण का उपयोग करूंगा। एक स्विफ्ट बग हो सकता है।
किलोईडिस

5

यह सीधे संभव नहीं है। कुछ विकल्प हैं:

  1. अपना स्वयं का रिवाज बनाएं UIBarButtonItemजो परीक्षण पास होने पर टैप और पॉप पर मान्य होता है
  2. एक UITextFieldप्रतिनिधि विधि का उपयोग करके प्रपत्र फ़ील्ड सामग्री को मान्य करें , जैसे कि -textFieldShouldReturn:, जिसे कीबोर्ड पर Returnया Doneबटन दबाए जाने के बाद कहा जाता है

पहले विकल्प का नकारात्मक पक्ष यह है कि बैक बटन के बाईं ओर-इंगित-तीर शैली को कस्टम बार बटन से एक्सेस नहीं किया जा सकता है। तो आपको एक छवि का उपयोग करना होगा या एक नियमित शैली बटन के साथ जाना होगा।

दूसरा विकल्प अच्छा है क्योंकि आपको टेक्स्ट फ़ील्ड वापस डेलिगेट विधि में मिलती है, इसलिए आप डेलिगेट कॉल-बैक पद्धति को भेजे गए विशिष्ट टेक्स्ट फ़ील्ड पर अपने सत्यापन तर्क को लक्षित कर सकते हैं।


5

कुछ थ्रेडिंग कारणों के लिए, @HansPinckaers द्वारा उल्लिखित समाधान मेरे लिए सही नहीं था, लेकिन मुझे बैक बटन पर टच पकड़ने का बहुत आसान तरीका मिला, और मैं इसे नीचे पिन करना चाहता हूं, क्योंकि यह धोखे के घंटों से बच सकता है। कोई और। चाल वास्तव में आसान है: बस अपने UINavigationBar के लिए एक सबव्यू के रूप में एक पारदर्शी UIButton जोड़ें, और उसके लिए अपने चयनकर्ताओं को सेट करें जैसे कि यह असली बटन था! यहां एक उदाहरण मोनोटच और सी # का उपयोग किया गया है, लेकिन ऑब्जेक्टिव-सी के लिए अनुवाद बहुत मुश्किल नहीं होना चाहिए।

public class Test : UIViewController {
    public override void ViewDidLoad() {
        UIButton b = new UIButton(new RectangleF(0, 0, 60, 44)); //width must be adapted to label contained in button
        b.BackgroundColor = UIColor.Clear; //making the background invisible
        b.Title = string.Empty; // and no need to write anything
        b.TouchDown += delegate {
            Console.WriteLine("caught!");
            if (true) // check what you want here
                NavigationController.PopViewControllerAnimated(true); // and then we pop if we want
        };
        NavigationController.NavigationBar.AddSubview(button); // insert the button to the nav bar
    }
}

मजेदार तथ्य: परीक्षण के प्रयोजनों के लिए और मेरे नकली बटन के लिए अच्छे आयाम खोजने के लिए, मैंने इसकी पृष्ठभूमि का रंग नीला कर दिया है ... और यह पीछे के बटन के पीछे दिखाई देता है! वैसे भी, यह अभी भी मूल बटन को लक्षित करने वाले किसी भी स्पर्श को पकड़ता है।


3

यह तकनीक आपको किसी भी दृश्य नियंत्रक के शीर्षक को प्रभावित किए बिना "बैक" बटन के पाठ को बदलने या एनीमेशन के दौरान बैक बटन पाठ को देखने की अनुमति देती है।

कॉलिंग व्यू कंट्रोलर में इसे init मेथड में जोड़ें :

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];

3

सबसे आसान उपाय

आप UINavigationController के प्रतिनिधि विधियों का उपयोग कर सकते हैं। willShowViewControllerजब वीसी का बैक बटन दबाया जाता है तो वह विधि कहलाती है

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

सुनिश्चित करें कि आपका व्यू कंट्रोलर विरासत में दिए गए नेविगेशनकंट्रोलर के प्रतिनिधि के रूप में खुद को सेट करता है और UINavigationControllerDelegate प्रोटोकॉल के अनुरूप है
जस्टिन मिलो

3

यहाँ मेरा स्विफ्ट समाधान है। UIViewController के अपने उपवर्ग में, नेविगेशन को हटा दें।

extension UIViewController {
    func navigationShouldPopOnBackButton() -> Bool {
        return true
    }
}

extension UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {
        if let vc = self.topViewController {
            if vc.navigationShouldPopOnBackButton() {
                self.popViewControllerAnimated(true)
            } else {
                for it in navigationBar.subviews {
                    let view = it as! UIView
                    if view.alpha < 1.0 {
                        [UIView .animateWithDuration(0.25, animations: { () -> Void in
                            view.alpha = 1.0
                        })]
                    }
                }
                return false
            }
        }
        return true
    }

}

UIViewController में ओवरराइडिंग पद्धति नेविगेशनशोल्डपॉपऑनबैकबटन काम नहीं करता है - कार्यक्रम अभिभावक पद्धति को निष्पादित करता है न कि ओवरराइड करता है। उसके लिए कोई उपाय? किसी को भी एक ही मुद्दा है?
पावेल कैला

अगर यह सच है तो सभी
रूटव्यू पर

@Pawriwes यहाँ एक समाधान है जो मैंने लिखा है कि मेरे लिए काम करने लगता है: stackoverflow.com/a/34343418/826435
kgaidis

3

एक समाधान मिला जो बैक बटन स्टाइल को भी बरकरार रखता है। अपने दृश्य नियंत्रक में निम्न विधि जोड़ें।

-(void) overrideBack{

    UIButton *transparentButton = [[UIButton alloc] init];
    [transparentButton setFrame:CGRectMake(0,0, 50, 40)];
    [transparentButton setBackgroundColor:[UIColor clearColor]];
    [transparentButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.navigationController.navigationBar addSubview:transparentButton];


}

अब निम्नलिखित विधि में आवश्यकतानुसार कार्यक्षमता प्रदान करें:

-(void)backAction:(UIBarButtonItem *)sender {
    //Your functionality
}

यह सब पारदर्शी बटन के साथ बैक बटन को कवर करना है;)


3

ओवरराइडिंग नेविगेशनबार (_ नेविगेशनबेर: शोपीस) : यह एक अच्छा विचार नहीं है, भले ही यह काम करता हो। मेरे लिए यह वापस नेविगेट करने पर यादृच्छिक दुर्घटनाओं को उत्पन्न करता है। मैं आपको सलाह देता हूं कि आप नेविगेशन बटन से डिफ़ॉल्ट बैकबटन को हटाकर और नीचे जैसा कस्टम बैक बटन बनाकर बैक बटन को ओवरराइड करें:

override func viewDidLoad(){
   super.viewDidLoad()
   
   navigationItem.leftBarButton = .init(title: "Go Back", ... , action: #selector(myCutsomBackAction) 

   ...
 
}

========================================

साथ पिछली प्रतिक्रियाएं पर बिल्डिंग UIAlert में Swift5 एक में अतुल्यकालिक रास्ता


protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ())
}

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
      
        if viewControllers.count < navigationBar.items!.count {
            return true
        }
        
        // Check if we have a view controller that wants to respond to being popped
        
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            
            viewController.shouldPopOnBackButtonPress { shouldPop in
                if (shouldPop) {
                    /// on confirm => pop
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                } else {
                    /// on cancel => do nothing
                }
            }
            /// return false => so navigator will cancel the popBack
            /// until user confirm or cancel
            return false
        }else{
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        }
        return true
    }
}

अपने कंट्रोलर पर


extension MyController: NavigationControllerBackButtonDelegate {
    
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ()) {
    
        let msg = "message"
        
        /// show UIAlert
        alertAttention(msg: msg, actions: [
            
            .init(title: "Continuer", style: .destructive, handler: { _ in
                completion(true)
            }),
            .init(title: "Annuler", style: .cancel, handler: { _ in
                completion(false)
            })
            ])
   
    }

}

क्या आप अगर viewControllers.count के साथ क्या चल रहा है के बारे में विवरण प्रदान कर सकते हैं?
H4Hugo

// बहुत अधिक नेविगेशन आइटम को पॉप करने के एक सिंक्रनाइज़ेशन मुद्दे से रोकता है // और असामान्य टैपिंग से पर्याप्त दृश्य नियंत्रक या
उपाध्यक्ष नहीं

2

मुझे विश्वास नहीं होता कि यह आसानी से संभव है। एकमात्र तरीका जो मुझे लगता है कि इसके चारों ओर पाने के लिए अपनी खुद की बैक बटन एरो छवि बनाना है। यह पहली बार में मेरे लिए निराशाजनक था, लेकिन मैं देखता हूं कि, निरंतरता के लिए, इसे छोड़ दिया गया था।

आप एक नियमित बटन बनाकर और डिफ़ॉल्ट बैक बटन को छिपाकर करीब (तीर के बिना) प्राप्त कर सकते हैं:

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Servers" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;

2
हाँ समस्या यह है कि मैं चाहता हूँ कि यह सामान्य बैक बटन की तरह दिखे, बस मुझे अपनी कस्टम एक्शन को कॉल करने की आवश्यकता है ...
तोते

2

वहाँ एक आसान तरीका सिर्फ उपवर्गीकरण द्वारा प्रतिनिधि विधि की UINavigationBarऔर ओवरराइड ShouldPopItemविधि


मुझे लगता है कि आप UINavigationController वर्ग को उप-वर्ग कहने और एक कंधे की विधि लागू करने का मतलब है। वह मेरे लिए अच्छा काम कर रहा है। हालाँकि, उस विधि को केवल YES या NO को वापस नहीं करना चाहिए जैसा कि आप अपेक्षा करेंगे। एक स्पष्टीकरण और समाधान यहां उपलब्ध है: stackoverflow.com/a/7453933/462162
arlomedia

2

onegray का समाधान सुरक्षित नहीं है। Apple के आधिकारिक दस्तावेजों के अनुसार, https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CasizingExistingClasses/CustomizingExistingClasses.html , हमें ऐसा करने से बचना चाहिए।

"यदि किसी श्रेणी में घोषित विधि का नाम मूल वर्ग में एक विधि के समान है, या उसी वर्ग (या यहां तक ​​कि एक सुपरक्लास) पर किसी अन्य श्रेणी में एक विधि है, तो व्यवहार अपरिभाषित है कि किस पद्धति का उपयोग किया जाता है रनटाइम पर। यह एक समस्या होने की संभावना है यदि आप अपनी कक्षाओं के साथ श्रेणियों का उपयोग कर रहे हैं, लेकिन मानक कोको या कोको टच कक्षाओं में तरीकों को जोड़ने के लिए श्रेणियों का उपयोग करते समय समस्याएं हो सकती हैं। "


2

स्विफ्ट का उपयोग करना:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.navigationController?.topViewController != self {
        print("back button tapped")
    }
}

IOS 10 और शायद इससे पहले, यह अब काम नहीं कर रहा है।
मुरैना सगल

2

यहां से फायर होने से पहले नेविगेशन बार बैक बटन इवेंट को पकड़ने के लिए @oneway के जवाब का स्विफ्ट 3 संस्करण है । जैसा कि इसके लिए UINavigationBarDelegateउपयोग नहीं किया जा सकता है UIViewController, आपको एक प्रतिनिधि बनाने की आवश्यकता navigationBar shouldPopहै जिसे कहा जाने पर ट्रिगर किया जाएगा ।

@objc public protocol BackButtonDelegate {
      @objc optional func navigationShouldPopOnBackButton() -> Bool 
}

extension UINavigationController: UINavigationBarDelegate  {

    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

        if viewControllers.count < (navigationBar.items?.count)! {                
            return true
        }

        var shouldPop = true
        let vc = self.topViewController

        if vc.responds(to: #selector(vc.navigationShouldPopOnBackButton)) {
            shouldPop = vc.navigationShouldPopOnBackButton()
        }

        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for subView in navigationBar.subviews {
                if(0 < subView.alpha && subView.alpha < 1) {
                    UIView.animate(withDuration: 0.25, animations: {
                        subView.alpha = 1
                    })
                }
            }
        }

        return false
    }
}

और फिर, आपके विचार नियंत्रक में प्रतिनिधि फ़ंक्शन जोड़ें:

class BaseVC: UIViewController, BackButtonDelegate {
    func navigationShouldPopOnBackButton() -> Bool {
        if ... {
            return true
        } else {
            return false
        }        
    }
}

मैंने महसूस किया है कि हम अक्सर उपयोगकर्ताओं के लिए एक चेतावनी नियंत्रक जोड़ना चाहते हैं ताकि यह तय किया जा सके कि वे वापस जाना चाहते हैं या नहीं। यदि हां, तो आप हमेशा कर सकते हैं return falseमें navigationShouldPopOnBackButton()समारोह और कुछ इस तरह करने से आपके विचार नियंत्रक बंद:

func navigationShouldPopOnBackButton() -> Bool {
     let alert = UIAlertController(title: "Warning",
                                          message: "Do you want to quit?",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { UIAlertAction in self.yes()}))
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { UIAlertAction in self.no()}))
            present(alert, animated: true, completion: nil)
      return false
}

func yes() {
     print("yes")
     DispatchQueue.main.async {
            _ = self.navigationController?.popViewController(animated: true)
        }
}

func no() {
    print("no")       
}

मुझे एक त्रुटि मिल रही है: Value of type 'UIViewController' has no member 'navigationShouldPopOnBackButton' जब मैं आपके कोड को संकलित करने की कोशिश करता हूं, तो लाइन के लिए if vc.responds(to: #selector(v...भी, self.topViewControllerएक वैकल्पिक रिटर्न देता है और इसके लिए एक चेतावनी भी है।
शंकर १

FWIW, मैंने उस कोड को बनाकर तय किया है: let vc = self.topViewController as! MyViewControllerऔर यह अब तक ठीक काम करता है। यदि आप मानते हैं कि यह एक सही बदलाव है, तो आप कोड को संपादित कर सकते हैं। इसके अलावा, अगर आपको लगता है कि ऐसा नहीं किया जाना चाहिए, तो मुझे यह जानकर खुशी होगी कि क्यों। इस कोड के लिए धन्यवाद। आपको शायद इस बारे में एक ब्लॉग पोस्ट लिखना चाहिए, क्योंकि यह जवाब वोट के अनुसार नीचे दफन है।
शंकर १

@SankarP जिस कारण से आपको त्रुटि मिली है वह आपके MyViewControllerअनुरूप नहीं है BackButtonDelegate। अनचाहे को मजबूर करने के बजाय, आपको guard let vc = self.topViewController as? MyViewController else { return true }संभावित दुर्घटना से बचने के लिए करना चाहिए ।
लॉलिएट

धन्यवाद। मुझे लगता है कि गार्ड स्टेटमेंट बन जाना चाहिए: guard let vc = self.topViewController as? MyViewController else { self.popViewController(animated: true) return true }यह सुनिश्चित करने के लिए कि स्क्रीन सही पेज पर जा रही है अगर यह सही तरीके से नहीं डाली जा सकती है। मैं अब समझता हूं कि navigationBarसभी वीसी में फ़ंक्शन को बुलाया जाता है, न कि केवल व्यू-कॉन्ट्रोलर को जहां यह कोड मौजूद है। हो सकता है कि आपके उत्तर में भी कोड को अपडेट करना अच्छा होगा? धन्यवाद।
शंकर

2

स्विफ्ट 4 iOS 11.3 संस्करण:

यह https://stackoverflow.com/a/34343418/4316579 से kgaidis के उत्तर पर बनाता है

मुझे यकीन नहीं है कि जब एक्सटेंशन ने काम करना बंद कर दिया था, लेकिन इस लेखन (स्विफ्ट 4) के समय, ऐसा प्रतीत होता है कि एक्सटेंशन को तब तक निष्पादित नहीं किया जाएगा जब तक कि आप नीचे बताए अनुसार UINavigationBarDelegate अनुरूपता की घोषणा नहीं करते।

आशा है कि यह उन लोगों की मदद करता है जो सोच रहे हैं कि उनका विस्तार अब काम क्यों नहीं करता है।

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

    }
}

1

लक्ष्य और एक्शन चर का उपयोग करके जो आप वर्तमान में 'निल' छोड़ रहे हैं, आपको अपने सेव-डायलॉग को वायर करने में सक्षम होना चाहिए ताकि बटन "चयनित" होने पर उन्हें कॉल किया जा सके। बाहर देखो, यह अजीब क्षणों में शुरू हो सकता है।

मैं ज्यादातर Amagrammer से सहमत हूं, लेकिन मुझे नहीं लगता कि यह तीर कस्टम के साथ बटन बनाने के लिए कठिन होगा। मैं सिर्फ बैक बटन का नाम बदलूंगा, स्क्रीन शॉट लूंगा, फोटोशूट बटन की जरूरत होगी, और आपके बटन के शीर्ष पर छवि होगी।


मैं मानता हूं कि आप फोटोशॉप कर सकते हैं और मुझे लगता है कि मैं ऐसा कर सकता हूं अगर मैं वास्तव में यह चाहता था, लेकिन अब मैंने इसे बदलने का फैसला किया है और इस तरह से काम करने के लिए एक छोटा सा महसूस करना चाहता हूं।
जॉन बॉलिंगर

हाँ, सिवाय इसके कि क्रियाओं को ट्रिगर नहीं किया जाता है जब वे बैकबार्टन इटेम से जुड़े होते हैं। मुझे नहीं पता कि यह बग है या फीचर; यह संभव है कि Apple को भी पता न हो। फ़ोटोशोपिंग अभ्यास के लिए, फिर से, मैं सावधान रहूंगा कि ऐप्पल एक विहित प्रतीक के दुरुपयोग के लिए ऐप को अस्वीकार कर देगा।
Amagrammer

हेड-अप: यह उत्तर डुप्लिकेट से मर्ज किया गया है।
शोग

1

आप नेविगेशनबार्स राइट बटन आइटम तक पहुँचने की कोशिश कर सकते हैं और इसकी चयनकर्ता संपत्ति सेट कर सकते हैं ... एक संदर्भ UIBarButtonItem संदर्भ , एक और बात अगर यह काम नहीं करेगा जो काम को हरा देगा, एक कस्टम UIBarButtonItem के लिए नौसेना बार के सही बटन आइटम सेट करें जिसे आप इसके चयनकर्ता को बनाएं और सेट करें ... आशा है कि यह मदद करता है


हेड-अप: यह उत्तर डुप्लिकेट से मर्ज किया गया है।
शोग

1

ऐसे फॉर्म के लिए जिसे उपयोगकर्ता इनपुट की आवश्यकता होती है, मैं इसे आपके नेविगेशन स्टैक के भाग के बजाय "मोडल" के रूप में लागू करने की सलाह दूंगा। इस तरह उन्हें फॉर्म पर व्यवसाय की देखभाल करनी होती है, फिर आप इसे कस्टम कर सकते हैं और कस्टम बटन का उपयोग करके इसे खारिज कर सकते हैं। आप एक नेव बार भी डिजाइन कर सकते हैं जो आपके बाकी ऐप के समान दिखता है लेकिन आपको अधिक नियंत्रण प्रदान करता है।


हेड-अप: यह उत्तर डुप्लिकेट से मर्ज किया गया है।
शोग

1

बैक बटन को इंटरसेप्ट करने के लिए, बस इसे पारदर्शी UIControl से कवर करें और टच को इंटरसेप्ट करें।

@interface MyViewController : UIViewController
{
    UIControl   *backCover;
    BOOL        inhibitBackButtonBOOL;
}
@end

@implementation MyViewController
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Cover the back button (cannot do this in viewWillAppear -- too soon)
    if ( backCover == nil ) {
        backCover = [[UIControl alloc] initWithFrame:CGRectMake( 0, 0, 80, 44)];
#if TARGET_IPHONE_SIMULATOR
        // show the cover for testing
        backCover.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.15];
#endif
        [backCover addTarget:self action:@selector(backCoverAction) forControlEvents:UIControlEventTouchDown];
        UINavigationBar *navBar = self.navigationController.navigationBar;
        [navBar addSubview:backCover];
    }
}

-(void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [backCover removeFromSuperview];
    backCover = nil;
}

- (void)backCoverAction
{
    if ( inhibitBackButtonBOOL ) {
        NSLog(@"Back button aborted");
        // notify the user why...
    } else {
        [self.navigationController popViewControllerAnimated:YES]; // "Back"
    }
}
@end

हेड-अप: यह उत्तर डुप्लिकेट से मर्ज किया गया है।
शोग

1

कम से कम Xcode 5 में, एक सरल और बहुत अच्छा (पूर्ण नहीं) समाधान है। आईबी में, यूटिलिटीज फलक से एक बार बटन आइटम को खींचें और नेविगेशन बार के बाईं ओर छोड़ दें जहां बैक बटन होगा। "बैक" पर लेबल सेट करें। आपके पास एक कार्यशील बटन होगा जिसे आप अपने IBAction से जोड़ सकते हैं और अपने दृश्य नियंत्रक को बंद कर सकते हैं। मैं कुछ काम कर रहा हूं और फिर एक आराम से बहस शुरू कर रहा हूं और यह पूरी तरह से काम करता है।

जो आदर्श नहीं है वह यह है कि इस बटन को <तीर नहीं मिलता है और पिछले VCs शीर्षक को आगे नहीं बढ़ाता है, लेकिन मुझे लगता है कि इसे प्रबंधित किया जा सकता है। मेरे उद्देश्यों के लिए, मैंने नया बैक बटन "डन" बटन होने के लिए सेट किया है, इसलिए यह उद्देश्य स्पष्ट है।

आप आईबी नेविगेटर में दो बैक बटन के साथ भी समाप्त होते हैं, लेकिन स्पष्टता के लिए इसे लेबल करना काफी आसान है।

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


1

तीव्र

override func viewWillDisappear(animated: Bool) {
    let viewControllers = self.navigationController?.viewControllers!
    if indexOfArray(viewControllers!, searchObject: self) == nil {
        // do something
    }
    super.viewWillDisappear(animated)
}

func indexOfArray(array:[AnyObject], searchObject: AnyObject)-> Int? {
    for (index, value) in enumerate(array) {
        if value as UIViewController == searchObject as UIViewController {
            return index
        }
    }
    return nil
}

1

इस दृष्टिकोण ने मेरे लिए काम किया (लेकिन "बैक" बटन में "<" साइन नहीं होगा):

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem* backNavButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
                                                                      style:UIBarButtonItemStyleBordered
                                                                     target:self
                                                                     action:@selector(backButtonClicked)];
    self.navigationItem.leftBarButtonItem = backNavButton;
}

-(void)backButtonClicked
{
    // Do something...
    AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    [delegate.navController popViewControllerAnimated:YES];
}

1

@ Onegray के उत्तर का स्विफ्ट संस्करण

protocol RequestsNavigationPopVerification {
    var confirmationTitle: String { get }
    var confirmationMessage: String { get }
}

extension RequestsNavigationPopVerification where Self: UIViewController {
    var confirmationTitle: String {
        return "Go back?"
    }

    var confirmationMessage: String {
        return "Are you sure?"
    }
}

final class NavigationController: UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {

        guard let requestsPopConfirm = topViewController as? RequestsNavigationPopVerification else {
            popViewControllerAnimated(true)
            return true
        }

        let alertController = UIAlertController(title: requestsPopConfirm.confirmationTitle, message: requestsPopConfirm.confirmationMessage, preferredStyle: .Alert)

        alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                let dimmed = navigationBar.subviews.flatMap { $0.alpha < 1 ? $0 : nil }
                UIView.animateWithDuration(0.25) {
                    dimmed.forEach { $0.alpha = 1 }
                }
            })
            return
        })

        alertController.addAction(UIAlertAction(title: "Go back", style: .Default) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                self.popViewControllerAnimated(true)
            })
        })

        presentViewController(alertController, animated: true, completion: nil)

        return false
    }
}

अब किसी भी नियंत्रक में, बस के अनुरूप है RequestsNavigationPopVerificationऔर यह व्यवहार डिफ़ॉल्ट रूप से अपनाया जाता है।


1

उपयोग isMovingFromParentViewController

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)

    if self.isMovingFromParentViewController {
        // current viewController is removed from parent
        // do some work
    }
}

क्या आप आगे बता सकते हैं कि यह कैसे साबित करता है कि बैक बटन टैप किया गया था?
मरे सगल

यह सरल है, लेकिन यह केवल तभी काम करता है जब आप किसी भी बच्चे के दृष्टिकोण से उस दृश्य पर वापस आ सकते हैं जिसे आप लोड कर सकते हैं। यदि बच्चा इस दृश्य को छोड़ देता है क्योंकि यह माता-पिता के पास वापस चला जाता है, तो आपका कोड नहीं कहा जाएगा (अभिभावक से स्थानांतरित किए बिना दृश्य पहले ही गायब हो गया था)। लेकिन ओपी द्वारा पूछे गए बैक बटन के ट्रिगर पर केवल हैंडलिंग की घटनाओं के साथ यही मुद्दा है। तो यह उनके प्रश्न का एक सरल उत्तर है।
14

यह सुपर सरल और सुरुचिपूर्ण है। मुझे यह पसंद है। बस एक समस्या: यह भी आग लग जाएगी अगर उपयोगकर्ता वापस जाने के लिए स्वाइप करता है, भले ही वे बीच रास्ते को रद्द कर दें। शायद इस कोड को डालने के लिए एक बेहतर समाधान होगा viewDidDisappear। इस तरह यह केवल आग लग जाएगी जब दृश्य निश्चित रूप से चला गया है।
फोंटेन जुड

1

@William का उत्तर सही है, हालांकि, यदि उपयोगकर्ता स्वाइप-टू-गो-बैक जेस्चर शुरू करता है तो viewWillDisappearविधि को कॉल किया जाता है और यहां तक selfकि नेविगेशन स्टैक में भी नहीं होगा (जो कि self.navigationController.viewControllersशामिल नहीं होगा self), भले ही स्वाइप हो पूरा नहीं हुआ है और व्यू कंट्रोलर वास्तव में पॉप नहीं हुआ है। इस प्रकार, समाधान के लिए किया जाएगा:

  1. में स्वाइप-टू-गो-बैक जेस्चर अक्षम करें viewDidAppearऔर केवल बैक बटन का उपयोग करके अनुमति दें:

    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }
  2. या बस के viewDidDisappearबजाय इस प्रकार उपयोग करें :

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        if (![self.navigationController.viewControllers containsObject:self])
        {
            // back button was pressed or the the swipe-to-go-back gesture was
            // completed. We know this is true because self is no longer
            // in the navigation stack.
        }
    }

0

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

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];

  if ((self.isMovingFromParentViewController || self.isBeingDismissed)
      && !self.isPoppingProgrammatically) {
    // Do your stuff here
  }
}

आपको उस प्रॉपर्टी को अपने कंट्रोलर के साथ जोड़ना होगा और प्रोग्रामेटिक रूप से पॉप करने से पहले इसे YES में सेट करना होगा:

self.isPoppingProgrammatically = YES;
[self.navigationController popViewControllerAnimated:YES];

0

इसे करने का नया तरीका मिला:

उद्देश्य सी

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

तीव्र

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.