So übergeben Sie prepareForSegue: ein Objekt

358

Ich habe viele Anmerkungen in einer Kartenansicht (mit rightCalloutAccessorySchaltflächen). Die Schaltfläche führt einen Übergang von hier mapviewzu a durch tableview. Ich möchte tableviewein anderes Objekt (das Daten enthält) übergeben, je nachdem, auf welche Callout-Schaltfläche geklickt wurde.

Zum Beispiel: (komplett erfunden)

  • annotation1 (Austin) -> Daten übergeben obj 1 (relevant für Austin)
  • annotation2 (Dallas) -> Daten übergeben obj 2 (relevant für Dallas)
  • annotation3 (Houston) -> Daten übergeben obj 3 und so weiter ... (Sie haben die Idee)

Ich kann erkennen, auf welche Callout-Schaltfläche geklickt wurde.

Ich benutze prepareForSegue: um das Datenobjekt an das Ziel zu übergeben ViewController. Da ich diesen Aufruf nicht ausführen kann, um ein zusätzliches Argument für das von mir benötigte Datenobjekt zu verwenden, was sind einige elegante Möglichkeiten, um den gleichen Effekt zu erzielen (dynamisches Datenobjekt)?

Jeder Tipp wäre dankbar.

chizzle
quelle

Antworten:

674

Nehmen Sie einfach einen Verweis auf den Zielansichts-Controller in der prepareForSegue:Methode und übergeben Sie alle benötigten Objekte an diese. Hier ist ein Beispiel ...

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Make sure your segue name in storyboard is the same as this line
    if ([[segue identifier] isEqualToString:@"YOUR_SEGUE_NAME_HERE"])
    {
        // Get reference to the destination view controller
        YourViewController *vc = [segue destinationViewController];

        // Pass any objects to the view controller here, like...
        [vc setMyObjectHere:object];
    }
}

REVISION: Sie können auch die performSegueWithIdentifier:sender:Methode verwenden, um den Übergang zu einer neuen Ansicht basierend auf einer Auswahl oder einem Tastendruck zu aktivieren.

Stellen Sie sich zum Beispiel vor, ich hätte zwei View-Controller. Die erste enthält drei Tasten und die zweite muss wissen, welche dieser Tasten vor dem Übergang gedrückt wurde. Sie können die Schaltflächen mit einer IBActionin Ihrem Code verbundenen performSegueWithIdentifier:Methode verbinden, die eine Methode wie diese verwendet ...

// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
    [self performSegueWithIdentifier:@"MySegue" sender:sender];
}

// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"MySegue"]) {

        // Get destination view
        SecondView *vc = [segue destinationViewController];

        // Get button tag number (or do whatever you need to do here, based on your object
        NSInteger tagIndex = [(UIButton *)sender tag];

        // Pass the information to your destination view
        [vc setSelectedButton:tagIndex];
    }
}

BEARBEITEN: Die Demo-Anwendung, die ich ursprünglich angehängt habe, ist jetzt sechs Jahre alt, daher habe ich sie entfernt, um Verwirrung zu vermeiden.

Simon
quelle
Vielen Dank, aber ich möchte [vc setMyObjectHere:object];dies dynamisch einstellen . dh obj1 für button1, obj2 für button2 Das Problem ist, dass ich kein Argument weitergeben kann. Gibt es einen Weg, dies zu umgehen?
Chizzle
2
Ich habe meinen Beitrag mit einem herunterladbaren Beispiel für das, worüber ich spreche, aktualisiert.
Simon
das hat funktioniert! Vielen Dank. Als Randnotiz prepareForSegue: hat ein UIControl-Argument, das eine übergeordnete Klasse von UIButton ist (wodurch das Tag
abgerufen werden kann
Wird prepareForSegue auch dann aufgerufen, wenn Sie nur auf einen Navigationscontroller ohne Storyboard-Übergang drücken?
Zakdances
Dies ist eine der gründlichsten Antworten, die ich je gesehen habe. Gute Codebeispiele, löst das Problem, bietet herunterladbare Beispiele ... wow. Ich bin beeindruckt!
Lewisuez
82

Manchmal ist es hilfreich zu vermeiden, dass eine Abhängigkeit zur Kompilierungszeit zwischen zwei Ansichtscontrollern erstellt wird. So können Sie dies tun, ohne sich um den Typ des Zielansichts-Controllers zu kümmern:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController respondsToSelector:@selector(setMyData:)]) {
        [segue.destinationViewController performSelector:@selector(setMyData:) 
                                              withObject:myData];
    } 
}

Solange Ihr Controller für die Zielansicht eine öffentliche Eigenschaft deklariert, z.

@property (nonatomic, strong) MyData *myData;

Sie können diese Eigenschaft im vorherigen Ansichts-Controller wie oben beschrieben festlegen.

Macondo2Seattle
quelle
12
Das ist wirklich Ansichtssache. Ich hatte (noch) keine Situation, in der ich die View-Controller nicht "streng" kontrollieren wollte, obwohl ich es zu schätzen weiß, was Sie sagen, und es unter den richtigen Umständen wichtig sein kann.
Simon
2
@ Simon: Ja, du wählst einfach den Ansatz, der für dich am besten ist. In der App, an der ich gerade arbeite, ist mein Ansatz beispielsweise sehr sinnvoll, da ich immer wieder Ansichts-Controller hinzufüge, die dasselbe Datenobjekt benötigen. Es ist sehr praktisch, sie mit nur einem Abschnitt zu verbinden und zu wissen, dass sie die richtigen Daten erhalten.
Macondo2Seattle
10
Es ist keine Ansichtssache, es ist einfach falsch :) Der Satz "Die akzeptierte Antwort ist nicht der beste Weg, dies zu tun" ist falsch. Es sollte lauten "In bestimmten Fällen müssen Sie dies tun ..."
Fattie
2
Ich bevorzuge Simons Methode. Da werden mir die Fehler beim Kompilieren mitgeteilt. Wenn ich beispielsweise die Deklaration von myData im Zielansichts-Controller verpasst habe, weiß ich es sofort. Aber für Ihr Szenario scheint Ihr Ansatz gut zu sein!
Abdurrahman Mubeen Ali
14
Dieser Ansatz erzeugt absolut eine Abhängigkeit, da der Zielansichts-Controller über eine verfügen muss setMyData:. Das ist eine Abhängigkeit. Die Tatsache, dass Sie einen Selektor verwenden, um den Kompilierungsfehler zu vermeiden, sollte als Schwäche Ihres Ansatzes und nicht als Vorteil betrachtet werden. Es ist schockierend für mich, wie viele Entwickler das Konzept verloren haben, dass Kompilierungszeitfehler Laufzeitfehlern vorgezogen werden sollten.
Nate
21

In Swift 4.2 würde ich so etwas machen:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let yourVC = segue.destination as? YourViewController {
        yourVC.yourData = self.someData
    }
}
Remy Cilia
quelle
Müssen Sie anrufen: `super.prepare (für: segue, Absender: Absender)`?
Andrew K
16

Ich habe einen Sender Klasse , wie diese

@class MyEntry;

@interface MySenderEntry : NSObject
@property (strong, nonatomic) MyEntry *entry;
@end

@implementation MySenderEntry
@end

Ich benutze diese Absenderklasse zum Übergeben von Objekten anprepareForSeque:sender:

-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
    MySenderEntry *sender = [MySenderEntry new];
    sender.entry = [_entries objectAtIndex:indexPath.row];
    [self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}

-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
        NSAssert([sender isKindOfClass:[MySenderEntry class]], @"MySenderEntry");
        MySenderEntry *senderEntry = (MySenderEntry*)sender;
        MyEntry *entry = senderEntry.entry;
        NSParameterAssert(entry);

        [segue destinationViewController].delegate = self;
        [segue destinationViewController].entry = entry;
        return;
    }

    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
        // ...
        return;
    }

    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
        // ...
        return;
    }
}
neoneye
quelle
wir verwenden prepareForSegue oder wir sind die Umsetzung sie und die Annahme ist , prepareForSegue selbst wir also aufgerufen wird , nicht benötigen , wie etwas zu tun[self prepareForSegue]
Honig
15

Ich bin auf diese Frage gestoßen, als ich versuchte zu lernen, wie Daten von einem View Controller an einen anderen übergeben werden. Ich brauche jedoch etwas Visuelles, um lernen zu können, daher ist diese Antwort eine Ergänzung zu den anderen, die bereits hier sind. Es ist etwas allgemeiner als die ursprüngliche Frage, kann aber an die Arbeit angepasst werden.

Dieses grundlegende Beispiel funktioniert folgendermaßen:

Geben Sie hier die Bildbeschreibung ein

Die Idee ist, eine Zeichenfolge aus dem Textfeld im First View Controller an die Beschriftung im Second View Controller zu übergeben.

First View Controller

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

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

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

}

Second View Controller

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
    }

}

Erinnere dich an

  • Machen Sie den Übergang, indem controlSie auf die Schaltfläche klicken und sie zum Second View Controller übertragen.
  • 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.

Quelle

So senden Sie Daten über segue (schnell) (YouTube-Tutorial)

Siehe auch

View Controller: Daten weiterleiten und Daten zurückgeben (ausführlichere Antwort)

Suragch
quelle
5

Für Swift verwenden Sie dies,

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var segueID = segue.identifier

    if(segueID! == "yourSegueName"){

        var yourVC:YourViewController = segue.destinationViewController as YourViewController

        yourVC.objectOnYourVC = setObjectValueHere!

    }
}
Mohammad Zaid Pathan
quelle
4

Ich habe eine Bibliothek mit einer Kategorie in UIViewController implementiert, die diesen Vorgang vereinfacht. Grundsätzlich legen Sie die Parameter fest, die Sie in einem NSDictionary übergeben möchten, das dem UI-Element zugeordnet ist, das die Übergabe ausführt. Es funktioniert auch mit manuellen Segues.

Zum Beispiel können Sie tun

[self performSegueWithIdentifier:@"yourIdentifier" parameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

für einen manuellen Übergang oder erstellen Sie eine Schaltfläche mit einem Übergang und verwenden Sie

[button setSegueParameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

Wenn der Zielansichts-Controller für einen Schlüssel nicht mit der Schlüsselwertcodierung kompatibel ist, geschieht nichts. Es funktioniert auch mit Schlüsselwerten (nützlich zum Abwickeln von Segues). Überprüfen Sie es hier https://github.com/stefanomondino/SMQuickSegue

Stefano Mondino
quelle
2

Meine Lösung ist ähnlich.

// In destination class: 
var AddressString:String = String()

// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   if (segue.identifier == "seguetobiddetailpagefromleadbidder")
    {
        let secondViewController = segue.destinationViewController as! BidDetailPage
        secondViewController.AddressString = pr.address as String
    }
}
AG
quelle
1

Verwenden Sie einfach diese Funktion.

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let index = CategorytableView.indexPathForSelectedRow
    let indexNumber = index?.row
    let VC = segue.destination as! DestinationViewController
   VC.value = self.data

}
Parth Barot
quelle
0

Ich habe diese Lösung verwendet, um den Aufruf des Segues und die Datenkommunikation in derselben Funktion zu halten:

private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?

func performSegue(withIdentifier identifier: String, sender: Any?, completion: @escaping (UIStoryboardSegue, Any?) -> Void) {
    self.segueCompletion = completion;
    self.performSegue(withIdentifier: identifier, sender: sender);
    self.segueCompletion = nil
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    self.segueCompletion?(segue, sender)
}

Ein Anwendungsfall wäre etwa:

func showData(id : Int){
    someService.loadSomeData(id: id) {
        data in
        self.performSegue(withIdentifier: "showData", sender: self) {
            storyboard, sender in
            let dataView = storyboard.destination as! DataView
            dataView.data = data
        }
    }
}

Dies scheint für mich zu funktionieren, ich bin jedoch nicht 100% sicher, dass die Funktionen zum Ausführen und Vorbereiten immer auf demselben Thread ausgeführt werden.

dannrob
quelle