iOS Pass data back from viewController2 to viewCon

2019-02-28 22:37发布

问题:

I have viewController1 that makes a modal segue to my viewController2 but the

viewController2 is embebed on a navigation controller

because I need the navigation bar there.

I've implemented a protocol to send the data back from viewController2 to viewController1 but it doesn't function. Here is my code:

protocol writeValueBackDelegate {
    func writeValueBack(value: String)
}

class viewController1: UITableViewController, writeValueBackDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "SelectAddress"{
            if let dest = segue.destinationViewController as? MapAddressViewController{
                dest.delegate = self
            }
        }

    }
}

On the viewController2 I have this:

class viewController2: UIViewController{
    var delegate: writeValueBackDelegate?

@IBAction func TaskOneClick(sender: AnyObject) {
        delegate?.writeValueBack(txtAddress!.text!)
        self.navigationController?.popViewControllerAnimated(true)
    }
}

I dont know why but it functions only if I remove the navigation controller from my secondviewController and make the segue from viewController 1 to viewController 2 directly, but I need the navigation controller to show the navigation bar.

Do you know why it is happening? or Why I'm doing bad.

回答1:

Here is my understanding of your setup.

ViewController1 -> NavigationController -> ViewController2

In this case, in the prepare for segue method, the destination viewcontroller is the Navigation Controller and Not ViewController2. Therefore, this line of code will not be true.

if let dest = segue.destinationViewController as? MapAddressViewController{
            dest.delegate = self
        }

The downcast you have there will fail, because the destination VC is not MapAddressViewContoller,instead its a UINavigation controller.

To fix this, you can change the code as follows :

if let dest = segue.destinationViewController as? UINavigationController{
            dest.rootViewController.delegate = self
        }

However, I prefer using NSNotification to pass data back down the view controller hierarchy. You could try that out also.

Using Notifications :

  • Step 1 : In VC1 : Register for a notification. override func viewDidLoad() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("myFunc:"), name: "SomeName", object: nil) } func myFunc(theNotifiction : NSNotification){ print("I got notified (theNotifiction.userInfo!)") }
    • Step 2 : In VC2 : Post a Notification at an appropriate time. NSNotificationCenter.defaultCenter().postNotificationName("SomeName", object: nil, userInfo: ["theKey":"theData"])


回答2:

Here is the way doing this through NSNotification and CustomDelegate

Let's see the Passing Data from ViewController2 to ViewController1 Using NSNotification first.

in ViewControllerB

.h

#import <UIKit/UIKit.h>
@interface ViewControllerB : UIViewController  
@property (nonatomic, strong) IBOutlet UITextField *txtAddress;
- (IBAction)TaskOneClick:(id)sender;
@end

.m

#import "ViewControllerB.h"
@interface ViewControllerB ()
@end
@implementation ViewControllerB

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
   self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
   if (self) {
    // Custom initialization
   }
   return self;
}

- (void)viewDidLoad
{
     [super viewDidLoad];
     // Do any additional setup after loading the view.
}

//Using NSNotification 
- (IBAction)TaskOneClick:(id)sender;
{
          //Use NSNotificationCenter
     [[NSNotificationCenter defaultCenter] postNotificationName:@"passingDataFromSecondViewToFirstView" object:self.txtAddress.text];
     [self.navigationController popViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning
{
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
}

in ViewControllerA

.h

#import <UIKit/UIKit.h>
#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController<ViewControllerBDelegate>
@property (nonatomic, strong) IBOutlet UILabel *labelGetData; 
- (IBAction)gotoNextView:(id)sender;
@end

.m

#import "ViewControllerA.h"
@interface ViewControllerA ()
@end
@implementation ViewControllerA
- (void)viewDidLoad
{
   [super viewDidLoad];
   //addObserver here...
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFromPreviousViewControllerNotificationReceived:) name:@"passingDataFromSecondViewToFirstView" object:nil];
   // Do any additional setup after loading the view, typically from a nib.
}

//addObserver Method here....
- (void)textFromPreviousViewControllerNotificationReceived:(NSNotification *)notification
{
   // set text to label...
   NSString *string = [notification object];
   self.labelGetData.text = string;
}
- (IBAction)gotoNextView:(id)sender;
{
   //If you use storyboard
   ViewControllerB *viewControllerB = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerB"];
   //OR  If you use XIB
   ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNibName:@"ViewControllerB" bundle:nil];
   [self.navigationController pushViewController:viewControllerB animated:YES];
}
- (void)didReceiveMemoryWarning
{
   [super didReceiveMemoryWarning];
   // Dispose of any resources that can be recreated.
 }

First we navigate the view from ViewControllerA to ViewControllerB.

After that we send the data from ViewControllerB to ViewControllerA.

In above coding we use the NSNotification.

So that, you need to set the postNotificationName for getting data in button action method in ViewControllerB.

Next you need to write addObserver in (sending data to your required View Controller) ViewControllerA and call the addObserver method in same View Controller.

Let's see the Passing Data from ViewController2 to ViewController1 Using CustomDelegate.

 in ViewControllerB

 .h


#import <UIKit/UIKit.h>
@class ViewControllerB;
@protocol ViewControllerBDelegate <NSObject>
- (void)viewControllerB:(ViewControllerB *)viewControllerB didEnterText:(NSString *)text;
@end
@interface ViewControllerB : UIViewController
@property (nonatomic, assign)id<ViewControllerBDelegate> delegate;  
@property (nonatomic, strong) IBOutlet UITextField *txtAddress;
- (IBAction)TaskOneClick:(id)sender;
@end

.m

#import "ViewControllerB.h"
@interface ViewControllerB ()
@end
@implementation ViewControllerB

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
   self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
   if (self) {
    // Custom initialization
   }
   return self;
}

- (void)viewDidLoad
{
     [super viewDidLoad];
     // Do any additional setup after loading the view.
}

//Using Custom Delegate
- (IBAction)TaskOneClick:(id)sender;
{
     [self.delegate viewControllerB:self didEnterText:self.txtAddress.text];
     [self.navigationController popViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning
{
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
}

in ViewController

.h

#import <UIKit/UIKit.h>
#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController<ViewControllerBDelegate>
@property (nonatomic, strong) IBOutlet UILabel *labelGetData; 
- (IBAction)gotoNextView:(id)sender;
@end

.m

#import "ViewControllerA.h"
@interface ViewControllerA ()
@end
@implementation ViewControllerA
- (void)viewDidLoad
{
   [super viewDidLoad];
   // Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)gotoNextView:(id)sender;
{
   //If you use storyboard
   ViewControllerB *viewControllerB = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerB"];
   //OR  If you use XIB
   ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNibName:@"ViewControllerB" bundle:nil];
   viewControllerB.delegate = self;
   [self.navigationController pushViewController:viewControllerB animated:YES];
}

//Calling custom delegate method
- (void)viewControllerB:(ViewControllerB *)ViewControllerB didEnterText:(NSString *)text
{
   self.labelGetData.text = text; //Getting the data and assign the data to label here.
}

- (void)didReceiveMemoryWarning
{
   [super didReceiveMemoryWarning];
   // Dispose of any resources that can be recreated.
 }

In above coding we use the CustomProtocolDelegate Method.

First we need to Set and Assign the delegate in ViewControllerB.h

Very importantly we have to set the Custom Delegate Method in the ViewControllerB because where we send the data to ViewControllerA once we click the action button in ViewControllerB.

Finally we must call the Custom Delegate Method in View ControllerA, where we get the data and assign that data to label.Now you can see the passed data(From ViewControllerB to ViewControllerA) using custom delegate.

Likewise you can send the data to other view controller using Custom Delegate Methods or NSNotificationCenter