Come passare preparForSegue: un oggetto


358

Ho molte annotazioni in una visualizzazione mappa (con rightCalloutAccessorypulsanti). Il pulsante eseguirà un seguito da questo mapviewa un tableview. Voglio passare tableviewun oggetto diverso (che contiene dati) a seconda del pulsante di richiamo selezionato.

Ad esempio: (totalmente inventato)

  • annotation1 (Austin) -> pass data obj 1 (rilevante per Austin)
  • annotation2 (Dallas) -> pass data obj 2 (rilevante per Dallas)
  • annotation3 (Houston) -> passa i dati obj 3 e così via ... (ottieni l'idea)

Sono in grado di rilevare quale pulsante di richiamo è stato fatto clic.

Sto usando prepareForSegue: per passare i dati obj alla destinazione ViewController. Dal momento che non posso fare in modo che questa chiamata prenda un argomento in più per l'oggetto dati richiesto, quali sono alcuni modi eleganti per ottenere lo stesso effetto (oggetto dinamico dati)?

Qualsiasi suggerimento sarebbe apprezzato.


Risposte:


674

Basta prendere un riferimento al controller della vista di destinazione nel prepareForSegue:metodo e passare tutti gli oggetti necessari. Ecco un esempio ...

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

REVISIONE: è inoltre possibile utilizzare performSegueWithIdentifier:sender: metodo per attivare la transizione a una nuova vista in base alla selezione o alla pressione di un pulsante.

Ad esempio, considera che avevo due controller di visualizzazione. Il primo contiene tre pulsanti e il secondo deve sapere quale di questi pulsanti è stato premuto prima della transizione. Puoi collegare i pulsanti a un IBActionnel tuo codice che utilizza il performSegueWithIdentifier:metodo, come questo ...

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

EDIT: l'applicazione demo che avevo originariamente allegato ora ha sei anni, quindi l'ho rimossa per evitare confusione.


Grazie ma voglio impostarlo in [vc setMyObjectHere:object];modo dinamico. cioè obj1 per button1, obj2 per button2 Il problema è che non riesco a passare un argomento. C'è un modo per aggirare questo?
Chizzle

2
Ho aggiornato il mio post con un esempio scaricabile di ciò di cui sto parlando.
Simone,

che ha funzionato! Grazie mille. Come nota a margine preparForSegue: ha un argomento UIControl che è una classe genitrice di UIButton (quindi in grado di ottenere il tag): D
Chizzle

PreparForSegue viene chiamato anche quando segui semplicemente un controller di navigazione senza uno storyboard?
zakdances,

Questa è una delle risposte più complete che abbia mai visto. Buoni esempi di codice, risolve il problema, offre esempi scaricabili ... wow. Sono impressionato!
lewiguez,

82

A volte è utile evitare di creare una dipendenza in fase di compilazione tra due controller di visualizzazione. Ecco come puoi farlo senza preoccuparti del tipo di controller della vista di destinazione:

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

Finché il controller della vista di destinazione dichiara una proprietà pubblica, ad esempio:

@property (nonatomic, strong) MyData *myData;

puoi impostare questa proprietà nel controller della vista precedente come ho descritto sopra.


12
Questa è davvero una questione di opinione. Non ho (ancora) avuto una situazione in cui non volevo controllare i controllori della vista "strettamente", anche se apprezzo quello che stai dicendo e potrebbe essere importante nelle giuste circostanze.
Simon,

2
@Simon: sì, hai appena scelto l'approccio che è meglio per te. Nell'app su cui sto lavorando ora, ad esempio, il mio approccio ha molto senso poiché continuo ad aggiungere controller di visualizzazione che richiedono lo stesso oggetto dati. Essere in grado di collegarli con solo un seguito e sapere che otterranno i dati giusti è super conveniente.
Macondo2Seattle,

10
Non è una questione di opinione, è solo sbagliato :) La frase "La risposta accettata non è il modo migliore per farlo" è sbagliata. Dovrebbe essere visualizzato "In alcuni casi, è necessario eseguire questa operazione ..."
Fattie,

2
Preferisco il metodo di Simon. Come mi dirà gli errori in fase di compilazione. Ad esempio, se ho perso la dichiarazione di myData nel controller della vista di destinazione, lo saprò immediatamente. Ma per il tuo scenario, il tuo approccio sembra buono!
Abdurrahman Mubeen Ali,

14
Questo approccio crea assolutamente una dipendenza, poiché è necessario che il controller della vista di destinazione disponga di setMyData:. Questa è una dipendenza. Il fatto che tu usi un selettore per evitare l'errore di compilazione dovrebbe essere trattato come una debolezza del tuo approccio, non un vantaggio. È scioccante per me quanti sviluppatori abbiano perso l'idea che gli errori di compilazione dovrebbero essere preferiti agli errori di runtime.
Nate,

21

In Swift 4.2 farei una cosa del genere:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let yourVC = segue.destination as? YourViewController {
        yourVC.yourData = self.someData
    }
}

Devi chiamare: `super.prepare (per: segue, mittente: mittente)`?
Andrew K,

16

Ho una classe mittente , come questa

@class MyEntry;

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

@implementation MySenderEntry
@end

Uso questa classe mittente per passare oggetti aprepareForSeque: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;
    }
}

lo stiamo usando prepareForSegue o lo stiamo implementando e il presupposto è readyForSegue si chiama se stesso cioè non abbiamo bisogno di fare qualcosa del genere[self prepareForSegue]
Honey

15

Mi sono imbattuto in questa domanda quando stavo cercando di imparare come passare i dati da un View Controller a un altro. Ho bisogno di qualcosa di visivo per aiutarmi ad imparare, quindi questa risposta è un supplemento agli altri già qui. È un po 'più generale della domanda originale ma può essere adattato per funzionare.

Questo esempio di base funziona in questo modo:

inserisci qui la descrizione dell'immagine

L'idea è di passare una stringa dal campo di testo nel controller della prima vista all'etichetta nel controller della seconda vista.

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

}

Controller di seconda vista

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
    }

}

Ricordati di

  • Effettuare il seguito controlfacendo clic sul pulsante e trascinandolo sul controller Second View.
  • Collegare le prese per il UITextFielde il UILabel.
  • Impostare il primo e il secondo controller di visualizzazione sui file Swift appropriati in IB.

fonte

Come inviare i dati tramite segue (rapido) (tutorial di YouTube)

Guarda anche

Visualizza controller: passare i dati in avanti e passare i dati indietro (risposta più completa)


5

Per Swift usare questo,

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

    if(segueID! == "yourSegueName"){

        var yourVC:YourViewController = segue.destinationViewController as YourViewController

        yourVC.objectOnYourVC = setObjectValueHere!

    }
}

4

Ho implementato una libreria con una categoria su UIViewController che semplifica questa operazione. Fondamentalmente, si impostano i parametri che si desidera trasferire in un NSDictionary associato all'elemento dell'interfaccia utente che sta eseguendo i seguenti. Funziona anche con segues manuali.

Ad esempio, puoi farlo

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

per un manuale segue o crea un pulsante con un segue e usa

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

Se il controller di visualizzazione della destinazione non è conforme alla codifica del valore-chiave per una chiave, non accade nulla. Funziona anche con valori-chiave (utile per svolgere segues). Dai un'occhiata qui https://github.com/stefanomondino/SMQuickSegue


2

La mia soluzione è simile

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

1

Usa questa funzione.

 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

}

0

Ho usato questa soluzione in modo da poter mantenere l'invocazione dei seguenti e la comunicazione dei dati all'interno della stessa funzione:

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

Un caso d'uso sarebbe qualcosa del tipo:

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

Questo sembra funzionare per me, tuttavia, non sono sicuro al 100% che le funzioni di esecuzione e preparazione siano sempre eseguite sullo stesso thread.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.