Datenübertragung zwischen View-Controllern

Ich bin neu bei iOS und Objective-C und dem gesamten MVC-Paradigma, und ich bleibe bei folgenden Themen:

Ich habe eine Ansicht, die als Dateneingabeformular dient, und ich möchte dem Benutzer die Möglichkeit geben, mehrere Produkte auszuwählen. Produkte werden in einer anderen Ansicht mit UITableViewController und ich habe mehrere Optionen UITableViewController .

Meine Frage ist: Wie kann ich Daten von einer Ansicht zu einer anderen übertragen? Ich werde die Auswahl in der UITableView in einem Array UITableView , aber wie übertrage ich diese zurück in die vorherige Ansicht des Dateneingabeformulars, damit sie mit anderen Daten in Core Data gespeichert werden kann, wenn das Formular gesendet wird?

Ich habe gesehen und gesehen, wie einige Leute im Anwendungsdelegierten ein Array deklarieren. Ich habe etwas über Singletones gelesen, aber ich verstehe nicht, was es ist, und ich habe etwas über das Erstellen eines Datenmodells gelesen.

Was ist der richtige Weg, und wie mache ich das?

1250
06 марта '11 в 15:43 2011-03-06 15:43 Matt Preis wird eingestellt 6. März 11 um 15:43 2011-03-06 15:43
ответ 41 Antworten
  • 1
  • 2

Diese Frage scheint bei stackoverflow sehr populär zu sein, also habe ich mich dazu entschlossen, die beste Antwort zu geben, um Leuten zu helfen, die in der iOS-Welt wie mir anfangen.

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

Daten weiterleiten

Senden Sie Daten von einem anderen Controller an den View-Controller. Sie müssen diese Methode verwenden, wenn Sie ein Objekt / einen Wert von einem View Controller zu einem anderen View Controller übertragen möchten, den Sie auf den Navigationsstapel klicken können.

In diesem Beispiel haben wir ViewControllerA und ViewControllerB

Um den BOOL Wert von ViewControllerA nach ViewControllerB zu ViewControllerB wir folgende BOOL aus.

  1. Erstellen Sie in ViewControllerB.h eine Eigenschaft für BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled; 
  2. In ViewControllerA Sie ViewControllerB davon in ViewControllerA setzen

     #import "ViewControllerB.h" 

    Wo möchten Sie beispielsweise die Präsentation herunterladen? didSelectRowAtIndex oder eine andere IBAction Sie müssen eine Eigenschaft in ViewControllerB bevor Sie im Navigationsstapel darauf klicken.

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

    Dies setzt isSomethingEnabled in ViewControllerB auf BOOL YES .

Daten mit Segues weiterleiten

Wenn Sie Storyboards verwenden, werden Sie höchstwahrscheinlich Segmente verwenden, und Sie benötigen dieses Verfahren, um Daten weiterzuleiten. Dies ist ähnlich wie oben, aber anstatt Daten zu übertragen, bevor Sie auf einen View-Controller klicken, verwenden Sie eine aufgerufene Methode

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

BOOL eine BOOL von ViewControllerA zu ViewControllerB zu ViewControllerB würden wir also Folgendes tun:

  1. Erstellen Sie in ViewControllerB.h eine Eigenschaft für BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled; 
  2. In ViewControllerA Sie ViewControllerB davon in ViewControllerA setzen

     #import "ViewControllerB.h" 
  3. Erstellen Sie einen ViewControllerA aus ViewControllerA in ViewControllerB auf dem Storyboard und geben Sie ihm eine ID. In diesem Beispiel nennen wir es "showDetailSegue"

  4. Dann müssen wir dem ViewControllerA eine Methode hinzufügen, die ViewControllerA wird, wenn ein Segment ausgeführt wird. Aus diesem Grund müssen wir ermitteln, welcher Aufruf aufgerufen wurde, und dann etwas tun. In unserem Beispiel prüfen wir "showDetailSegue" und wenn dies erledigt ist, übergeben wir unseren BOOL Wert an ViewControllerB

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

    Wenn Sie eigene Ansichten in den Navigationscontroller eingebettet haben, müssen Sie die obige Methode leicht ä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 isSomethingEnabled in ViewControllerB auf BOOL YES .

Datenübertragung zurück

Um Daten von ViewControllerB nach ViewControllerA zu übertragen, ViewControllerA Sie Protokolle und Delegaten oder Blöcke verwenden. Letztere können als lose gekoppelte Mechanismen für Rückrufe verwendet werden.

Zu diesem ViewControllerA machen wir den ViewControllerA Delegierten zum ViewControllerB . Dadurch kann ViewControllerB eine Nachricht an ViewControllerA sodass wir Daten ViewControllerA können.

Damit ViewControllerA Delegierter von ViewControllerB muss er das ViewControllerB Protokoll einhalten, das wir angeben müssen. Dies teilt ViewControllerA welche Methoden es implementieren soll.

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

     @class ViewControllerB; @protocol ViewControllerBDelegate <NSObject> - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; @end 
  2. Als Nächstes ViewControllerB.h Sie in ViewControllerB.h die delegate Eigenschaft ViewControllerB.m und in ViewControllerB.m synthetisieren

     @property (nonatomic, weak) id <ViewControllerBDelegate> delegate; 
  3. In ViewControllerB rufen wir den Nachrichtendelegierten auf, wenn wir den View-Controller ausfahren.

     NSString *itemToPassBack = @"Pass this value back to ViewControllerA"; [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack]; 
  4. Dies ist für ViewControllerB . ViewControllerA.h Sie ViewControllerA jetzt in ViewControllerA.h ViewControllerA importieren und dessen Protokoll zu befolgen.

     #import "ViewControllerB.h" @interface ViewControllerA : UIViewController <ViewControllerBDelegate> 
  5. In ViewControllerA.m implementieren ViewControllerA.m folgende Methode aus unserem Protokoll

     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item { NSLog(@"This was returned from ViewControllerB %@",item); } 
  6. Bevor Sie viewControllerB in den Navigationsstapel verschieben, müssen Sie ViewControllerB mitteilen, dass ViewControllerA sein Delegat ist. Andernfalls wird ein Fehler ViewControllerA .

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

Empfehlungen

  1. Verwenden der Delegierung zum Kommunizieren mit anderen Ansichtscontrollern im Programmierhandbuch des Viewcontrollers
  2. Delegierungsvorlage

NSNotification Center Dies ist eine weitere Möglichkeit, Daten zu übertragen.

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

Übertragen von Daten von einer Klasse zu einer anderen (die Klasse kann ein beliebiger Controller, ein Netzwerk- / Sitzungsmanager, eine Unterklasse von UIView oder eine andere Klasse sein)

Blöcke sind anonyme Funktionen.

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

Block definieren

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

Fügen Sie einen Blockhandler (Listener) hinzu, an dem Sie einen Wert benötigen (z. B. benötigen Sie eine API-Antwort in ControllerA oder Sie benötigen Daten von ContorllerB nach 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

1596
16 марта '12 в 14:39 2012-03-16 14:39 Die Antwort wird von Matt Price 16. März 12 um 14:39 2012-03-16 14:39 gegeben

Schnell

Hier und um StackOverflow gibt es unzählige Erklärungen, aber wenn Sie ein Anfänger sind, der nur versucht, etwas Wesentliches für die Arbeit zu finden, versuchen Sie es mit diesem YouTube-Tutorial (das half mir, endlich zu verstehen, wie man das macht).

Übertragen Sie die Daten zum nächsten View-Controller

Unten sehen Sie ein Beispiel, das auf Video basiert. Die Idee ist, die Zeichenfolge aus dem Textfeld im First View Controller auf das Label im Second View Controller zu übertragen.

2019

11 авг. Die Antwort wird von Suragch 11 Aug gegeben. 2015-08-11 09:35 '15 um 9:35 2015-08-11 09:35

M in MVC ist für "Model" gedacht, und im MVC-Paradigma besteht die Rolle der Modellklassen in der Verwaltung von Programmdaten. Das Modell ist der Ansicht entgegengesetzt - die Ansicht weiß, wie Daten angezeigt werden sollen, weiß jedoch nicht, was mit den Daten zu tun ist, während das Modell alles über die Arbeit mit den Daten weiß, aber nichts über die Anzeige. Modelle können komplex sein, müssen es aber nicht sein - ein Modell für Ihre Anwendung kann so einfach sein wie ein Array von Strings oder Wörterbüchern.

Die Rolle des Controllers besteht darin, zwischen der Ansicht und dem Modell zu vermitteln. Daher benötigen sie einen Verweis auf ein oder mehrere Ansichtsobjekte und ein oder mehrere Modellobjekte. Angenommen, Ihr Modell besteht aus einem Array von Wörterbüchern, wobei jedes Wörterbuch eine Zeile in Ihrer Tabelle darstellt. In der Stammansicht Ihrer Anwendung wird diese Tabelle angezeigt. Möglicherweise ist sie dafür verantwortlich, das Array aus einer Datei zu laden. Wenn der Benutzer beschließt, der Tabelle eine neue Zeile hinzuzufügen, drückt er 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 detaillierten View-Controller und gibt ihm ein neues Wörterbuch. Der Controller für die Detailansicht füllt das Wörterbuch aus und gibt es zurück. Das Wörterbuch ist bereits Teil des Modells, daher sollte nichts passieren.

122
06 марта '11 в 16:49 2011-03-06 16:49 Die Antwort wird gegeben Caleb 6. März 11 um 16:49 2011-03-06 16:49

Es gibt verschiedene Möglichkeiten, wie Daten in einer anderen Klasse in iOS abgerufen werden können. Zum Beispiel -

  • Direkte Initialisierung nach Zuweisung einer anderen Klasse.
  • Delegation - für die Datenübertragung zurück
  • Benachrichtigung - um Daten gleichzeitig auf mehrere Klassen zu übertragen
  • Speichern Sie in NSUserDefaults , um später darauf zugreifen zu können
  • Einzelunterricht
  • Datenbanken und andere Speichermechanismen wie plist usw.

Bei einem einfachen Szenario, bei dem ein Wert an eine andere Klasse übergeben wird, die in der aktuellen Klasse verteilt ist, besteht die häufigste und bevorzugte Methode darin, die Werte direkt nach der Auswahl anzupassen. Dies geschieht wie folgt:

Wir können dies mit Hilfe zweier Controller - Controller1 und Controller2 - verstehen

Angenommen, Sie möchten ein Controller2-Objekt in der Controller1-Klasse erstellen und mit dem übergebenen String-Wert darauf klicken. Dies kann wie folgt durchgeführt werden:

 - (void)pushToController2 { Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passValue:@"String"]; [self pushViewController:obj animated:YES]; } 

Bei der Implementierung der Klasse Controller2 wird diese Funktion neben

 @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 mehrere Parameter verwenden, zum Beispiel:

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

Wenn Sie mehr als drei Parameter übergeben müssen, die sich auf eine gemeinsame Funktion beziehen, können Sie die Werte in der Modellklasse 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]; 

So kurz, wenn du willst -

 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

87
08 апр. die antwort ist borncrazy 08 apr. 2014-04-08 13:24 '14 um 13:24 2014-04-08 13:24

Nach weiteren Untersuchungen schien es, als seien Protokolle und Delegierte der richtige / bevorzugte Weg für Apple.

Am Ende habe ich dieses Beispiel verwendet.

Datenaustausch zwischen Dispatchern und anderen Objekten @iPhone Dev SDK

Es funktionierte gut und erlaubte mir, einen String und ein Array zwischen meinen Ansichten hin und her zu übergeben.

Danke für Ihre Hilfe

78
14 марта '11 в 0:20 2011-03-14 00:20 Die Antwort wird von Matt Price am 14. März 11 um 0:20 gegeben. 2011-03-14 00:20

Ich finde die einfachste und eleganteste Version mit fehlenden Blöcken. Lassen Sie den Namenscontroller, der die zurückgegebenen Daten als "A" erwartet, und den Viewcontroller als "B" zurückgeben. In diesem Beispiel möchten wir zwei Werte erhalten: zuerst von Typ1 und zweitens von Typ2.

Angenommen, wir verwenden Storyboard, installiert der erste Controller beispielsweise während der Vorbereitung eines Segments einen Rückmeldeblock:

 - (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 do some action after with returned values action1(value1); action2(value2); }; } } 

und der "B" -Controller sollte die Callback-Eigenschaft BViewController.h deklarieren:

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

In der BViewController.m-Implementierungsdatei sollten wir, nachdem wir die gewünschten Werte für den Rückruf erhalten haben, Folgendes aufrufen:

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

Dabei ist zu beachten, dass die Verwendung eines Blocks häufig das Verwalten von starken und __ Jahrhundert-Links erfordert, wie sie hier erläutert werden.

61
14 окт. Antwort von Leszek Zarna am 14. Oktober. 2013-10-14 21:11 '13 um 21:11 2013-10-14 21:11

Viele Antworten enthalten einige gute Informationen, aber niemand berührt die Frage vollständig.

Die Frage stellt eine Frage zur Übertragung von Informationen zwischen den Disponenten. In diesem speziellen Beispiel wird eine Frage zur Übertragung von Informationen zwischen Ansichten gestellt. Angesichts der selbstdefinierten Neuheit für iOS war das ursprüngliche Poster wahrscheinlich zwischen viewControllers und nicht zwischen Ansichten gemeint (ohne Teilnahme an ViewControllers). Es scheint, dass alle Antworten auf zwei Ansichtscontroller fokussiert sind. Was aber, wenn sich die Anwendung so entwickelt, dass mehr als zwei Ansichtscontroller beim Informationsaustausch verwendet werden?

Das ursprüngliche Poster fragte auch nach Singletons und der Verwendung von AppDelegate . Diese Fragen müssen beantwortet werden.

Um anderen Personen zu helfen, sich diese Frage anzusehen, die eine vollständige Antwort wünscht, werde ich versuchen, sie zu beantworten.

Anwendungsskripte

Anstatt eine sehr hypothetische, abstrakte Diskussion zu führen, hilft es, konkrete Anwendungen zu haben. Um die Situation mit zwei Dispatch-Controllern und die Situation mit dem Controller aus mehr als zwei Gesichtspunkten zu bestimmen, werde ich zwei spezifische Anwendungsszenarien definieren.

Szenario 1: Maximal zwei Ansichtsmanager erfordern den Informationsaustausch. Siehe Diagramm 1.

In der Anwendung gibt es zwei Ansichtscontroller. Es gibt ein ViewControllerA (Dateneingabeformular) und ein View Controller B (Produktliste). Die in der Produktliste ausgewählten Elemente müssen mit den im Textfeld des Dateneingabeformulars angezeigten Elementen übereinstimmen. In diesem Fall müssen ViewControllerA und ViewControllerB direkt miteinander und mit keinen anderen View-Controllern kommunizieren.

Szenario 2 : Mehr als zwei View-Dispatcher sollten dieselbe Information verwenden. Siehe Abbildung 2.

Die Anwendung verfügt über vier View-Controller. Bei dieser Anwendung handelt es sich um ein tabulatorbasiertes Werkzeug zur Verwaltung des Home-Inventars. Die drei Ansichtsmanager repräsentieren unterschiedliche gefilterte Ansichten derselben Daten:

  • ViewControllerA - Luxusartikel
  • ViewControllerB - nicht versicherte Artikel
  • ViewControllerC - das gesamte Inventar
  • ViewControllerD - Neues Element hinzufügen

Jedes Mal, wenn ein einzelnes Element erstellt oder bearbeitet wird, muss es auch mit anderen Ansichtscontrollern synchronisiert werden. Wenn wir beispielsweise ein Boot zu ViewControllerD hinzufügen, aber noch nicht versichert sind, sollte das Boot erscheinen, wenn der Benutzer zu ViewControllerA (Luxury Items) und auch ViewControllerC (Gesamtes Inventar) wechselt, nicht aber, wenn der Benutzer zu ViewControllerB geht (nicht versichert) Elemente). Wir müssen nicht nur neue Elemente hinzufügen, sondern auch Elemente löschen (die von jedem der vier Ansichts-Controller aufgelöst werden können) oder vorhandene Elemente bearbeiten (die über "Neues Elementformular hinzufügen" zur Bearbeitung zugelassen werden können).

Da alle View-Controller dieselben Daten verwenden müssen, müssen alle vier View-Controller synchron bleiben. Daher muss für alle anderen View-Controller eine Verbindung bestehen, wenn ein View-Controller die zugrunde liegenden Daten ändert. Es sollte ziemlich offensichtlich sein, dass in diesem Szenario nicht jeder View-Controller direkt mit jedem anderen View-Controller kommunizieren soll. В случае, если это не очевидно, подумайте, было ли у нас 20 разных контроллеров представлений (а не только 4). Насколько сложно и подвержено ошибкам было бы уведомлять каждый из других 19 контроллеров представления в любое время, когда один контроллер просмотра внес изменения?

Решения: делегаты и шаблон наблюдателя, а также синглтоны