আইওএসে নেভিগেশনকন্ট্রোলারে ফিরে বোতাম কলব্যাক


102

আমি নেভিগেশন নিয়ামকের উপরে একটি ভিউ ঠেলে দিয়েছি এবং আমি যখন পিছনের বোতামটি টিপছি তখন এটি স্বয়ংক্রিয়ভাবে পূর্ববর্তী দৃশ্যে যায়। স্ট্যাকটি বন্ধ করে দেওয়ার আগে পপ বোতামটি চাপ দেওয়ার পরে আমি কয়েকটি জিনিস করতে চাই। পিছনের বোতাম কলব্যাক ফাংশন কোনটি?



এটি [সমাধান] [1] যাচাই করুন যা পিছনে বোতামের স্টাইলটিও ধরে রাখে। [1]: stackoverflow.com/a/29943156/3839641
Sarasranglt

উত্তর:


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
অথবা যখন নতুন দৃশ্যে এগিয়ে যান।
গাইব্রাশথ্রিপউড

বাম প্রান্তটি (ইন্টারেক্টিভপপগাস্টারআরকগনাইজার) থেকে ব্যবহারকারী যখন প্যান করেন তখন এটিকেও বলা হয়। আমার ক্ষেত্রে, আমি বিশেষত সন্ধান করছি যখন বাম প্রান্ত থেকে প্যানিং না করার সময় ব্যবহারকারী ফিরে টিপবে।
কাইল Clegg

2
এর অর্থ এই নয় যে পিছনের বোতামটি কারণ ছিল। উদাহরণস্বরূপ একটি অযাচিত অংশ হতে পারে।
হাসিবোট

1
আমার সন্দেহ আছে, কেন আমরা ভিডিডিস্পিয়ারে এটি করব না?
জনভানডিজক

85

আমার মতে সেরা সমাধান।

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

তবে এটি কেবল আইওএস 5 + এর সাথে কাজ করে


3
এই কৌশলটি কোনও ব্যাক বোতামের ট্যাপ এবং একটি আনওয়াইন্ড সেগুনের মধ্যে পার্থক্য করতে পারে না।
হাসিবোট

উইলমোভটোপ্যারেন্টভিউ কনট্রোলার এবং ভিউউইলডিস্পায়ার পদ্ধতিটি ব্যাখ্যা করে না যে নিয়ামকটি অবশ্যই ধ্বংস হয়ে যেতে হবে, মডোটোপ্যারেন্টভিউ কনট্রোলারটি সঠিক
হ্যাঙ্ক

27

ব্যাকবটনটি ওভাররাইড করা আরও ভাল যা ব্যবহারকারীর নিশ্চিতকরণের মতো জিনিসগুলির জন্য দৃশ্যটি পপ করার আগে আপনি ইভেন্টটি পরিচালনা করতে পারবেন ।

দ্যডলিডে একটি ইউআইবারবারটন আইটেম তৈরি করুন এবং এতে সেল-পাসিংয়ের জন্য সেল্ফ-নেভিগেশন আইটেম / লেফট বারবার বাটন সেট করুন

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

}

তারপরে আপনি ক্রিয়াটি নিশ্চিত করার জন্য কোনও ইউআইএলআর্টভিউ বাড়ান, তারপরে ভিউ কন্ট্রোলার পপ করুন ইত্যাদি কাজ করতে পারেন etc.

অথবা একটি নতুন ব্যাকবটন তৈরির পরিবর্তে, পিছনের বোতামটি টিপে গেলে আপনি ইউআইএনএভিগেশন কন্ট্রোলার প্রতিনিধি পদ্ধতিগুলি অনুসরন করতে পারেন।


UINavigationControllerDelegateপদ্ধতি যে, যখন ব্যাক বোতাম চাপড় মেরে হয় বলা হয় নেই।
অর্থ-বিষয়গুলি

এই কৌশলটি নেভিগেশন নিয়ন্ত্রকের পিছনের বোতামটি থেকে দর্শন নিয়ন্ত্রকের ডেটা বৈধকরণ এবং শর্তসাপেক্ষ রিটার্নের অনুমতি দেয়।
gjpc

এই সমাধানটি আইওএস 7+ এর প্রান্তের সোয়াইপ বৈশিষ্ট্যটিকে ভেঙে দেয়
লিরন ইয়াহদাভ

9

এটি এটি সনাক্ত করার সঠিক উপায়।

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

    }
}

যখন ভিউটিও ধাক্কা দেওয়া হয় তখন এই পদ্ধতিটি বলা হয়। সুতরাং প্যারেন্ট == নিল পরীক্ষা করা স্ট্যাক থেকে ভিউ কন্ট্রোলারের পপিংয়ের জন্য


9

আমি এই সমাধান দিয়ে শেষ। আমরা পিছনে বোতাম টিপ করার সাথে সাথে ডায়িডিস্পিয়ার পদ্ধতিটি কল করা হয়। আমরা ইসমুভিংফ্র্যাম্পেন্টভিউ কনট্রোলার নির্বাচককে কল করে যাচাই করে দেখতে পারি true আমরা ডেটা ফিরিয়ে দিতে পারি (ডেলিগেট ব্যবহার করে) someoneএকে সাহায্য করুন help

-(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]
SamB

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
    }
}

আপনি আমার ডেমোটি এখানে দেখতে পারেন ।

এখানে চিত্র বর্ণনা লিখুন


এটি একটি দুর্দান্ত সমাধান এবং একটি ব্লগপোস্টে বেক করা উচিত! আমি এই মুহূর্তে যা অনুসন্ধান করছি তার জন্য অতিরিক্ত দক্ষ হওয়া বলে মনে হচ্ছে, তবে অন্যান্য পরিস্থিতিতে এটি চেষ্টা করার মতো উপযুক্ত।
এ্যাসিগির

6

"ভিউটি প্যাকিংয়ের আগে স্ট্যাকটি ছড়িয়ে দেওয়ার আগে":

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

5

ভিউকন্ট্রোলারদের জিজ্ঞাসা করার চেয়ে আরও উপযুক্ত উপায় রয়েছে। আপনি নিজের কন্ট্রোলারটিকে নেভিগেশনবারের একটি প্রতিনিধি বানাতে পারেন যার পিছনে বোতাম রয়েছে। এখানে একটি উদাহরণ। আপনি যে কন্ট্রোলারটির পিছনে বোতামটি টিপতে চান তা বাস্তবায়নের ক্ষেত্রে এটি বলুন যে এটি ইউআইএনএভিগেশনবারডেলিগেট প্রোটোকলটি প্রয়োগ করবে:

@interface MyViewController () <UINavigationBarDelegate>

তারপরে কোথাও আপনার প্রারম্ভিককরণের কোডে (সম্ভবত ভিউডিডলডে) আপনার নিয়ামককে এটির নেভিগেশন বারের প্রতিনিধি বানান:

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
আমার জন্য কাজ করেননি ... ক্ষুধা কারণ এটি হাতা। "*** অপ্রত্যাশিত ব্যতিক্রম 'এনএসআইটার্নাল ইনসোনসিটিসিটি এক্সপেশন' এর কারণে অ্যাপ্লিকেশনটি সমাপ্ত করা হচ্ছে, কারণ: 'একটি নিয়ামক দ্বারা পরিচালিত ইউআইএনএভিগেশন বারে ম্যানুয়ালি প্রতিনিধি সেট করতে পারবেন না'"
ডায়নামিকডান

এটি দুর্ভাগ্যক্রমে কোনও ইউআইএনএভিগেশন কন্ট্রোলারের সাথে কাজ করবে না, পরিবর্তে আপনার এটিতে একটি ইউআইএনএভিগেশনবার সহ একটি স্ট্যান্ডার্ড ইউআইভিউকন্ট্রোলার প্রয়োজন। এর অর্থ এই যে আপনি নেভিগেশনকন্ট্রোলার আপনাকে যে কয়েকটি স্বয়ংক্রিয় ভিউ কন্ট্রোলার চাপ দিচ্ছেন এবং পপিংয়ের সুবিধা নিতে পারবেন না। দুঃখিত!
কার্লোস গুজম্যান

আমি স্রেফ নেভিগেশনবারকন্ট্রোলারের পরিবর্তে ইউআইএনএভিগেশন বারটি ব্যবহার করেছি এবং তারপরে এটি ঠিকঠাক কাজ করে। আমি জানি প্রশ্নটি নেভিগেশনবারকন্ট্রোলার সম্পর্কিত, তবে এই সমাধানটি হাতাশূন্য।
এপ্লিকেশন করা হয়েছে

3

আপনি যদি "ভিউউইলডিস্পিয়ার" বা অনুরূপ পদ্ধতি ব্যবহার করতে না পারেন তবে ইউআইএনএভিগেশন কন্ট্রোলার সাবক্লাস করার চেষ্টা করুন। এটি শিরোনাম শ্রেণি:

#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

এখানে আমি অন্যভাবে প্রয়োগ করেছি (এটি একটি অনিচ্ছাকৃত সেগুনের সাথে পরীক্ষা করে দেখেনি তবে এটি সম্ভবত পৃথক হবে না, যেমন অন্যরা এই পৃষ্ঠার অন্যান্য সমাধানের ক্ষেত্রে উল্লেখ করেছেন) সন্তানের ভিসি ধাক্কা দেওয়ার আগে পিতামাতার দৃষ্টিভঙ্গী নিয়ন্ত্রণ করতে পারে ভিউ স্ট্যাকটি পপ আপ হয়ে যায় (আমি এটি মূল ইউআইএনএভিগেশন কন্ট্রোলার থেকে কয়েক স্তরে ব্যবহার করেছি)। চাইল্ডভিসি খুব চাপ দেওয়ার আগেও এটি ক্রিয়া সম্পাদন করতে ব্যবহৃত হতে পারে। এটিতে কাস্টম UIBarButtonItem বা UIButton তৈরি না করে আইওএস সিস্টেমের ব্যাক বোতামের সাথে কাজ করার অতিরিক্ত সুবিধা রয়েছে।

  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:

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.