ios - mvc - uiviewcontroller add child view controller




Passage de données entre les contrôleurs de vue (20)

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.

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?


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


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.


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


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.


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.


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;

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

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.


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.


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.


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

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


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


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:


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


This is not the way to do it, you should use delegates, I'll assume we have two view controllers ViewController1 and ViewController2 and this check thing is in the first one and when its state changes, you want to do something in ViewController2, to achieve that in the proper way, you should do the below:

Add a new file to your project (Objective-C Protocol) File -> New, now name it ViewController1Delegate or whatever you want and write these between the @interface and @end directives

@optional

- (void)checkStateDidChange:(BOOL)checked;

Now go to ViewController2.h and add

#import "ViewController1Delegate.h"

then change its definition to

@interface ViewController2: UIViewController<ViewController1Delegate>

Now go to ViewController2.m and inside the implementation add:

- (void)checkStateDidChange:(BOOL)checked {
     if (checked) {
           // Do whatever you want here
           NSLog(@"Checked");
     }
     else {
           // Also do whatever you want here
           NSLog(@"Not checked");
     }
}

Now go to ViewController1.h and add the following property:

@property (weak, nonatomic) id<ViewController1Delegate> delegate; 

Now if you are creating ViewController1 inside ViewController2 after some event, then you should do it this way using NIB files:

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];

Now you are all set, whenever you detect the event of check changed in ViewController1, all you have to do is the below

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control

Please tell me if there's anything that's not clear of if I didn't understand your question properly.


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







uiviewcontroller