Jak przekazać preparForSegue: obiekt

357

Mam wiele adnotacji w widoku mapy (z rightCalloutAccessoryprzyciskami). Przycisk wykona przejście od tego mapviewdo a tableview. Chcę przekazać tableviewinny obiekt (który przechowuje dane) w zależności od tego, który przycisk objaśnienia został kliknięty.

Na przykład: (całkowicie wykonane)

  • adnotacja1 (Austin) -> przekaż dane obj 1 (dotyczy Austin)
  • adnotacja2 (Dallas) -> przekaż dane obj 2 (dotyczy Dallas)
  • adnotation3 (Houston) -> przekaż dane obj 3 i tak dalej ... (masz pomysł)

Jestem w stanie wykryć, który przycisk objaśnienia został kliknięty.

Używam prepareForSegue: do przekazania danych obj do miejsca docelowego ViewController. Ponieważ nie mogę zmusić tego wywołania do przyjęcia dodatkowego argumentu dla danych, których potrzebuję, jakie są eleganckie sposoby osiągnięcia tego samego efektu (dane dynamiczne obj)?

Wszelkie wskazówki będą mile widziane.

chichot
źródło

Odpowiedzi:

673

Po prostu chwyć referencję do kontrolera widoku docelowego prepareForSegue:metodą i przekaż tam potrzebne obiekty. Oto przykład ...

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

REWIZJA: Możesz także użyć performSegueWithIdentifier:sender:metody, aby aktywować przejście do nowego widoku na podstawie wyboru lub naciśnięcia przycisku.

Rozważmy na przykład, że mam dwa kontrolery widoku. Pierwszy zawiera trzy przyciski, a drugi musi wiedzieć, który z tych przycisków został naciśnięty przed przejściem. Możesz połączyć przyciski IBActionz kodem, który używa performSegueWithIdentifier:metody, takiej jak ta ...

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

EDYCJA: Aplikacja demo, którą pierwotnie załączyłem, ma teraz sześć lat, więc usunąłem ją, aby uniknąć nieporozumień.

Szymon
źródło
Dziękuję, ale chcę to ustawić [vc setMyObjectHere:object];dynamicznie. tj. obj1 dla button1, obj2 dla button2 Problem polega na tym, że nie mogę przekazać argumentu. Czy jest na to jakiś sposób?
chizzle
2
Zaktualizowałem swój post, pobierając przykład tego, o czym mówię.
Simon
to się udało! Wielkie dzięki. Na marginesie preparatForSegue: ma argument UIControl, który jest klasą nadrzędną UIButton (dzięki czemu może uzyskać tag): D
chizzle
Czy funkcja PrepForSegue jest wywoływana nawet wtedy, gdy naciskasz na kontroler nawigacyjny bez segregacji scenorysu?
zakdances
To jedna z najdokładniejszych odpowiedzi, jakie kiedykolwiek widziałem. Dobre próbki kodu, rozwiązuje problem, oferuje próbkę do pobrania ... wow. Jestem pod wrażeniem!
lewiguez
82

Czasami pomocne jest uniknięcie tworzenia zależności czasu kompilacji między dwoma kontrolerami widoku. Oto jak możesz to zrobić bez dbania o typ kontrolera widoku docelowego:

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

Tak długo, jak docelowy kontroler widoku deklaruje własność publiczną, np .:

@property (nonatomic, strong) MyData *myData;

możesz ustawić tę właściwość w poprzednim kontrolerze widoku, jak opisano powyżej.

Macondo2Seattle
źródło
12
To jest naprawdę kwestia opinii. Nie miałem (jeszcze) sytuacji, w której nie chciałbym kontrolować kontrolerów widoku „ściśle”, choć doceniam to, co mówisz i może to być ważne w odpowiednich okolicznościach.
Simon
2
@ Simon: tak, po prostu wybierasz najlepsze dla siebie podejście. Na przykład w aplikacji, nad którą teraz pracuję, moje podejście ma sens, ponieważ ciągle dodawam kontrolery widoku, które potrzebują tego samego obiektu danych. Możliwość połączenia ich za pomocą tylko segue i wiedza, że ​​otrzymają odpowiednie dane, jest bardzo wygodna.
Macondo2Seattle
10
To nie jest kwestia opinii, to po prostu źle :) Fraza „Akceptowana odpowiedź nie jest najlepszym sposobem na zrobienie tego” jest błędna. Powinien brzmieć „W niektórych przypadkach musisz to zrobić ...”
Fattie
2
Wolę metodę Simona. Jak powie mi to błędy w czasie kompilacji. Na przykład, jeśli przegapię deklarację myData w docelowym kontrolerze widoku, natychmiast się dowiem. Ale w twoim scenariuszu twoje podejście wydaje się dobre!
Abdurrahman Mubeen Ali
14
To podejście absolutnie tworzy zależność, ponieważ wymaga się, aby docelowy kontroler widoku miał setMyData:. To jest zależność. Fakt, że używasz selektora, aby uniknąć błędu kompilacji, powinien być traktowany jako słabość twojego podejścia, a nie korzyść. Szokuje mnie, jak wielu programistów straciło koncepcję, że błędy czasu kompilacji powinny być preferowane zamiast błędów czasu wykonywania.
Nate
21

W Swift 4.2 zrobiłbym coś takiego:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let yourVC = segue.destination as? YourViewController {
        yourVC.yourData = self.someData
    }
}
Remy Cilia
źródło
Czy musisz zadzwonić: `super.prepare (dla: segue, nadawca: nadawca)`?
Andrew K,
16

Mam klasę nadawcy , jak to

@class MyEntry;

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

@implementation MySenderEntry
@end

Używam tej klasy nadawcy do przekazywania obiektówprepareForSeque: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
źródło
jesteśmy przy użyciu prepareForSegue albo jesteśmy wdrożenia go i założenie jest prepareForSegue dostaje się nazywa czyli my nie trzeba zrobić coś podobnego[self prepareForSegue]
Miód
15

Natknąłem się na to pytanie, gdy próbowałem nauczyć się przesyłać dane z jednego kontrolera widoku do drugiego. Potrzebuję jednak czegoś wizualnego, aby pomóc mi się uczyć, więc ta odpowiedź stanowi uzupełnienie innych, które już tu są. Jest to nieco bardziej ogólne niż pierwotne pytanie, ale można je dostosować do pracy.

Ten podstawowy przykład działa w następujący sposób:

wprowadź opis zdjęcia tutaj

Pomysł polega na przekazaniu ciągu znaków z pola tekstowego w Pierwszym kontrolerze widoku do etykiety w drugim kontrolerze widoku.

Kontroler pierwszego widoku

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

}

Kontroler drugiego widoku

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
    }

}

Pamiętaj by

  • Dokonaj segregacji, controlklikając przycisk i przeciągając go do kontrolera drugiego widoku.
  • Podłącz punkty dla UITextFieldi UILabel.
  • Ustaw pierwszy i drugi kontroler widoku na odpowiednie pliki Swift w IB.

Źródło

Jak wysyłać dane przez segue (szybki) (samouczek YouTube)

Zobacz też

Zobacz kontrolery: Przekazywanie danych do przodu i do tyłu (pełna odpowiedź)

Suragch
źródło
5

Dla Swift użyj tego,

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
źródło
4

Zaimplementowałem bibliotekę z kategorią na UIViewController, która upraszcza tę operację. Zasadniczo ustawiasz parametry, które chcesz przekazać, w NSDictionary powiązanym z elementem interfejsu użytkownika, który wykonuje segment. Działa również z ręcznymi sekwencjami.

Na przykład możesz to zrobić

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

do ręcznego segue lub utwórz przycisk z segue i użyj

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

Jeśli kontroler widoku docelowego nie jest zgodny z kluczem klucz-wartość, nic się nie dzieje. Działa również z kluczowymi wartościami (przydatny przy odwijaniu sekwencji). Sprawdź tutaj https://github.com/stefanomondino/SMQuickSegue

Stefano Mondino
źródło
2

Moje rozwiązanie jest podobne.

// 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
źródło
1

Po prostu użyj tej funkcji.

 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
źródło
0

Użyłem tego rozwiązania, aby móc zachować wywołanie segue i transmisję danych w ramach tej samej funkcji:

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

Przypadek użycia może wyglądać następująco:

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

Wydaje mi się, że to działa, jednak nie jestem w 100% pewien, że funkcje wykonywania i przygotowywania są zawsze wykonywane w tym samym wątku.

dannrob
źródło