[ios] Passage de données entre les contrôleurs de vue



Answers

Rapide

Il y a des tonnes et des tonnes d'explications ici et autour de StackOverflow, mais si vous êtes un débutant essayant juste de faire quelque chose de basique, essayez de regarder ce tutoriel sur YouTube (c'est ce qui m'a aidé à comprendre).

Passage des données au prochain View Controller

Ce qui suit est un exemple basé sur la vidéo. L'idée est de passer une chaîne du champ de texte du First View Controller à l'étiquette du second View View.

Créez la disposition du storyboard dans l'interface Builder. Pour faire le segue, il suffit de cliquer sur le bouton Control et de glisser vers le second View View.

Premier contrôleur de vue

Le code du First View Controller est

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController

        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

Deuxième contrôleur de vue

Et le code pour le second View View est

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

Ne pas oublier

  • UITextField les prises pour UITextField et UILabel .
  • Définissez les premier et deuxième contrôleurs de vue sur les fichiers Swift appropriés dans IB.

Renvoyer les données au contrôleur de vue précédent

Pour renvoyer des données du second contrôleur de vue au premier contrôleur de vue, vous utilisez un protocole et un délégué . Cette vidéo est une marche très claire cependant de ce processus:

Ce qui suit est un exemple basé sur la vidéo (avec quelques modifications).

Créez la disposition du storyboard dans l'interface Builder. Encore une fois, pour faire le segue, il vous suffit de faire glisser le bouton depuis le bouton jusqu'au second contrôleur de vue. Définissez l'identificateur de segue sur showSecondViewController . Aussi, n'oubliez pas de brancher les prises et les actions en utilisant les noms dans le code suivant.

Premier contrôleur de vue

Le code du First View Controller est

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

Notez l'utilisation de notre protocole DataEnteredDelegate personnalisé.

Deuxième contrôleur de vue et protocole

Le code du second contrôleur de vue est

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: class {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // making this a weak variable so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

Notez que le protocol est en dehors de la classe View Controller.

C'est tout. En cours d'exécution de l'application, vous devriez être en mesure de renvoyer les données du second contrôleur de vue au premier.

Question

Je suis nouveau sur iOS et Objective-C et tout le paradigme MVC et je suis coincé avec ce qui suit:

J'ai une vue qui agit comme un formulaire de saisie de données et je veux donner à l'utilisateur la possibilité de sélectionner plusieurs produits. Les produits sont répertoriés dans une autre vue avec un UITableViewController et j'ai activé plusieurs sélections.

Ma question est, comment puis-je transférer les données d'une vue à l'autre? Je vais conserver les sélections sur UITableView dans un tableau, mais comment puis-je le renvoyer à la vue précédente du formulaire d'entrée de données afin qu'il puisse être sauvegardé avec les autres données dans Core Data à la soumission du formulaire?

J'ai surfé autour et j'ai vu certaines personnes déclarer un tableau dans le délégué de l'application. J'ai lu quelque chose à propos de Singletons mais je ne comprends pas ce que c'est et j'ai lu quelque chose sur la création d'un modèle de données.

Quelle serait la bonne façon de faire cela et comment j'y arriverais?




Il existe plusieurs façons par lesquelles une donnée peut être reçue dans une classe différente dans iOS. Par exemple -

  1. Initialisation directe après l'attribution d'une autre classe.
  2. Délégation - pour renvoyer des données
  3. Notification - pour diffuser des données à plusieurs classes en une seule fois
  4. Sauvegarde dans NSUserDefaults - pour y accéder plus tard
  5. Cours Singleton
  6. Bases de données et autres mécanismes de stockage comme plist, etc.

Mais pour le scénario simple de transmission d'une valeur à une classe différente dont l'allocation est effectuée dans la classe en cours, la méthode la plus courante et préférée serait la définition directe des valeurs après allocation. Cela se fait comme suit:-

Nous pouvons le comprendre en utilisant deux contrôleurs - Controller1 et Controller2

Supposons que dans la classe Controller1 vous voulez créer l'objet Controller2 et le pousser avec une valeur String en cours de transmission. Cela peut être fait comme ceci: -

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

Dans l'implémentation de la classe Controller2, il y aura cette fonction

@interface Controller2  : NSObject

@property (nonatomic , strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; //or self.stringPassed = value
}

@end

Vous pouvez également définir directement les propriétés de la classe Controller2 de la même manière:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];  
    [self pushViewController:obj animated:YES];
}

Pour passer plusieurs valeurs, vous pouvez utiliser les paramètres multiples comme:

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date]; 

Ou si vous devez transmettre plus de 3 paramètres liés à une fonctionnalité commune, vous pouvez stocker les valeurs dans une classe Model et transmettre ce modelObject à la classe suivante

ModelClass *modelObject = [[ModelClass alloc] init]; 
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

Donc, si vous voulez -

1) set the private variables of the second class initialise the values by calling a custom function and passing the values.
2) setProperties do it by directlyInitialising it using the setter method.
3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process.

J'espère que cela t'aides




Il existe plusieurs méthodes pour partager des données.

  1. Vous pouvez toujours partager des données à l'aide de NSUserDefaults . Définissez la valeur que vous souhaitez partager par rapport à une clé de votre choix et obtenez la valeur de NSUserDefault associée à cette clé dans le contrôleur de vue suivant.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. Vous pouvez simplement créer une propriété dans viewcontrollerA . Créez un objet de viewcontrollerA dans viewcontrollerB et affectez la valeur désirée à cette propriété.

  3. Vous pouvez également créer des délégués personnalisés pour cela.




This is a really great tutorial for anyone that wants one. Here is the example code:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"myIdentifer]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        myViewController *destViewController = segue.destinationViewController;
        destViewController.name = [object objectAtIndex:indexPath.row];
    }
}



In my case I used a singleton class which can work as a global object allowing accesses to the data from almost everywhere in the app. First thing is to build a singleton class. Please refer to the page," What should my Objective-C singleton look like? " And what I did to make the object globally accessible was simply import it in appName_Prefix.pch which is for applying import statement in every classes. To access this object and to use, I simply implemented class method to return the shared instance, which contains its own variables




I have seen a lot of people over complicating this using the didSelectRowAtPath method. I am using Core Data in my example.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    //this solution is for using Core Data
    YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];

    YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"];//make sure in storyboards you give your second VC an identifier

    //Make sure you declare your value in the second view controller
    details.selectedValue = value;

    //Now that you have said to pass value all you need to do is change views
    [self.navigationController pushViewController: details animated:YES];

}

4 lines of code inside the method and you are done.




I know this is a beaten subject but for those looking to answer this question with a SWIFT slant and want a bare-bones example, here my go-to method for passing data if you are using a segue to get around.

It is similar to the above but without the buttons, labels and such. Just simply passing data from one view to the next.

Setup The Storyboard

There are three parts.

  1. The Sender
  2. The Segue
  3. The Receiver

This is a very simple view layout with a segue between them.

Here is the setup for the sender

Here is the setup for the receiver.

Lastly, the setup for the segue.

The View Controllers

We are keeping this simple so no buttons, not actions, we are simply moving data from the sender to the receiver when the application loads and then outputting the transmitted value to the console.

This page takes the initially loaded value and passes it along.

//
//  ViewControllerSender.swift
//  PassDataBetweenViews
//
//  Created by Chris Cantley on 8/25/15.
//  Copyright (c) 2015 Chris Cantley. All rights reserved.
//

import UIKit


class ViewControllerSender: UIViewController {

    // THE STUFF - put some info into a variable
    let favoriteMovie = "Ghost Busters"


    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the recieving view controller.
        self.performSegueWithIdentifier("goToReciever", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        //GET REFERENCE - ...to the receiver view.
        var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver

        //PASS STUFF - pass the variable along to the target.
        viewControllerReceiver!.yourFavMovie = self.favoriteMovie

    }

}

This page just sends the value of the variable to the console when it loads. By this point our favorite movie should be in that variable.

//
//  ViewControllerReceiver.swift
//  PassDataBetweenViews
//
//  Created by Chris Cantley on 8/25/15.
//  Copyright (c) 2015 Chris Cantley. All rights reserved.
//

import UIKit

class ViewControllerReceiver: UIViewController {

    //Basic empty variable waiting for you to pass in your fantastic favorite movie.
    var yourFavMovie = ""

    override func viewDidLoad() {
        super.viewDidLoad()


        //And now we can view it in the console.
        println("The Movie is \(self.yourFavMovie)")

    }



}

That is how you can tackle it if you want to use a segue and you don't have your pages under a navigation controller.

Once it is run it should switch to the receiver view automatically and pass the value from the sender to the receiver, displaying the value in the console.




If you want to send data from one to another viewController, here's a way to it:

Say we have viewControllers: viewControllerA and viewControllerB

Now in viewControllerB.h

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;

In viewControllerB.m

#import "viewControllerB.h"

@implementation viewControllerB

- (id)initWithArray:(NSArray)a andString:(NSString)s {

   array = [[NSArray alloc] init];
   array = a;

   string = [[NSString alloc] init];
   string = s;

}

In viewControllerA.m

#import "viewControllerA.h"
#import "viewControllerB.h"

@implementation viewControllerA

- (void)someMethod {

  someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
  someString = [NSString stringWithFormat:@"Hahahahaha"];

  viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];

  [self.navigationController pushViewController:vc animated:YES];
  [vc release];

}

So this is how you can pass data from viewControllerA to viewControllerB without setting any delegate. ;)




Delegation is the only one solution to perform such operations when you are using .xib files however all answers described above are for storyboard for .xibs files you need to use delegation. that's only solution you can.

Another solution is use singleton class pattern initialize it once and use it in your entire app.




If you want to send data from one to another viewController, here's a way to it:

Say we have viewControllers: ViewController and NewViewController.

in ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
}

@property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;

-(IBAction)goToNextScreen:(id)sender;

@end

in ViewController.m

#import "ViewController.h"

#import "NewViewController.h"

@implementation ViewController
@synthesize mytext1,mytext2,mytext3,mytext4;

-(IBAction)goToNextScreen:(id)sender
{
    NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil];


    NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];

    newVc.arrayList = arr;

    [self.navigationController pushViewController:newVc animated:YES];

}

In NewViewController.h

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

    NSString *name,*age,*dob,*mobile;

}

@property(nonatomic, retain)NSArray *arrayList;

@end

In NewViewController.m

#import "NewViewController.h"

#import "ViewController.h"

@implementation NewViewController
@synthesize arrayList;

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    // Return the number of rows in the section.
    return [arrayList count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];      
    }
    // Configure the cell...
    cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
    return cell;


}

@end

So this way we can pass the data from one viewcontroller to another view controller...




Je trouve la version la plus simple et la plus élégante avec des blocs qui passent. Nommez le contrôleur de vue qui attend les données renvoyées comme "A" et renvoyez le contrôleur de vue comme "B". Dans cet exemple, nous voulons obtenir 2 valeurs: d'abord Type1 et seconde de Type2.

En supposant que nous utilisions Storyboard, le premier contrôleur règle le bloc de rappel, par exemple pendant la préparation du segue:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

et le contrôleur de vue "B" doit déclarer la propriété de rappel, BViewController.h:

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

Que dans le fichier d'implémentation BViewController.m après que nous ayons les valeurs désirées pour retourner notre callback devrait être appelé:

if (self.callback)
    self.callback(value1, value2);

Une chose à retenir est que l'utilisation du bloc doit souvent gérer des références fortes et faibles comme expliqué here




The OP didn't mention view controllers but so many of the answers do, that I wanted to chime in with what some of the new features of the LLVM allow to make this easier when wanting to pass data from one view controller to another and then getting some results back.

Storyboard segues, ARC and LLVM blocks make this easier than ever for me. Some answers above mentioned storyboards and segues already but still relied on delegation. Defining delegates certainly works but some people may find it easier to pass pointers or code blocks.

With UINavigators and segues, there are easy ways of passing information to the subservient controller and getting the information back. ARC makes passing pointers to things derived from NSObjects simple so if you want the subservient controller to add/change/modify some data for you, pass it a pointer to a mutable instance. Blocks make passing actions easy so if you want the subservient controller to invoke an action on your higher level controller, pass it a block. You define the block to accept any number of arguments that makes sense to you. You can also design the API to use multiple blocks if that suits things better.

Here are two trivial examples of the segue glue. The first is straightforward showing one parameter passed for input, the second for output.

// Prepare the destination view controller by passing it the input we want it to work on
// and the results we will look at when the user has navigated back to this controller's view.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results
     // by virtue of both controllers having a pointer to the same object.
     andResults:self.resultsFromNextController];
}

This second example shows passing a callback block for the second argument. I like using blocks because it keeps the relevant details close together in the source - the higher level source.

// Prepare the destination view controller by passing it the input we want it to work on
// and the callback when it has done its work.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results.
     resultsBlock:^(id results) {
         // This callback could be as involved as you like.
         // It can use Grand Central Dispatch to have work done on another thread for example.
        [self setResultsFromNextController:results];
    }];
}



You can save data in App delegate to access it across view controllers in your application. All you have to do is create a shared instance of app delegate

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

For Example

if you declare a NSArray object *arrayXYZ then you can access it in any view controller by appDelegate.arrayXYZ




Passing Data between FirstViewController to SecondViewController as below

Par exemple:

FirstViewController String value as

StrFirstValue = @"first";

so we can pass this value in second class using below step

1>We need to crate string object in SecondViewController.h file

NSString *strValue;

2>Need to declare property as below below declaration in .h file

@property (strong, nonatomic)  NSString *strSecondValue;

3>Need synthesize that value in FirstViewController.m file below header declaration

@synthesize strValue;

and in FirstViewController.h :

@property (strong, nonatomic)  NSString *strValue;

4>In FirstViewController,From which method we navigate to second view please write below code in that method.

SecondViewController *secondView= [[SecondViewController alloc]     
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];

[secondView setStrSecondValue:StrFirstValue];

[self.navigationController pushViewController:secondView animated:YES ];



I was searching this solution for long time, Atlast I found it. First of all declare all the objects in your SecondViewController.h file like

@interface SecondViewController: UIviewController 
{
    NSMutableArray *myAray;
    CustomObject *object;
}

Now in your implementation file allocate the memory for those objects like this

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

Now you have allocated the memory for Array and object. now you can fill that memory before pushing this ViewController

Go to your SecondViewController.h and write two methods

-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;

in implementation file you can implement the function

-(void)setMyArray:(NSArray *)_myArray
{
     [myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
     [object setCustomObject:_myObject];
}

expecting that your CustomObject must have a setter function with it.

now your basic work is done. go to the place where you want to push the SecondViewController and do the following stuff

SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];

Take care for spelling mistakes.




Links