Übergeben von Daten zwischen View Controllern

1372

Ich bin neu in iOS und Objective-C und im gesamten MVC-Paradigma und habe Folgendes festgestellt:

Ich habe eine Ansicht, die als Dateneingabeformular fungiert, und möchte dem Benutzer die Möglichkeit geben, mehrere Produkte auszuwählen. Die Produkte werden in einer anderen Ansicht mit a aufgelistet UITableViewControllerund ich habe Mehrfachauswahl aktiviert.

Meine Frage ist, wie übertrage ich die Daten von einer Ansicht in eine andere? Ich werde die Auswahl auf dem UITableViewin einem Array halten, aber wie kann ich diese dann an die vorherige Dateneingabeformularansicht zurückgeben, damit sie zusammen mit den anderen Daten bei Übermittlung des Formulars an Core Data gespeichert werden kann?

Ich habe herumgesurft und gesehen, dass einige Leute ein Array im App-Delegaten deklarieren. Ich habe etwas über Singletons gelesen, verstehe aber nicht, was dies sind, und ich habe etwas über das Erstellen eines Datenmodells gelesen.

Was wäre die richtige Vorgehensweise und wie würde ich vorgehen?

Matt Price
quelle

Antworten:

1684

Diese Frage scheint hier bei Stackoverflow sehr beliebt zu sein, daher dachte ich, ich würde versuchen, eine bessere Antwort zu geben, um Menschen zu helfen, die in der Welt von iOS wie mir anfangen.

Ich hoffe, diese Antwort ist klar genug, damit die Leute sie verstehen und dass ich nichts verpasst habe.

Daten weiterleiten

Weiterleiten von Daten an einen View Controller von einem anderen View Controller. Sie würden diese Methode verwenden, wenn Sie ein Objekt / einen Wert von einem Ansichts-Controller an einen anderen Ansichts-Controller übergeben möchten, den Sie möglicherweise an einen Navigationsstapel weiterleiten.

Für dieses Beispiel haben wir ViewControllerAundViewControllerB

Um einen BOOLWert von an ViewControllerAzu übergeben, gehen ViewControllerBwir wie folgt vor.

  1. in ViewControllerB.herstellen Sie eine Eigenschaft für dieBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. in müssen ViewControllerASie es erzählen, ViewControllerBalso verwenden Sie eine

    #import "ViewControllerB.h"

    Dann, wo Sie die Ansicht laden möchten, z. didSelectRowAtIndexoder einige, in denen IBActionSie die Eigenschaft festlegen müssen, ViewControllerBbevor Sie sie auf den Navigationsstapel übertragen.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];

    Dies setzt isSomethingEnabledin ViewControllerBan BOOLWert YES.

Datenweiterleitung mit Segues

Wenn Sie Storyboards verwenden, verwenden Sie höchstwahrscheinlich Segues und benötigen dieses Verfahren, um Daten weiterzuleiten. Dies ist ähnlich wie oben, aber anstatt die Daten zu übergeben, bevor Sie den Ansichts-Controller drücken, verwenden Sie eine Methode namens

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

Um also ein BOOLvon an weiterzugeben ViewControllerA, ViewControllerBwürden wir Folgendes tun:

  1. in ViewControllerB.herstellen Sie eine Eigenschaft für dieBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. in müssen ViewControllerASie es erzählen, ViewControllerBalso verwenden Sie eine

    #import "ViewControllerB.h"
  3. Erstellen Sie im Storyboard einen Übergang von ViewControllerAbis ViewControllerBund geben Sie ihm eine Kennung. In diesem Beispiel nennen wir ihn"showDetailSegue"

  4. Als nächstes müssen wir die Methode hinzufügen, die ViewControllerAaufgerufen wird, wenn ein Segue ausgeführt wird. Aus diesem Grund müssen wir erkennen, welcher Segue aufgerufen wurde, und dann etwas tun. In unserem Beispiel prüfen wir, "showDetailSegue"ob dies durchgeführt wird, und geben unseren BOOLWert an weiterViewControllerB

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
            controller.isSomethingEnabled = YES;
        }
    }

    Wenn Sie Ihre Ansichten in einen Navigationscontroller eingebettet haben, müssen Sie die obige Methode geringfügig wie folgt ändern

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
            ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
            controller.isSomethingEnabled = YES;
        }
    }

    Dies setzt isSomethingEnabledin ViewControllerBan BOOLWert YES.

Daten zurückgeben

Um Daten von ViewControllerBan ViewControllerASie zurückzuleiten, müssen Sie Protokolle und Delegaten oder Blöcke verwenden . Letztere können als lose gekoppelter Mechanismus für Rückrufe verwendet werden.

Zu diesem Zweck werden wir ViewControllerAeinen Delegierten von ViewControllerB. Auf diese Weise können Sie ViewControllerBeine Nachricht zurücksenden, ViewControllerAdamit wir Daten zurücksenden können.

Um ViewControllerAein Delegierter ViewControllerBdavon zu sein, muss es ViewControllerBdem Protokoll entsprechen, das wir spezifizieren müssen. Dies gibt an, ViewControllerAwelche Methoden implementiert werden müssen.

  1. In ViewControllerB.h, unter #import, aber über geben @interfaceSie das Protokoll an.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
  2. Als nächstes ViewControllerB.hmüssen Sie noch eine delegateEigenschaft einrichten und in synthetisierenViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
  3. In ViewControllerBrufen wir eine Meldung auf, delegatewenn wir den View Controller öffnen.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
  4. Dafür ist es da ViewControllerB. ViewControllerA.hSagen Sie jetzt in , dass Sie das Protokoll ViewControllerAimportieren ViewControllerBund anpassen sollen.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
  5. In ViewControllerA.mUmsetzung der folgenden Verfahren aus unserem Protokoll

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
  6. Bevor viewControllerBwir zum Navigationsstapel wechseln, müssen wir ihm mitteilen, ViewControllerBdass ViewControllerAes sich um seinen Delegaten handelt. Andernfalls wird eine Fehlermeldung angezeigt.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];

Verweise

  1. Verwenden der Delegierung zur Kommunikation mit anderen View Controllern im View Controller-Programmierhandbuch
  2. Muster delegieren

NSNotification Center Es ist eine andere Möglichkeit, Daten zu übergeben.

// add observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
    id someObject = notification.object // some custom object that was passed with notification fire.
}

// post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

Daten von einer Klasse an eine andere zurückgeben (Eine Klasse kann ein beliebiger Controller, Netzwerk- / Sitzungsmanager, eine UIView-Unterklasse oder eine andere Klasse sein)

Blöcke sind anonyme Funktionen.

In diesem Beispiel werden Daten von Controller B an Controller A übergeben

einen Block definieren

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

Fügen Sie einen Blockhandler (Listener) hinzu, für den Sie einen Wert benötigen (z. B. benötigen Sie Ihre API-Antwort in ControllerA oder ContorllerB-Daten für A).

// in ContollerA.m

- (void)viewDidLoad {
    [super viewDidLoad];
    __unsafe_unretained typeof(self) weakSelf = self;
    self.selectedVoucherBlock = ^(NSString *voucher) {
        weakSelf->someLabel.text = voucher;
    };
}

Gehen Sie zu Controller B.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];

Feuerblock

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: 
(NSIndexPath *)indexPath {
    NSString *voucher = vouchersArray[indexPath.row];
    if (sourceVC.selectVoucherBlock) {
        sourceVC.selectVoucherBlock(voucher);
    }
    [self.navigationController popToViewController:sourceVC animated:YES];
}

Ein weiteres Arbeitsbeispiel für Blöcke

Matt Price
quelle
24
Müssen wir auch ein @class ViewControllerB;@ über die @ protocol-Definition setzen? Ohne es erhalte ich einen Fehler "Erwarteter Typ" auf ViewControllerB in der Zeile: - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; innerhalb der @protocolDeklaration
alan-p
4
Das funktioniert super. Vergessen Sie nicht, @class ViewControllerB zu schreiben, wie alan-p sagt. über dem Protokoll wird sonst der Fehler "Erwarteter Typ" angezeigt.
Andrew Davis
6
Sie brauchen keine Delegierten für die Rückgabe, verwenden Sie einfach Unwind.
Malhal
4
Wenn ich "viewControllerB.delegate = self;" In ViewControllerB wird eine Fehlermeldung angezeigt. Bei Zuweisung zu 'id <ViewControllerBDelegate>' vom inkompatiblen Typ 'ViewControllerB * const __strong' bin ich mir nicht sicher, was ich falsch mache. Kann jemand helfen? Außerdem musste ich ändern: initWithNib -> initWithNibName.
uplearnedu.com
4
Wenn Sie verwenden NavigationController, müssen Sie [self.navigationController pushViewController:viewController animated:YES];stattdessen verwenden[self pushViewController:viewControllerB animated:YES];
Nazir
192

Schnell

Hier und in der Umgebung von StackOverflow gibt es unzählige Erklärungen. Wenn Sie jedoch Anfänger sind und nur versuchen, etwas Grundlegendes zum Laufen zu bringen, schauen Sie sich dieses YouTube-Tutorial an (es hat mir geholfen, endlich zu verstehen, wie es geht).

Weiterleitung von Daten an den nächsten View Controller

Das folgende Beispiel basiert auf dem Video. Die Idee ist, eine Zeichenfolge aus dem Textfeld im First View Controller an die Beschriftung im Second View Controller zu übergeben.

Geben Sie hier die Bildbeschreibung ein

Erstellen Sie das Storyboard-Layout im Interface Builder. Um den Übergang zu machen, Controlklicken Sie einfach auf die Schaltfläche und ziehen Sie zum Second View Controller.

First View Controller

Der Code für den First View Controller lautet

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

}

Second View Controller

Und der Code für den Second View Controller lautet

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
    }

}

Vergiss nicht

  • Schließen Sie die Steckdosen für die UITextFieldund die UILabel.
  • Stellen Sie den ersten und zweiten View Controller auf die entsprechenden Swift-Dateien in IB ein.

Daten an den vorherigen View Controller zurückgeben

Um Daten vom zweiten View Controller an den ersten View Controller zurückzugeben, verwenden Sie ein Protokoll und einen Delegaten . Dieses Video ist ein sehr klarer Überblick über diesen Prozess:

Das folgende Beispiel basiert auf dem Video (mit einigen Änderungen).

Geben Sie hier die Bildbeschreibung ein

Erstellen Sie das Storyboard-Layout im Interface Builder. Nochmals, um den Übergang zu machen, Sie nurControl von der Schaltfläche auf den Second View Controller. Setzen Sie die Segue-ID auf showSecondViewController. Vergessen Sie auch nicht, die Steckdosen und Aktionen mit den Namen im folgenden Code anzuschließen.

First View Controller

Der Code für den First View Controller lautet

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

Beachten Sie die Verwendung unserer Gewohnheit DataEnteredDelegate Protokolls.

Second View Controller und Protokoll

Der Code für den zweiten Ansichtscontroller lautet

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    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)
    }
}

Notiere dass der protocol außerhalb der View Controller-Klasse befindet.

Das ist es. Wenn Sie die App jetzt ausführen, sollten Sie in der Lage sein, Daten vom zweiten Ansichts-Controller zum ersten zurückzusenden.

Suragch
quelle
Ist dies angesichts einiger der neuesten Swift-Updates immer noch ein gängiges Muster?
Piofusco
4
Bei den meisten Swift-Updates, die ich gesehen habe, handelt es sich um relativ geringfügige syntaktische Änderungen, nicht um Änderungen bei der Datenübertragung zwischen Ansichtscontrollern. Wenn ich von solchen wesentlichen Änderungen erfahre, aktualisiere ich meine Antwort.
Suragch
2
offtopic - iOS hat eine so hässliche Möglichkeit, Parameter an neue Ansichts-Controller zu übergeben, unglaublich - Sie müssen Parameter nicht an einem Ort festlegen, an dem Sie den Anruf tätigen, sondern an einem anderen. Android hat in dieser Hinsicht einen besseren Ansatz: Wenn Sie eine Aktivität starten, können Sie alle Daten (fast) über ihre Startabsicht übergeben. Einfach. Keine Notwendigkeit zu besetzen oder so. Die Rückgabe von Rückgabewerten an den Anrufer ist ebenfalls eine wichtige Sache, die nicht delegiert werden muss. Natürlich ist es auch möglich, hässliche Ansätze zu verwenden, kein Problem))
Mixaz
1
@Himanshu, bekomme zuerst einen Verweis auf den zweiten View Controller. Aktualisieren Sie dann die darin enthaltene öffentliche Variable.
Suragch
8
@Honig. Ich finde das Wort "Delegierter" verwirrend. Lassen Sie mich das Wort "Arbeiter" verwenden. Der "Arbeiter" (Controller für die erste Ansicht) tut, was der "Chef" (Controller für die zweite Ansicht) von ihm verlangt. Der "Chef" weiß nicht, wer sein "Arbeiter" sein wird; es könnte jeder sein. In der ersten Ansichtssteuerung ("Arbeiter" -Klasse) heißt es also, ich werde Ihr "Arbeiter" sein. Du sagst mir, was ich auf dem Etikett schreiben soll und ich werde es für dich tun. Bedeutet secondViewController.delegate = selfalso "Ich bin damit einverstanden, der Arbeiter des Chefs zu sein." In dieser Antwort finden Sie ein weiteres Beispiel und weitere Erläuterungen.
Suragch
136

Das M in MVC steht für "Modell" und im MVC-Paradigma besteht die Rolle von Modellklassen darin, die Daten eines Programms zu verwalten. Ein Modell ist das Gegenteil einer Ansicht - eine Ansicht weiß, wie Daten angezeigt werden, aber sie weiß nichts darüber, was mit Daten zu tun ist, während ein Modell alles über die Arbeit mit Daten weiß, aber nichts darüber, wie sie angezeigt werden. Modelle können kompliziert sein, müssen es aber nicht - das Modell für Ihre App ist möglicherweise so einfach wie eine Reihe von Zeichenfolgen oder Wörterbüchern.

Die Rolle eines Controllers besteht darin, zwischen Ansicht und Modell zu vermitteln. Daher benötigen sie einen Verweis auf ein oder mehrere Ansichtsobjekte und ein oder mehrere Modellobjekte. Angenommen, Ihr Modell besteht aus einer Reihe von Wörterbüchern, wobei jedes Wörterbuch eine Zeile in Ihrer Tabelle darstellt. In der Stammansicht Ihrer App wird diese Tabelle angezeigt, und sie ist möglicherweise für das Laden des Arrays aus einer Datei verantwortlich. Wenn der Benutzer beschließt, der Tabelle eine neue Zeile hinzuzufügen, tippt er auf eine Schaltfläche, und Ihr Controller erstellt ein neues (veränderbares) Wörterbuch und fügt es dem Array hinzu. Um die Zeile auszufüllen, erstellt der Controller einen Detailansichts-Controller und gibt ihm das neue Wörterbuch. Der Detailansichts-Controller füllt das Wörterbuch aus und kehrt zurück. Das Wörterbuch ist bereits Teil des Modells, sodass nichts anderes passieren muss.

Caleb
quelle
95

Es gibt verschiedene Möglichkeiten, wie Daten in iOS an eine andere Klasse empfangen werden können. Beispielsweise -

  1. Direkte Initialisierung nach Zuweisung einer anderen Klasse.
  2. Delegierung - zum Zurückgeben von Daten
  3. Benachrichtigung - zum gleichzeitigen Senden von Daten an mehrere Klassen
  4. Speichern in NSUserDefaults - für den späteren Zugriff
  5. Singleton-Klassen
  6. Datenbanken und andere Speichermechanismen wie Plist usw.

Für das einfache Szenario, einen Wert an eine andere Klasse zu übergeben, deren Zuordnung in der aktuellen Klasse erfolgt, wäre die häufigste und bevorzugte Methode die direkte Festlegung von Werten nach der Zuweisung. Dies geschieht wie folgt: -

Wir können es mit zwei Controllern verstehen - Controller1 und Controller2

Angenommen, Sie möchten in der Controller1-Klasse das Controller2-Objekt erstellen und es mit einem übergebenen String-Wert übertragen. Dies kann folgendermaßen geschehen:

- (void)pushToController2 {

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

Bei der Implementierung der Controller2-Klasse wird diese Funktion als

@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

Sie können die Eigenschaften der Controller2-Klasse auch direkt wie folgt festlegen:

- (void)pushToController2 {

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

Um mehrere Werte zu übergeben, können Sie die folgenden Parameter verwenden:

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

Wenn Sie mehr als 3 Parameter übergeben müssen, die sich auf ein allgemeines Feature beziehen, können Sie die Werte in einer Model-Klasse speichern und dieses modelObject an die nächste Klasse übergeben

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

Also kurz gesagt, wenn Sie wollen -

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.

Hoffe das hilft

Borncrazy
quelle
84

Nach weiteren Recherchen schien es, dass Protokolle und Delegierte die richtige / von Apple bevorzugte Methode sind.

Am Ende habe ich dieses Beispiel verwendet

Datenaustausch zwischen View Controllern und anderen Objekten im iPhone Dev SDK

Funktionierte einwandfrei und erlaubte mir, einen String und ein Array zwischen meinen Ansichten vorwärts und rückwärts zu übergeben.

Danke für deine Hilfe

Matt Price
quelle
3
Verwenden Sie keine Protokolle und Delegaten, sondern nur Unwind.
Malhal
1
@malhal Was ist, wenn Sie keine Storyboards verwenden?
Evan R
Ich hasse auch nutzlose Protokolle und Delegierte. @ Malhal
DawnSong
@EvanR Sie können Segmente im Code erstellen und ausführen. Es ist alles das Gleiche.
DawnSong
1
Im Wesentlichen ist die gesamte Qualitätssicherung auf dieser Seite "aus den alten Tagen vor den Containeransichten". Sie hätten sich in einer Million Jahren noch nie mit Protokollen oder Delegierten beschäftigt. Jedes kleine Ding, das Sie auf einem Bildschirm tun, ist sowieso eine Containeransicht, daher existiert die Frage wirklich nicht mehr - Sie haben bereits alle Referenzen "auf und ab" aus allen Containeransichten.
Fattie
66

Ich finde die einfachste und eleganteste Version mit vorbeiziehenden Blöcken. Nennen wir den View Controller, der auf zurückgegebene Daten wartet, "A" und den zurückgegebenen View Controller "B". In diesem Beispiel möchten wir zwei Werte erhalten: den ersten von Typ1 und den zweiten von Typ2.

Angenommen, wir verwenden Storyboard, setzt der erste Controller den Rückrufblock, beispielsweise während der Vorbereitung des Segues:

- (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);
        };

    }
}

und "B" -Ansichtscontroller sollten die Rückrufeigenschaft BViewController.h deklarieren:

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

Als in der Implementierungsdatei BViewController.m sollte nach Aufrufen der gewünschten Werte für die Rückgabe unseres Rückrufs Folgendes aufgerufen werden:

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

Eine Sache, an die Sie sich erinnern sollten, ist, dass die Verwendung von Block häufig starke und __schwache Referenzen verwalten muss, wie hier erläutert

Leszek Zarna
quelle
Warum sollte value nicht ein Parameter für den Rückrufblock sein, anstatt eine separate Eigenschaft zu sein?
Timuçin
56

In vielen der gegebenen Antworten sind einige gute Informationen enthalten, aber keine beantwortet die Frage vollständig.

Bei der Frage wird nach der Weitergabe von Informationen zwischen View Controllern gefragt. In dem angegebenen Beispiel wird nach der Weitergabe von Informationen zwischen Ansichten gefragt. Angesichts der selbst erklärten Neuheit für iOS war das Originalposter wahrscheinlich zwischen viewControllern und nicht zwischen Ansichten gedacht (ohne Beteiligung der ViewController). Es scheint, dass sich alle Antworten auf zwei Ansichts-Controller konzentrieren. Was ist jedoch, wenn die App mehr als zwei Ansichts-Controller in den Informationsaustausch einbeziehen muss?

Das Originalplakat fragte auch nach Singletons und der Verwendung des AppDelegate . Diese Fragen müssen beantwortet werden.

Um allen anderen zu helfen, die sich diese Frage ansehen und eine vollständige Antwort wünschen, werde ich versuchen, sie bereitzustellen.

Anwendungsszenarien

Anstatt eine sehr hypothetische, abstrakte Diskussion zu führen, ist es hilfreich, konkrete Anwendungen im Auge zu behalten. Um eine Situation mit zwei View-Controllern und eine Situation mit mehr als zwei View-Controllern zu definieren, werde ich zwei konkrete Anwendungsszenarien definieren.

Szenario eins: Maximal zwei View Controller müssen jemals Informationen austauschen. Siehe Abbildung eins.

Diagramm des ursprünglichen Problems

Die Anwendung enthält zwei Ansichtssteuerungen. Es gibt einen ViewControllerA (Dateneingabeformular) und einen View Controller B (Produktliste). Die in der Produktliste ausgewählten Artikel müssen mit den Artikeln übereinstimmen, die im Textfeld des Dateneingabeformulars angezeigt werden. In diesem Szenario müssen ViewControllerA und ViewControllerB direkt miteinander und mit keinen anderen View-Controllern kommunizieren.

Szenario zwei : Mehr als zwei Ansichtscontroller müssen dieselben Informationen gemeinsam nutzen. Siehe Abbildung zwei.

Anwendungsdiagramm für das Hausinventar

Die Anwendung enthält vier Ansichtssteuerungen. Es ist eine tab-basierte Anwendung zum Verwalten des Inventars. Drei Ansichtssteuerungen zeigen unterschiedlich gefilterte Ansichten derselben Daten:

  • ViewControllerA - Luxusartikel
  • ViewControllerB - Nicht versicherte Artikel
  • ViewControllerC - Gesamtes Hausinventar
  • ViewControllerD - Neues Artikelformular hinzufügen

Jedes Mal, wenn ein einzelnes Element erstellt oder bearbeitet wird, muss es auch mit den anderen Ansichtssteuerungen synchronisiert werden. Wenn wir beispielsweise ein Boot in ViewControllerD hinzufügen, das jedoch noch nicht versichert ist, muss das Boot angezeigt werden, wenn der Benutzer zu ViewControllerA (Luxusartikel) und auch zu ViewControllerC (Gesamtes Hausinventar) wechselt, jedoch nicht, wenn der Benutzer zu wechselt ViewControllerB (Nicht versicherte Gegenstände). Wir müssen uns nicht nur mit dem Hinzufügen neuer Elemente befassen, sondern auch mit dem Löschen von Elementen (die von einem der vier Ansichts-Controller zulässig sein können) oder dem Bearbeiten vorhandener Elemente (die möglicherweise über das Formular "Neues Element hinzufügen" zulässig sind), um sie erneut zu verwenden zur Bearbeitung).

Da alle Ansichtscontroller dieselben Daten gemeinsam nutzen müssen, müssen alle vier Ansichtscontroller synchron bleiben, und daher muss eine Art Kommunikation mit allen anderen Ansichtscontrollern bestehen, wenn ein einzelner Ansichtscontroller die zugrunde liegenden Daten ändert. Es sollte ziemlich offensichtlich sein, dass in diesem Szenario nicht jeder Ansichts-Controller direkt miteinander kommunizieren soll. Falls es nicht offensichtlich ist, überlegen Sie, ob wir 20 verschiedene Ansichts-Controller hatten (anstatt nur 4). Wie schwierig und fehleranfällig wäre es, jeden der anderen 19 View Controller zu benachrichtigen, wenn ein View Controller eine Änderung vornimmt?

Die Lösungen: Delegierte und das Beobachtermuster sowie Singletons

In Szenario eins haben wir mehrere praktikable Lösungen, wie andere Antworten gegeben haben

  • Segues
  • Delegierte
  • Eigenschaften direkt auf View Controllern einstellen
  • NSUserDefaults (eigentlich eine schlechte Wahl)

In Szenario zwei haben wir andere praktikable Lösungen:

  • Beobachtermuster
  • Singletons

Ein Singleton ist eine Instanz einer Klasse, wobei diese Instanz die einzige Instanz ist, die während ihrer Lebensdauer existiert. Ein Singleton erhält seinen Namen von der Tatsache, dass es sich um die einzelne Instanz handelt. Normalerweise haben Entwickler, die Singletons verwenden, spezielle Klassenmethoden, um auf sie zuzugreifen.

+ (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;
}

Nachdem wir nun verstanden haben, was ein Singleton ist, wollen wir diskutieren, wie ein Singleton in das Beobachtermuster passt. Das Beobachtermuster wird für ein Objekt verwendet, um auf Änderungen durch ein anderes Objekt zu reagieren. Im zweiten Szenario haben wir vier verschiedene Ansichts-Controller, die alle über Änderungen an den zugrunde liegenden Daten informiert werden möchten. Die "zugrunde liegenden Daten" sollten zu einer einzelnen Instanz, einem Singleton, gehören. Das "Wissen über Änderungen" wird durch Beobachten von Änderungen erreicht, die am Singleton vorgenommen wurden.

Die Home-Inventar-Anwendung verfügt über eine einzelne Instanz einer Klasse, mit der eine Liste von Inventarelementen verwaltet werden kann. Der Manager würde eine Sammlung von Haushaltsgegenständen verwalten. Das Folgende ist eine Klassendefinition für den Datenmanager:

#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

Wenn sich die Sammlung von Inventargegenständen ändert, müssen die Ansichtscontroller auf diese Änderung aufmerksam gemacht werden. Die obige Klassendefinition macht nicht deutlich, wie dies geschehen wird. Wir müssen dem Beobachtermuster folgen. Die View Controller müssen den sharedManager formell beobachten. Es gibt zwei Möglichkeiten, ein anderes Objekt zu beobachten:

  • Schlüsselwertbeobachtung (KVO)
  • NSNotificationCenter.

In Szenario zwei haben wir keine einzige Eigenschaft des HouseholdInventoryManager, die mit KVO beobachtet werden könnte. Da wir keine einzige Eigenschaft haben, die leicht zu beobachten ist, muss das Beobachtermuster in diesem Fall mithilfe von NSNotificationCenter implementiert werden. Jeder der vier View-Controller abonniert Benachrichtigungen, und der sharedManager sendet gegebenenfalls Benachrichtigungen an das Benachrichtigungscenter. Der Bestandsverwalter muss nichts über die Ansichtssteuerungen oder Instanzen anderer Klassen wissen, die möglicherweise wissen möchten, wann sich die Sammlung von Bestandsgegenständen ändert. Das NSNotificationCenter kümmert sich um diese Implementierungsdetails. Die View Controller abonnieren einfach Benachrichtigungen und der Datenmanager sendet einfach Benachrichtigungen.

Viele Anfängerprogrammierer nutzen die Tatsache, dass es immer genau einen Anwendungsdelegierten gibt in der Lebensdauer der Anwendung, die global zugänglich ist. Anfängliche Programmierer verwenden diese Tatsache, um Objekte und Funktionen in das appDelegate einzufügen, um von überall in der Anwendung bequem darauf zugreifen zu können. Nur weil das AppDelegate ein Singleton ist, heißt das nicht, dass es alle anderen Singletons ersetzen sollte. Dies ist eine schlechte Praxis, da sie eine Klasse zu stark belastet und gegen gute objektorientierte Praktiken verstößt. Jede Klasse sollte eine klare Rolle haben, die leicht zu erklären ist, oft nur durch den Namen der Klasse.

Jedes Mal, wenn Ihr Anwendungsdelegierter aufgebläht wird, entfernen Sie Funktionen in Singletons. Beispielsweise sollte der Core Data Stack nicht im AppDelegate belassen werden, sondern in einer eigenen Klasse, einer coreDataManager-Klasse.

Verweise

Jason Cross
quelle
41

Das OP erwähnte keine View Controller, aber so viele Antworten, dass ich mich darauf einlassen wollte, was einige der neuen Funktionen des LLVM ermöglichen, um dies zu vereinfachen, wenn Daten von einem View Controller an einen anderen übertragen werden sollen einige Ergebnisse zurückbekommen.

Storyboard-Segmente, ARC- und LLVM-Blöcke machen dies für mich einfacher als je zuvor. Einige der oben genannten Antworten erwähnten bereits Storyboards und Segues, stützten sich jedoch immer noch auf die Delegation. Das Definieren von Delegaten funktioniert sicherlich, aber einige Leute finden es möglicherweise einfacher, Zeiger oder Codeblöcke zu übergeben.

Mit UINavigators und Segues gibt es einfache Möglichkeiten, Informationen an den untergeordneten Controller weiterzuleiten und die Informationen zurückzugewinnen. ARC vereinfacht das Übergeben von Zeigern auf von NSObjects abgeleitete Elemente. Wenn Sie also möchten, dass der untergeordnete Controller einige Daten für Sie hinzufügt / ändert / ändert, übergeben Sie ihm einen Zeiger auf eine veränderbare Instanz. Blöcke erleichtern das Übergeben von Aktionen. Wenn Sie also möchten, dass der untergeordnete Controller eine Aktion auf Ihrem übergeordneten Controller aufruft, übergeben Sie ihm einen Block. Sie definieren den Block so, dass er eine beliebige Anzahl von Argumenten akzeptiert, die für Sie sinnvoll sind. Sie können die API auch so gestalten, dass mehrere Blöcke verwendet werden, wenn dies besser passt.

Hier sind zwei triviale Beispiele für den Segue-Kleber. Der erste ist unkompliziert und zeigt einen Parameter, der für die Eingabe übergeben wurde, den zweiten für die Ausgabe.

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

Dieses zweite Beispiel zeigt die Übergabe eines Rückrufblocks für das zweite Argument. Ich verwende gerne Blöcke, weil dadurch die relevanten Details in der Quelle - der übergeordneten Quelle - nahe beieinander bleiben.

// 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];
    }];
}
Schwacher Zeiger
quelle
41

Interessanter ist es, Daten von ViewController 2 (Ziel) an ViewController 1 (Quelle) zurückzugeben. Angenommen, Sie verwenden storyBoard. Dies sind alle Möglichkeiten, die ich herausgefunden habe:

  • Delegieren
  • Benachrichtigung
  • Benutzer Standardeinstellungen
  • Singleton

Diese wurden hier bereits besprochen.

Ich fand, dass es mehr Möglichkeiten gibt:

- Blockrückrufe verwenden:

Verwenden Sie es in der prepareForSegueMethode im VC1

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

-Verwenden von Storyboards Abwickeln (Beenden)

Implementieren Sie eine Methode mit einem UIStoryboardSegue-Argument in VC 1 wie folgt:

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

Haken Sie im StoryBoard die Schaltfläche "Zurück" in die grüne Schaltfläche "Beenden" (Abwickeln) des VC ein. Jetzt haben Sie einen Abschnitt, der "zurückgeht", sodass Sie die Eigenschaft destinationViewController in prepareForSegue von VC2 verwenden und alle Eigenschaften von VC1 ändern können, bevor sie zurückkehren.

  • Eine weitere Option zur Verwendung von Storyboards Undwind (Beenden) - Sie können die in VC1 geschriebene Methode verwenden

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

    Und in prepareForSegue von VC1 können Sie jede Eigenschaft ändern, die Sie freigeben möchten.

In beiden Abwicklungsoptionen können Sie die Tag-Eigenschaft der Schaltfläche festlegen und in prepareForSegue überprüfen.

Hoffe, ich habe etwas zur Diskussion hinzugefügt.

:) Prost.

Jewgeni
quelle
40

Es gibt mehrere Methoden zum Teilen von Daten.

  1. Sie können jederzeit Daten mit teilen NSUserDefaults. Legen Sie den Wert fest, den Sie für einen Schlüssel Ihrer Wahl freigeben möchten, und ermitteln Sie den Wert NSUserDefault, der diesem Schlüssel zugeordnet ist, im nächsten Ansichts-Controller.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
  2. Sie können einfach eine Eigenschaft in erstellen viewcontrollerA. Erstellen Sie ein Objekt von viewcontrollerAin viewcontrollerBund weisen Sie dieser Eigenschaft den gewünschten Wert zu.

  3. Sie können hierfür auch benutzerdefinierte Delegaten erstellen.

Anubrata Santra
quelle
30
Der typische Zweck von NSUserDefaults besteht darin, Benutzereinstellungen zu speichern, die zwischen den App-Ausführungen bestehen bleiben. Daher bleibt alles, was hier gespeichert wird, hier, sofern es nicht ausdrücklich entfernt wird. Es ist eine wirklich schlechte Idee, dies zu verwenden, um Informationen zwischen View Controllern (oder anderen Objekten) in einer App zu übertragen.
José González
30

Wenn Sie Daten von einem Controller an einen anderen übergeben möchten, versuchen Sie diesen 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];
 }
user2998756
quelle
29

Dies ist eine sehr alte Antwort und dies ist ein Anti-Muster. Bitte verwenden Sie Delegierte. Verwenden Sie diesen Ansatz nicht !!

1. Erstellen Sie die Instanz des ersten View Controllers im zweiten View Controller und legen Sie deren Eigenschaft fest @property (nonatomic,assign).

2. Weisen Sie die SecondviewControllerInstanz dieses View Controllers zu.

2. Wenn Sie den Auswahlvorgang abgeschlossen haben, kopieren Sie das Array in den ersten View Controller. Wenn Sie die SecondView entladen, enthält FirstView die Array-Daten.

Hoffe das hilft.

kaar3k
quelle
2
Ich glaube nicht, dass dies der richtige Weg ist, da dadurch eine sehr verzerrte Verbindung zwischen View Controllern hergestellt wird. Ich bleibe nicht wirklich bei MVC.
Matt Price
1
Wenn Sie MVC strikt befolgen möchten, verwenden Sie NSNotificationCenter. Eine Methode kann von ViewControllerA zu ViewControllerB aufgerufen werden. Überprüfen Sie dies , um u
kaar3k am
28

Ich habe lange nach dieser Lösung gesucht, aber ich habe sie gefunden. Deklarieren Sie zunächst alle Objekte in Ihrer SecondViewController.h-Datei wie

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

Weisen Sie nun in Ihrer Implementierungsdatei den Speicher für diese Objekte wie folgt zu

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

Jetzt haben Sie den Speicher für Arrayund das Objekt zugewiesen . Jetzt können Sie diesen Speicher füllen, bevor Sie ihn drückenViewController

Gehen Sie zu Ihrer SecondViewController.h und schreiben Sie zwei Methoden

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

In der Implementierungsdatei können Sie die Funktion implementieren

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

Erwarten Sie, dass Sie CustomObjecteine Setter-Funktion haben müssen.

Jetzt ist Ihre grundlegende Arbeit erledigt. Gehen Sie zu dem Ort, an dem Sie die drücken möchten, SecondViewControllerund führen Sie die folgenden Schritte aus

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

Achten Sie auf Rechtschreibfehler.

AsifHabib
quelle
24

Dies ist nicht der richtige Weg. Sie sollten Delegaten verwenden. Ich gehe davon aus, dass wir zwei View Controller ViewController1 und ViewController2 haben. Diese Überprüfungssache befindet sich in der ersten und wenn sich ihr Status ändert, möchten Sie etwas in ViewController2 tun, um Um dies auf die richtige Weise zu erreichen, sollten Sie Folgendes tun:

Fügen Sie Ihrem Projekt eine neue Datei hinzu (Objective-C-Protokoll) Datei -> Neu, nennen Sie sie jetzt ViewController1Delegate oder was auch immer Sie möchten, und schreiben Sie diese zwischen die Anweisungen @interface und @end

@optional

- (void)checkStateDidChange:(BOOL)checked;

Gehen Sie nun zu ViewController2.h und fügen Sie hinzu

#import "ViewController1Delegate.h"

Ändern Sie dann die Definition in

@interface ViewController2: UIViewController<ViewController1Delegate>

Gehen Sie nun zu ViewController2.m und fügen Sie in der Implementierung Folgendes hinzu:

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

Gehen Sie nun zu ViewController1.h und fügen Sie die folgende Eigenschaft hinzu:

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

Wenn Sie nun nach einem bestimmten Ereignis ViewController1 in ViewController2 erstellen, sollten Sie dies mithilfe von NIB-Dateien tun:

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

Jetzt sind Sie fertig. Immer wenn Sie feststellen, dass sich das Überprüfungsereignis in ViewController1 geändert hat, müssen Sie nur noch Folgendes tun

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

Bitte sagen Sie mir, wenn etwas nicht klar ist, wenn ich Ihre Frage nicht richtig verstanden habe.

Boda Taljo
quelle
23

Wenn Sie Daten von einem zu einem anderen viewController senden möchten, haben Sie folgende Möglichkeiten:

Angenommen, wir haben viewController: viewControllerA und viewControllerB

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

}

Auf diese Weise können Sie Daten von viewControllerA an viewControllerB übergeben, ohne einen Delegaten festzulegen. ;)

Aniruddh Joshi
quelle
1
Ich habe versucht, Ihren Code in meinem Projekt zu verwenden, kann die Werte jedoch nicht in viewcontrollerB abrufen. Kannst du mir sagen, was das Problem sein könnte?
Der X-Coder
1
@ Ajitthala Können Sie Ihren Code in eine neue Frage einfügen? Ich werde versuchen, Ihr Problem zu lösen. :)
Aniruddh Joshi
1
Ist es falsch, keine Init-Methoden zu verwenden und einfach so etwas wie vcB.string = @ "asdf" von Viewcontroller A aus zu tun?
Khanh.tran.vinh
1
@ khanh.tran.vinh Hängt davon ab, ob Sie ARC verwenden oder nicht.
Aniruddh Joshi
21

Ich weiß, dass dies ein geschlagenes Thema ist, aber für diejenigen, die diese Frage mit einer SWIFT-Neigung beantworten möchten und ein Beispiel mit bloßen Knochen wünschen, hier meine Methode zum Übergeben von Daten, wenn Sie einen Segue verwenden, um herumzukommen.

Es ist ähnlich wie oben, jedoch ohne die Schaltflächen, Beschriftungen und dergleichen. Einfach Daten von einer Ansicht zur nächsten weitergeben.

Richten Sie das Storyboard ein

Es gibt drei Teile.

  1. Der Absender
  2. Der Segue
  3. Der Empfänger

Dies ist ein sehr einfaches Ansichtslayout mit einem Übergang zwischen ihnen.


Sehr einfaches Ansichtslayout.  Hinweis: Kein Navigationscontroller


Hier ist das Setup für den Absender


Der Absender


Hier ist das Setup für den Empfänger.


Der Empfänger


Zuletzt das Setup für den Segue.


Die Segue-ID


Die View Controller

Wir halten dies einfach, also keine Schaltflächen, keine Aktionen. Wir verschieben einfach Daten vom Absender zum Empfänger, wenn die Anwendung geladen wird, und geben dann den übertragenen Wert an die Konsole aus.

Diese Seite nimmt den ursprünglich geladenen Wert und gibt ihn weiter.

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

    }

}

Diese Seite sendet nur den Wert der Variablen beim Laden an die Konsole. Zu diesem Zeitpunkt sollte sich unser Lieblingsfilm in dieser Variablen befinden.

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)")

    }   
}

So können Sie es angehen, wenn Sie einen Segue verwenden möchten und Ihre Seiten nicht unter einem Navigationscontroller stehen.

Sobald es ausgeführt wird, sollte es automatisch zur Empfängeransicht wechseln und den Wert vom Absender an den Empfänger übergeben, wobei der Wert in der Konsole angezeigt wird.

Ghost Busters ist ein Klassiker.

Christopher Wade Cantley
quelle
19

In meinem Fall habe ich eine Singleton-Klasse verwendet, die als globales Objekt fungieren kann und den Zugriff auf die Daten von fast überall in der App ermöglicht. Als erstes muss eine Singleton-Klasse erstellt werden. Weitere Informationen finden Sie auf der Seite „ Wie sollte mein Objective-C-Singleton aussehen? “. Um das Objekt global zugänglich zu machen, habe ich es einfach importiert, appName_Prefix.pchum die Importanweisung in allen Klassen anzuwenden. Um auf dieses Objekt zuzugreifen und es zu verwenden, habe ich einfach die Klassenmethode implementiert, um die gemeinsam genutzte Instanz zurückzugeben, die ihre eigenen Variablen enthält

petershine
quelle
Dies ist die richtige Antwort. Verwenden Sie einfach einen Singleton als "Modell". Beachten Sie, dass, wie Caleb sagt, "das Modell für Ihre App möglicherweise so einfach wie ein Array von Zeichenfolgen ist" . Es ist wichtig zu beachten, dass es wirklich trivial ist, in Swift einen Singleton zu machen . (So ​​einfach, dass es hier nicht einmal erwähnenswert ist - nur Google.) Für neue Programmierer lohnt es sich zu verstehen, dass das Erstellen eines Singletons früher ein echtes Ärgernis war . Singletons sind jedoch für die iOS-Programmierung von zentraler Bedeutung - alles, was Apple tut, ist ein Singleton. Aus diesem Grund hat Apple es endlich geschafft (in Swift), Singletons richtig zu machen.
Fattie
1
Beachten Sie jedoch, dass heutzutage (2016+) "in iOS alles eine Containeransicht ist". Bei jeder einzelnen Aktion auf dem Bildschirm wird eine kleine Containeransicht erstellt. Es ist ziemlich trivial, die Referenzketten von Containeransichten "auf und ab" zu erhalten (obwohl Apple dies in Zukunft einfacher machen wird), und Sie tun dies sowieso für fast jede Containeransicht. Wenn Sie das trotzdem getan haben, haben Sie die Antwort. Keine Notwendigkeit für einen Singleton. Container Ansicht Intro ... stackoverflow.com/a/23403979/294884
Fattie
19

Swift 5

Nun, die Antwort von Matt Price ist vollkommen in Ordnung, um Daten weiterzugeben, aber ich werde sie in der neuesten Swift-Version neu schreiben, da ich glaube, dass neue Programmierer es aufgrund neuer Syntax und Methoden / Frameworks als schwierig empfinden, da der ursprüngliche Beitrag in Objective-C enthalten ist.

Es gibt mehrere Optionen zum Übergeben von Daten zwischen View Controllern.

  1. Verwenden des Navigationscontrollers Push
  2. Segue verwenden
  3. Delegate verwenden
  4. Benachrichtigungsbeobachter verwenden
  5. Block verwenden

Ich werde seine Logik in Swift mit dem neuesten iOS Framework umschreiben


Daten über Navigation Controller übertragen Push : Von ViewControllerA zu ViewControllerB

Schritt 1. Deklarieren Sie die Variable in ViewControllerB

var isSomethingEnabled = false

Schritt 2. Druckvariable in der ViewDidLoad-Methode von ViewControllerB

override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through segue, navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }

Schritt 3. Übergeben Sie in ViewControllerA Daten, während Sie durch Navigation Controller drücken

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
        viewControllerB.isSomethingEnabled = true
        if let navigator = navigationController {
            navigator.pushViewController(viewControllerB, animated: true)
        }
    }

Hier ist der vollständige Code für:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:Passing Data through Navigation PushViewController
    @IBAction func goToViewControllerB(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.isSomethingEnabled = true
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    //MARK:  - Variable for Passing Data through Navigation push   
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }
}

Übergeben von Daten durch Segue : Von ViewControllerA zu ViewControllerB

Schritt 1. Erstellen Sie einen Segue von ViewControllerA zu ViewControllerB und geben Sie Identifier = showDetailSegue im Storyboard wie unten gezeigt ein

Geben Sie hier die Bildbeschreibung ein

Schritt 2. Deklarieren Sie in ViewControllerB einen brauchbaren Namen mit dem Namen isSomethingEnabled und drucken Sie seinen Wert aus.

Schritt 3. In ViewControllerA übergeben Sie den Wert von SomethingEnabled, während Sie Segue übergeben

Hier ist der vollständige Code für:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:  - - Passing Data through Segue  - - 
    @IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
        performSegue(withIdentifier: "showDetailSegue", sender: nil)
    }

    //Segue Delegate Method
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true//passing data
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through segue
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }
}

Übergeben von Daten durch Delegate : Von ViewControllerB zu ViewControllerA

Schritt 1. Deklarieren Sie das Protokoll ViewControllerBDelegate in der ViewControllerB-Datei, jedoch außerhalb der Klasse

protocol ViewControllerBDelegate: NSObjectProtocol {

    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

Schritt 2. Deklarieren Sie die Delegate-Variableninstanz in ViewControllerB

var delegate: ViewControllerBDelegate?

Schritt 3. Senden Sie Daten für den Delegaten innerhalb der viewDidLoad-Methode von ViewControllerB

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

Schritt 4. Bestätigen Sie ViewControllerBDelegate in ViewControllerA

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}

Schritt 5. Bestätigen Sie, dass Sie delegate in ViewControllerA implementieren

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self//confirming delegate
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }

Schritt 6. Implementieren Sie die Delegate-Methode zum Empfangen von Daten in ViewControllerA

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

Hier ist der vollständige Code für:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //Delegate method
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        //MARK:  - - - -  Set Data for Passing Data through Delegate  - - - - - -
        delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
    }
}

Übergeben von Daten durch Notification Observer : Von ViewControllerB zu ViewControllerA

Schritt 1. Daten in Notification Observer in ViewControllerB festlegen und veröffentlichen

let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

Schritt 2. Fügen Sie Notification Observer in ViewControllerA hinzu

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Schritt 3. Benachrichtigungsdatenwert in ViewControllerA empfangen

@objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }

Hier ist der vollständige Code für:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

        // add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    //MARK: Method for receiving Data through Post Notification 
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK:Set data for Passing Data through Post Notification
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }
}

Übergeben von Daten durch Block : Von ViewControllerB zu ViewControllerA

Schritt 1. Deklarieren Sie den Block in ViewControllerB

var authorisationCompletionBlock: ((Bool) -> ())? = {_ in}

Schritt 2. Legen Sie die Daten im Block in ViewControllerB fest

if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }

Schritt 3. Blockdaten in ViewControllerA empfangen

//Receiver Block
                controller!.authorizationCompletionBlock = { isGranted in
                    print("Data received from Block is :", isGranted)
                }

Hier ist der vollständige Code für:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:Method for receiving Data through Block
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if (segue.identifier == "showDetailSegue") {
                let controller = segue.destination as? ViewControllerB
                controller?.isSomethingEnabled = true

                //Receiver Block
                controller!.authorizationCompletionBlock = { isGranted in
                    print("Data received from Block is :", isGranted)
                }
            }
        }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    //MARK:Variable for Passing Data through Block
    var authorizationCompletionBlock:((Bool)->())? = {_ in}

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK:Set data for Passing Data through Block
        if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
    }
}

Eine vollständige Beispielanwendung finden Sie auf meinem GitHub. Bitte lassen Sie mich wissen, wenn Sie Fragen dazu haben.

swiftBoy
quelle
18

Übergeben von Daten zwischen FirstViewController an SecondViewController wie unten

Zum Beispiel:

FirstViewController String-Wert als

StrFirstValue = @"first";

Daher können wir diesen Wert in der zweiten Klasse mit dem folgenden Schritt übergeben

1> Wir müssen ein String-Objekt in der Datei SecondViewController.h erstellen

NSString *strValue;

2> Die Eigenschaft muss wie unten unter der Deklaration in der .h-Datei deklariert werden

@property (strong, nonatomic)  NSString *strSecondValue;

3> Sie müssen diesen Wert in der Datei FirstViewController.m unterhalb der Headerdeklaration synthetisieren

@synthesize strValue;

und in FirstViewController.h:

@property (strong, nonatomic)  NSString *strValue;

4> In FirstViewController, von welcher Methode wir zur zweiten Ansicht navigieren, schreiben Sie bitte den folgenden Code in diese Methode.

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

[secondView setStrSecondValue:StrFirstValue];

[self.navigationController pushViewController:secondView animated:YES ];
Chris Alan
quelle
Wie geben Sie Daten an den FirstViewController zurück, nachdem Sie sich im SecondViewController befunden haben?
Bruno
18

Ich trage derzeit mit einem Projekt namens MCViewFactory zu einer Open-Source-Lösung für dieses Problem bei, die hier zu finden ist:

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

Die Idee ist, das Absichtsparadigma von Android zu imitieren, indem eine globale Fabrik verwendet wird, um zu verwalten, welche Ansicht Sie betrachten, und "Absichten" verwendet werden, um Daten zwischen Ansichten zu wechseln und zu übertragen. Die gesamte Dokumentation befindet sich auf der Github-Seite, aber hier sind einige Highlights:

Sie richten alle Ihre Ansichten in .XIB-Dateien ein und registrieren sie beim App-Delegaten, während Sie die Factory initialisieren.

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

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

Jetzt erstellen Sie in Ihrer VC jedes Mal, wenn Sie zu einer neuen VC wechseln und Daten übergeben möchten, eine neue Absicht und fügen Daten zu ihrem Wörterbuch hinzu (savedInstanceState). Stellen Sie dann einfach die aktuelle Absicht der Fabrik ein:

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

Alle Ihre Ansichten, die diesem entsprechen, müssen Unterklassen von MCViewController sein, mit denen Sie die neue onResume: -Methode überschreiben und auf die von Ihnen übergebenen Daten zugreifen können.

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

Hoffe, einige von Ihnen finden diese Lösung nützlich / interessant.


quelle
Dann könnten alle Controller-Objekte alle registrierten Wörterbücher in einem beliebigen Bereich abrufen / festlegen? Stimmen Sie dies ab.
Itachi
15

Erstellen Sie die Eigenschaft als nächstes view controller .hund definieren Sie Getter und Setter.

Fügen Sie dies propertyin NextVC.h auf nextVC hinzu

@property (strong, nonatomic) NSString *indexNumber;

Hinzufügen

@synthesize indexNumber; in NextVC.m

Und zuletzt

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

vc.indexNumber=@"123";

[self.navigationController vc animated:YES];
Vivek Yadav
quelle
11

Es gibt unzählige Möglichkeiten, dies zu tun, und es ist wichtig, die richtige auszuwählen. Wahrscheinlich liegt eine der größten Architekturentscheidungen darin, wie der Modellcode in der gesamten App geteilt oder darauf zugegriffen wird.

Ich habe vor einiger Zeit einen Blog-Beitrag darüber geschrieben: Teilen von Modellcode . Hier ist eine kurze Zusammenfassung:

Freigegebene Daten

Ein Ansatz besteht darin, Zeiger auf die Modellobjekte zwischen Ansichtscontrollern zu teilen.

  • Brute-Force-Iteration auf View-Controllern (in Navigation oder Tab Bar Controller), um die Daten festzulegen
  • Legen Sie die Daten in prepareForSegue (falls Storyboards) oder init (falls programmatisch) fest.

Da die Vorbereitung auf den Übergang am häufigsten vorkommt, ist hier ein Beispiel:

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

Unabhängiger Zugang

Ein anderer Ansatz besteht darin, einen Bildschirm voller Daten gleichzeitig zu behandeln und anstatt die Ansichts-Controller miteinander zu koppeln, jeden Ansichts-Controller mit einer einzelnen Datenquelle zu koppeln, auf die sie unabhängig zugreifen können.

Die häufigste Art, wie ich dies gesehen habe, ist eine Singleton- Instanz. Wenn Ihr Singleton-Objekt also wäre DataAccess, könnten Sie in der viewDidLoad-Methode von UIViewController Folgendes tun:

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

Es gibt zusätzliche Tools, mit denen Sie auch Daten weitergeben können:

  • Schlüsselwertbeobachtung
  • NSNotification
  • Kerndatei
  • NSFetchedResultsController
  • Datenquelle

Kerndatei

Das Schöne an Core Data ist, dass es inverse Beziehungen hat. Wenn Sie also einem NotesViewController nur das Notizenobjekt geben möchten, können Sie dies tun, da es eine umgekehrte Beziehung zu etwas anderem wie dem Notizbuch hat. Wenn Sie im NotesViewController Daten zum Notizbuch benötigen, können Sie das Objektdiagramm wie folgt wiederherstellen:

let notebookName = note.notebook.name

Weitere Informationen hierzu finden Sie in meinem Blogbeitrag: Teilen von Modellcode

Korey Hinton
quelle
10

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;
Mohsin Sabasara
quelle
10

Die Delegierung ist die einzige Lösung, die solche Vorgänge ausführt, wenn Sie .xib-Dateien verwenden. Alle oben beschriebenen Antworten gelten jedoch storyboardfür .xibs-Dateien, für die Sie die Delegierung verwenden müssen. Das ist die einzige Lösung, die Sie können.

Eine andere Lösung besteht darin, das Singleton-Klassenmuster einmal zu initialisieren und in Ihrer gesamten App zu verwenden.

user2786888
quelle
10

Wenn Sie Daten von ViewControlerOne an ViewControllerTwo übergeben möchten, versuchen Sie diese ..

Tun Sie dies in ViewControlerOne.h

 @property (nonatomic, strong) NSString *str1;

Tun Sie dies in ViewControllerTwo.h

 @property (nonatomic, strong) NSString *str2;

Synthetisieren Sie str2 in ViewControllerTwo.m

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;

Führen Sie diese in ViewControlerOne.m aus

 - (void)viewDidLoad
 {
   [super viewDidLoad];

  // Data or string you wants to pass in ViewControllerTwo..
  self.str1 = @"hello world";

 }

Klicken Sie auf die Schaltflächen auf Ereignis.

-(IBAction)ButtonClicked
{ //Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
  ViewControllerTwo *objViewTwo=[self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
  obj.str2=str1;
  [self.navigationController pushViewController: objViewTwo animated:YES];
}

Führen Sie diese in ViewControllerTwo.m aus

- (void)viewDidLoad
{
 [super viewDidLoad];
  NSLog(@"%@",str2);
}
krushnsinh
quelle
10

Sie können Daten in App Delegate speichern, um über View Controller in Ihrer Anwendung darauf zuzugreifen. Sie müssen lediglich eine gemeinsam genutzte Instanz des App-Delegaten erstellen

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

Zum Beispiel

Wenn Sie a deklarieren NSArray object *arrayXYZ, können Sie in jedem View Controller über darauf zugreifenappDelegate.arrayXYZ

ak_tyagi
quelle
Es ist die Methode der Wahl für Hackathon
Hai Feng Kao
9

Wenn Sie Daten von einem zu einem anderen viewController senden möchten, haben Sie folgende Möglichkeiten:

Angenommen, wir haben viewController: ViewController und 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

Auf diese Weise können wir die Daten von einem Viewcontroller an einen anderen Viewcontroller übergeben ...

Sabs
quelle
8

Ich mag die Idee, dass Modellobjekte und Mock-Objekte, die auf NSProxy basieren, Daten festschreiben oder verwerfen, wenn die vom Benutzer ausgewählten Daten abgebrochen werden können.

Es ist einfach, Daten weiterzugeben, da es sich um ein einzelnes Objekt oder mehrere Objekte handelt. Wenn Sie beispielsweise den UINavigationController-Controller haben, können Sie den Verweis auf das Modell im Inneren behalten und alle Push-View-Controller können direkt vom Navigations-Controller darauf zugreifen.

Ben Sinclair
quelle
8

Ich habe viele Leute gesehen, die dies mit dieser didSelectRowAtPathMethode kompliziert haben . In meinem Beispiel verwende ich Core Data.

- (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 Codezeilen innerhalb der Methode und Sie sind fertig.

App Dev Guy
quelle
6

Es gibt viele Antworten auf diese Fragen, die viele verschiedene Möglichkeiten bieten, eine View-Controller-Kommunikation durchzuführen, die tatsächlich funktionieren würde, aber ich sehe nirgendwo erwähnt, welche tatsächlich am besten zu verwenden und welche zu vermeiden sind.

In der Praxis werden meiner Meinung nach nur wenige Lösungen empfohlen:

  • So geben Sie Daten weiter:
    • Überschreiben Sie die prepare(for:sender:)Methode zur UIViewControllerVerwendung eines Storyboards und von Segues
    • Übergeben Sie Daten durch einen Initialisierer oder durch Eigenschaften, wenn Sie View Controller-Übergänge durch Code ausführen
  • Daten rückwärts übergeben
    • Aktualisieren Sie den gemeinsam genutzten App-Status (den Sie mit einer der oben genannten Methoden zwischen View-Controllern weitergeben können).
    • Delegation verwenden
    • Verwenden Sie einen Abwicklungsbereich

Lösungen Ich empfehle, NICHT zu verwenden:

  • Direktes Verweisen auf den vorherigen Controller anstelle der Delegierung
  • Daten über einen Singleton teilen
  • Übergeben von Daten durch den App-Delegaten
  • Die Freigabe von Daten über den Benutzer erfolgt standardmäßig
  • Weitergabe von Daten über Benachrichtigungen

Obwohl diese Lösungen kurzfristig funktionieren, führen sie zu viele Abhängigkeiten ein, die die Architektur der App beeinträchtigen und später weitere Probleme verursachen.

Für Interessierte habe ich einige Artikel geschrieben, die diese Punkte ausführlicher behandeln und die verschiedenen Nachteile hervorheben:

Matteo Manferdini
quelle