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




objective-c oop model-view-controller uiviewcontroller (25)

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?


Answers

Il y a de bonnes informations dans la plupart des réponses données, mais aucune n'aborde complètement la question.

La question porte sur le transfert d'informations entre les contrôleurs de vue. L'exemple spécifique donné demande de transmettre des informations entre les vues, mais compte tenu de la nouveauté déclarée à iOS, l'affiche originale signifiait probablement entre viewControllers, pas entre les vues (sans aucune implication des ViewControllers). Il semble que toutes les réponses se concentrent sur deux contrôleurs de vue, mais que se passe-t-il si l'application évolue pour impliquer plus de deux contrôleurs de vue dans l'échange d'informations?

L'affiche originale a également posé des questions sur Singletons et l'utilisation de l' AppDelegate . Ces questions doivent être répondues.

Pour aider quelqu'un d'autre à regarder cette question, qui veut une réponse complète, je vais essayer de la fournir.

Scénarios d'application

Plutôt que d'avoir une discussion très hypothétique et abstraite, cela aide à avoir des applications concrètes à l'esprit. Pour aider à définir une situation de contrôleur à deux vues et une situation de contrôleur à plus de deux vues, je vais définir deux scénarios d'application concrets.

Scénario un: deux contrôleurs de vue maximum ont besoin de partager des informations. Voir le diagramme un.

Il y a deux contrôleurs de vue dans l'application. Il y a un ViewControllerA (formulaire de saisie de données) et View Controller B (liste de produits). Les éléments sélectionnés dans la liste de produits doivent correspondre aux éléments affichés dans la zone de texte du formulaire de saisie de données. Dans ce scénario, ViewControllerA et ViewControllerB doivent communiquer directement entre eux et aucun autre contrôleur de vue.

Scénario deux : plus de deux contrôleurs de vue doivent partager la même information. Voir le diagramme deux.

Il y a quatre contrôleurs de vue dans l'application. Il s'agit d'une application basée sur des onglets pour la gestion de l'inventaire de la maison. Trois contrôleurs de vue présentent des vues filtrées différemment des mêmes données:

  • ViewControllerA - Articles de luxe
  • ViewControllerB - Articles non-assurés
  • ViewControllerC - Inventaire complet de la maison
  • ViewControllerD - Ajouter un nouveau formulaire d'article

Chaque fois qu'un élément individuel est créé ou modifié, il doit également se synchroniser avec les autres contrôleurs de vue. Par exemple, si nous ajoutons un bateau dans ViewControllerD, mais qu'il n'est pas encore assuré, alors le bateau doit apparaître quand l'utilisateur va à ViewControllerA (Luxury Items), et aussi ViewControllerC (Entire Home Inventory), mais pas quand l'utilisateur va à ViewControllerB (Articles non assurés). Nous devons non seulement ajouter de nouveaux éléments, mais aussi supprimer des éléments (qui peuvent être autorisés à partir de l'un des quatre contrôleurs de vue) ou modifier des éléments existants (qui peuvent être autorisés à partir du "Ajouter un nouveau formulaire"). pour l'édition).

Étant donné que tous les contrôleurs de vue doivent partager les mêmes données, les quatre contrôleurs de vue doivent rester synchronisés et il doit donc y avoir une sorte de communication avec tous les autres contrôleurs de vue lorsqu'un contrôleur de vue unique modifie les données sous-jacentes. Il devrait être assez évident que nous ne voulons pas que chaque contrôleur de vue communique directement avec chaque autre contrôleur de vue dans ce scénario. Dans le cas où ce n'est pas évident, considérons si nous avions 20 contrôleurs de vue différents (plutôt que seulement 4). Dans quelle mesure est-il difficile et sujette à erreur d'aviser chacun des 19 autres contrôleurs de vue à chaque fois qu'un contrôleur de vue effectue un changement?

Les solutions: les délégués et le modèle d'observateur, et les singletons

Dans le scénario un, nous avons plusieurs solutions viables, comme d'autres réponses ont donné

  • segues
  • délégués
  • définir les propriétés directement sur les contrôleurs de vue
  • NSUserDefaults (en fait un mauvais choix)

Dans le deuxième scénario, nous avons d'autres solutions viables:

  • Modèle d'observateur
  • Singletons

Un singleton est une instance d'une classe, cette instance étant la seule instance existant au cours de sa vie. Un singleton tire son nom du fait qu'il s'agit de l'instance unique. Normalement, les développeurs qui utilisent des singletons ont des méthodes de classe spéciales pour y accéder.

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed once in the
    // lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

Maintenant que nous comprenons ce qu'est un singleton, discutons comment un singleton s'intègre dans le modèle d'observateur. Le modèle d'observateur est utilisé pour qu'un objet réponde aux changements d'un autre objet. Dans le second scénario, nous avons quatre contrôleurs de vue différents, qui veulent tous savoir à propos des changements apportés aux données sous-jacentes. Les "données sous-jacentes" devraient appartenir à une instance unique, un singleton. Le "savoir sur les changements" est accompli en observant les changements apportés au singleton.

L'application d'inventaire de maison aurait une instance unique d'une classe qui est conçue pour gérer une liste d'éléments d'inventaire. Le gestionnaire gérerait une collection d'articles ménagers. Voici une définition de classe pour le gestionnaire de données:

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

Lorsque la collection d'éléments de l'inventaire de la maison change, les contrôleurs de vue doivent être mis au courant de cette modification. La définition de classe ci-dessus ne rend pas évident comment cela va se passer. Nous devons suivre le modèle d'observateur. Les contrôleurs de vue doivent observer formellement le sharedManager. Il y a deux façons d'observer un autre objet:

  • Key-Value-Observation (KVO)
  • NSNotificationCenter.

Dans le scénario deux, nous n'avons pas une seule propriété du HouseholdInventoryManager qui pourrait être observée en utilisant KVO. Parce que nous n'avons pas une seule propriété qui est facilement observable, le modèle d'observateur, dans ce cas, doit être implémenté en utilisant NSNotificationCenter. Chacun des quatre contrôleurs de vue s'abonnait à des notifications et le gestionnaire partagé envoyait des notifications au centre de notification, le cas échéant. Le gestionnaire d'inventaire n'a pas besoin de savoir quoi que ce soit à propos des contrôleurs de vue ou des instances d'autres classes qui pourraient être intéressées à savoir quand la collection d'éléments d'inventaire change; NSNotificationCenter prend en charge ces détails d'implémentation. Les contrôleurs de vue s'abonnent simplement aux notifications, et le gestionnaire de données publie simplement des notifications.

Beaucoup de programmeurs débutants profitent du fait qu'il y a toujours exactement un délégué d'application dans la vie de l'application, qui est globalement accessible. Les programmeurs débutants utilisent ce fait pour placer des objets et des fonctionnalités dans l'appDelegate pour faciliter l'accès depuis n'importe quel endroit de l'application. Le fait que AppDelegate soit un singleton ne signifie pas qu'il doit remplacer tous les autres singletons. Ceci est une mauvaise pratique car elle place trop de charge sur une classe, brisant de bonnes pratiques orientées objet. Chaque classe devrait avoir un rôle clair qui est facilement expliqué, souvent juste par le nom de la classe.

Chaque fois que votre délégué aux applications commence à être gonflé, commencez à supprimer la fonctionnalité en singletons. Par exemple, la pile de données principale ne doit pas être laissée dans AppDelegate, mais doit plutôt être placée dans sa propre classe, une classe coreDataManager.

Les références


There are tons of ways to do this and it's important to pick the right one. Probably one of the biggest architectural decisions lies on how the model code will be shared or accessed throughout the app.

I wrote a blog post about this a while back: Sharing Model Code . Here's a brief summary:

Shared data

One approach is to share pointers to the model objects between view controllers.

  • Brute force iteration on view controllers (in Navigation or Tab Bar Controller) to set the data
  • Set data in prepareForSegue (if storyboards) or init (if programmatic)

Since prepare for segue is the most common here is an example:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var next = segue.destinationViewController as NextViewController
    next.dataSource = dataSource
}

Independent access

Another approach is to handle a screen full of data at a time and instead of coupling the view controllers to each other couple each view controller to single data source that they can get to independently.

The most common way I've seen this done is a singleton instance. So if your singleton object was DataAccess you could do the following in the viewDidLoad method of UIViewController:

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}

There are addition tools that also help pass along data:

  • Key-Value Observing
  • NSNotification
  • Core Data
  • NSFetchedResultsController
  • La source de données

Core Data

The nice thing about Core Data is that it has inverse relationships. So if you want to just give a NotesViewController the notes object you can because it'll have an inverse relationship to something else like the notebook. If you need data on the notebook in the NotesViewController you can walk back up the object graph by doing the following:

let notebookName = note.notebook.name

Read more about this in my blog post: Sharing Model Code


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. ;)


NewsViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  [tbl_View deselectRowAtIndexPath:indexPath animated:YES];
  News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
  NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];

  newsDetailView.newsHeadlineStr = newsObj.newsHeadline;

  [self.navigationController pushViewController:newsDetailView animated:YES];
}

NewsDetailViewController.h

@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end

NewsDetailViewController.m

@synthesize newsHeadlineStr;

Rapide

Il y a des tonnes et des tonnes d'explications ici et autour de , 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.


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.


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

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


Transférer des données de ViewController 2 (destination) à viewController 1 (Source) est la chose la plus intéressante. En supposant que vous utilisez storyboard, ce sont toutes les façons que j'ai découvertes:

  • Déléguer
  • Notification
  • Valeurs par défaut de l'utilisateur
  • Singleton

Ceux-ci ont déjà été discutés ici.

J'ai trouvé il y a plus de façons:

-En utilisant les rappels de bloc:

l'utiliser dans la méthode prepareForSegue dans le VC1

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
    self.blockLabel.text = destination.blockTextField.text;
}];

-Utiliser les storyboards Dérouler (Quitter)

Implémentez une méthode avec un argument UIStoryboardSegue dans VC 1, comme celui-ci:

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

Dans le storyBoard, accrochez le bouton "return" au bouton de sortie vert (Unwind) du vc. Vous avez maintenant un segment qui "remonte", donc vous pouvez utiliser la propriété destinationViewController dans prepareForSegue de VC2 et changer n'importe quelle propriété de VC1 avant de revenir en arrière.

  • Une autre option d'utilisation de storyboards Undwind (Exit) - vous pouvez utiliser la méthode que vous avez écrite dans VC1

    -(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
        NextViewController *nextViewController = segue.sourceViewController;
        self.unwindLabel.text = nextViewController.unwindPropertyPass;
    } 
    

    Et dans le prepareForSegue de VC1 vous pouvez changer n'importe quelle propriété que vous voulez partager.

Dans les deux options de déroulement, vous pouvez définir la propriété tag du bouton et la vérifier dans prepareForSegue.

J'espère avoir ajouté quelque chose à la discussion.

:) À votre santé.


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


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


Après plus de recherches, il a semblé que les Protocoles et les Délégués étaient la bonne façon de faire cela.

J'ai fini par utiliser cet exemple

Partage de données entre les contrôleurs de vue et d'autres objets @ iPhone SDK

Travaillé bien et m'a permis de passer une chaîne et un tableau en avant et en arrière entre mes vues.

Merci pour votre aide


There are many answers to this questions offering many different ways to perform view controller communication that would indeed work, but I don't see anywhere mentioned which one are actually best to use and which ones to avoid.

In practice, in my opinion only a few solutions are recommended:

  • To pass data forward:
    • override the prepare(for:sender:) method of UIViewController when using a storyboard and segues
    • pass data through an initializer or through properties when performing view controller transitions thtough code
  • To pass data backwards
    • update the app shared state (which you can pass forward between view controllers with either one of the methods above)
    • use delegation
    • use an unwind segue

Solutions I recommend NOT to use:

  • Referencing the previous controller directly instead of using delegation
  • Sharing data through a singleton
  • Passing data through the app delegate
  • Sharing data through the user defaults
  • Passing data through notifications

These solutions, although working in the short term, introduce too many dependencies that will garble the architecture of the app and create more problems later.

For those interested, I wrote some articles that address these points more in depth and highlight the various drawbacks:


I like the idea of Model objects and Mock objects based on NSProxy to commit or discard data if what user selects can be cancelled.

It's easy to pass data around since it's single object or couple of objects and if you have let's say UINavigationController controller, you can keep the reference to model inside and all pushed view controllers can access it directly from navigation controller.


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.


Le M dans MVC est pour "Modèle" et dans le paradigme MVC le rôle des classes de modèles est de gérer les données d'un programme. Un modèle est l'opposé d'une vue - une vue sait comment afficher des données, mais elle ne sait rien sur les données, alors qu'un modèle sait tout sur la façon de travailler avec les données, mais rien sur la façon de les afficher. Les modèles peuvent être compliqués, mais ils ne doivent pas l'être - le modèle de votre application peut être aussi simple qu'un tableau de chaînes ou de dictionnaires.

Le rôle d'un contrôleur est de servir de médiateur entre la vue et le modèle. Par conséquent, ils ont besoin d'une référence à un ou plusieurs objets de vue et un ou plusieurs objets de modèle. Disons que votre modèle est un tableau de dictionnaires, chaque dictionnaire représentant une ligne dans votre tableau. La vue racine de votre application affiche cette table et elle peut être chargée de charger le tableau à partir d'un fichier. Lorsque l'utilisateur décide d'ajouter une nouvelle ligne à la table, il appuie sur un bouton et votre contrôleur crée un nouveau dictionnaire (modifiable) et l'ajoute au tableau. Afin de remplir la ligne, le contrôleur crée un contrôleur de vue de détail et lui donne le nouveau dictionnaire. Le contrôleur de vue détaillée remplit le dictionnaire et revient. Le dictionnaire fait déjà partie du modèle, donc rien d'autre ne doit arriver.


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.


1. Create the instance of first View Controller in the second View Controller and make its property @property (nonatomic,assign) .

2. Assign the SecondviewController instance of this view controller.

2. When you finish the selection operation copy the array to first View Controller,When u unload the SecondView ,FirstView will hold the Array Data.

Hope This Helps.


I am currently contributing to an open source solution to this problem through a project called MCViewFactory, which may be found here:

https://github.com/YetiHQ/manticore-iosviewfactory

The idea is imitate Android's intent paradigm, using a global factory to manage which view you are looking at and using "intents" to switch and pass data between views. All the documentation is on the github page, but here are some highlights:

You setup all your views in .XIB files and register them in the app delegate, while initializing the factory.

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// the following two lines are optional. 
[factory registerView:@"YourSectionViewController"]; 

Now, in your VC, anytime you want to move to a new VC and pass data, you create a new intent and add data to its dictionary (savedInstanceState). Then, just set the current intent of factory:

MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];

All of your views that conform to this need to be subclasses of MCViewController, which allow you to override the new onResume: method, allowing you access to the data you've passed in.

-(void)onResume:(MCIntent *)intent {
    NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
    NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];

    // ...

    // ensure the following line is called, especially for MCSectionViewController
    [super onResume:intent];
}

Hope some of you find this solution useful/interesting.


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

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.


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


Create the property on next view controller .h and define getter and setter.

Add this property in NextVC.h on nextVC

@property (strong, nonatomic) NSString *indexNumber;

Ajouter

@synthesize indexNumber; in NextVC.m

And last

NextVC *vc=[[NextVC alloc]init];

[email protected]"123";

[self.navigationController vc animated:YES];

If you want to pass data from one controller to other try this code

FirstViewController.h

@property (nonatomic, retain) NSString *str;

SecondViewController.h

@property (nonatomic, retain) NSString *str1;

FirstViewController.m

- (void)viewDidLoad
   {
     // message for the second SecondViewController
     self.str = @"text message";

     [super viewDidLoad];
   }

-(IBAction)ButtonClicked
 {
   SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
   secondViewController.str1 = str;
  [self.navigationController pushViewController:secondViewController animated:YES];
 }

Le guide définitif sur l’utilisation des méthodes statiques, de classe ou abstraites en Python est un bon lien pour cette rubrique, et résumez-le comme suit.

@staticmethod fonction @staticmethod n'est rien d'autre qu'une fonction définie dans une classe. Il est appelable sans instancier d'abord la classe. Sa définition est immuable par héritage.

  • Python n'a pas besoin d'instancier une méthode liée pour object.
  • Cela facilite la lisibilité du code et ne dépend pas de l'état de l'objet lui-même;

@classmethod fonction @classmethod également être appelée sans instancier la classe, mais sa définition suit la classe Sub, et non la classe Parent, via l'héritage, pouvant être remplacée par la sous-classe. En effet, le premier argument de la fonction @classmethod doit toujours être cls (classe).

  • Les méthodes d'usine , qui sont utilisées pour créer une instance pour une classe en utilisant par exemple une sorte de pré-traitement.
  • Méthodes statiques appelant des méthodes statiques : si vous divisez une méthode statique en plusieurs méthodes statiques, vous ne devez pas coder en dur le nom de la classe, mais utiliser des méthodes de classe.




ios objective-c oop model-view-controller uiviewcontroller