Programowe tworzenie segue

202

Mam wspólne, UIViewControllerże cały mój czas, UIViewsControllersaby ponownie wykorzystać niektóre typowe operacje.

Chcę ustawić segue na tym „wspólnym” UIViewController, aby wszystkie pozostałe UIViewControllersdziedziczyły.

Próbuję dowiedzieć się, jak to zrobić programowo.

Wydaje mi się, że pytanie może dotyczyć również tego, jak ustawić seguedla siebie wszystko UIViewControllersbez wchodzenia na planszę i robienia ich ręcznie.

Tiago Veloso
źródło

Odpowiedzi:

169

Z definicji segue nie może tak naprawdę istnieć niezależnie od scenorysu. To nawet nie w imię klasy: UIStoryboardSegue. Nie tworzysz sekwensów programowo - tworzy je środowisko wykonawcze scenariuszy. Zwykle możesz wywołać performSegueWithIdentifier:kod kontrolera widoku, ale zależy to od tego, że w scenorysie jest już skonfigurowany segue.

Myślę jednak, że pytasz, w jaki sposób możesz utworzyć metodę we wspólnym kontrolerze widoku (klasie bazowej), która przejdzie do nowego kontrolera widoku i zostanie odziedziczona przez wszystkie klasy pochodne. Możesz to zrobić, tworząc taką metodę w kontrolerze widoku klasy podstawowej:

- (IBAction)pushMyNewViewController
{
    MyNewViewController *myNewVC = [[MyNewViewController alloc] init];

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

a następnie w klasie pochodnej wywołaj tę metodę po kliknięciu odpowiedniego przycisku lub wybraniu wiersza tabeli lub cokolwiek innego.

jonkroll
źródło
4
Dzięki. Szkoda, że ​​nie możemy tego zrobić programowo. Naprawdę poprawiłoby to jakość kodu źródłowego (zawsze mniejsze powielanie jest dobre). Spróbuję z twoją sugestią.
Tiago Veloso,
2
@jonkroll czy można wywołać / wykonać segue z instrukcji switch, tzn. na podstawie jakiego indeksu mam?
codejunkie
5
@codejunkie: Tak, możesz to zrobić. Użyłbyś UIViewControllermetody nazwanej performSegueWithIdentifier:sender:do tego.
jonkroll
2
Tworzę i wykonuję programowo segue (zobacz moją odpowiedź). Coś jest nie tak z moim kodem, jeśli Twoja odpowiedź jest poprawna?
Jean-Philippe Pellet
13
Aktualizacja dla iOS 6+: UIView„s presentModalViewController:animated:jest przestarzała. Z dokumentów - (Przestarzałe w iOS 6.0. Użyj presentViewController: animated: complete: zamiast.)
użytkownik
346

Myślałem, że dodam inną możliwość. Jedną z rzeczy, które możesz zrobić, jest połączenie dwóch scen w serii ujęć za pomocą segue, który nie jest dołączony do akcji, a następnie programowe wyzwolenie tego segmentu w kontrolerze widoku. Sposób, w jaki to robisz, polega na tym, że musisz przeciągnąć ikonę właściciela pliku na dole sceny scenorysu, która jest sceną segregowania, i przeciągnąć w prawo do sceny docelowej. Wrzucę obraz, aby pomóc wyjaśnić.

wprowadź opis zdjęcia tutaj

Pojawi się wyskakujące okienko dla „Manual Segue”. Jako typ wybrałem Push. Naciśnij mały kwadrat i upewnij się, że jesteś w inspektorze atrybutów. Podaj identyfikator, którego będziesz używać, aby się do niego odwoływać w kodzie.

wprowadź opis zdjęcia tutaj

Ok, następnie zamierzam segregować za pomocą przycisku programowanego paska narzędzi. W viewDidLoad lub gdzieś indziej utworzę element przycisku na pasku nawigacyjnym z tym kodem:

UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:@"Buttonize"
                                                                    style:UIBarButtonItemStyleDone
                                                                   target:self
                                                                   action:@selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = @[buttonizeButton];

Ok, zauważ, że selektor jest przyciskany Przyciskiem Przycisku :. Napisz więc metodę void dla tego przycisku, a w tej metodzie wywołasz segue w następujący sposób:

-(void)buttonizeButtonTap:(id)sender{
    [self performSegueWithIdentifier:@"Associate" sender:sender];
    }

Parametr nadawca jest wymagany do zidentyfikowania przycisku, gdy wywoływane jest przygotowanieForSegue. preparForSegue to metoda ramowa, w której utworzysz instancję swojej sceny i przekażesz jej wartości potrzebne do jej wykonania. Oto jak wygląda moja metoda:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"Associate"])
    {
        TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
        translationQuizAssociateVC.contentID = self.contentID;
        translationQuizAssociateVC.index = self.index;
        translationQuizAssociateVC.content = self.content;
    }
}

Ok, właśnie go przetestowałem i działa. Mam nadzieję, że to ci pomoże.

smileBot
źródło
@MichaelRowe jak to eliminuje potrzebę segues? Widzę, że nadal musisz przeciągać i upuszczać Storyboard do kontrolera docelowego.
aherrick
@MichaelRowe nie eliminuje to potrzeby używania segu. Pozwala to na podział między kontrolerami widoku, które mają wbudowany kod zamiast konstruktora interfejsu.
Matthew
@Matt to po prostu kompletnie przemyślenie sposobu, w jaki konfiguruję aplikację ... Po całkowitym przepisaniu całego interfejsu użytkownika nie używam już żadnych znaków ..
Michael Rowe
@ Cocoanut Dostaję błąd, ponieważ „Aplikacja próbowała przedstawić modalnie aktywnego kontrolera” wszelka pomoc w tym zakresie ..
Bala
1
Ręczny segment „Push” jest przestarzały, użyj „Show”. Ta odpowiedź zawiera więcej szczegółów. @smileBot zaktualizuj odpowiedź.
NAbbas,
81

Używam tego kodu do tworzenia instancji mojej niestandardowej podklasy segue i uruchamiania jej programowo. Wydaje się, że działa. Coś z tym nie tak? Jestem zdziwiony, czytając wszystkie pozostałe odpowiedzi, mówiąc, że nie można tego zrobić.

UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:@"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];
Jean-Philippe Pellet
źródło
4
Co znajduje się w MyCustomSegue?
Victor Engel
3
Jest to niestandardowa podklasa UIStoryboardSegue.
Jean-Philippe Pellet
7
@MarkAmery Wiele osób (w tym ja) unika używania scenariuszy. Trudno je scalić i nie ma czasu sprawdzania, czy identyfikator, który przekazuję, performSegueWithIdentifier:jest naprawdę zdefiniowany w scenariuszu. Unikam wszystkich problemów, jeśli sam tworzę segue.
Jean-Philippe Pellet
3
Zgadzam się z Jean-Philippe. Zarządzanie scenorysem jest uciążliwe. Oczywiście łatwo jest przejść przez przeglądanie kilku widoków i dodać tutaj segue i segue, ale zarządzanie 6 widokami z 16 sekwencjami zdefiniowanymi w XML, gdy masz trzech programistów, którzy majstrują przy tym, jest straszne. W każdym razie chodzi o to, że: kod daje ci kontrolę, xml generowany przez xcode nie.
Krystian
3
Widzę awarię w [segue perform] na iOS7, nie jestem pewien, czy ktoś jeszcze tego doświadcza.
Eric Chen
45

Przypuszczam, że odpowiedź na to pytanie została zaakceptowana, ale chciałbym tylko dodać kilka dodatkowych szczegółów.

To, co zrobiłem, aby rozwiązać problem, w którym przedstawiałem widok logowania jako pierwszy ekran, a następnie chciałem przejść do aplikacji, jeśli logowanie jest prawidłowe. Utworzyłem segue z kontrolera widoku logowania do kontrolera widoku głównego i nadałem mu identyfikator taki jak „mój identyfikator”.

Następnie po sprawdzeniu całego kodu logowania, czy login był prawidłowy, zadzwonię

[self performSegueWithIdentifier: @"myidentifier" sender: self];

Moje największe nieporozumienie polegało na tym, że próbowałem włożyć segue do guzika i przerwać segregację, gdy tylko zostanie znaleziona.

qrikko
źródło
4
Jak napisałem jako inny komentarz: Tworzyłem i wykonuję niestandardowe sekwencje programowo (patrz moja odpowiedź).
Jean-Philippe Pellet,
32

Musisz połączyć swój kod z UIStoryboardtym, którego używasz. Upewnij się, że idziesz do YourViewController w swoim UIStoryboard, kliknij ramkę wokół niego, a następnie ustaw jego identifierpole na NSStringwywołanie w kodzie.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];
Jeff Grimes
źródło
1
Rozumiem, ale co, jeśli viewController, który chcę przedstawić, jest osadzony w Nawigatorze nawigacji w scenorysie? Z tego, co mogę znaleźć, mogę zainicjować NavigationController, aby go osadzić, ale w scenorysie mam już konfigurację push push dla widoku, który należy przedstawić.
jhilgert00
1
możesz to rozwinąć? Myślę, że mam taki problem, ale nie mogę znaleźć sposobu, jak to zrobić / gdzie to zrobić ...
jesses.co.tt
1
Nawet to rozwiązanie jest poprawne. Chodzi o unikanie segregacji, ale pytanie dotyczy segregacji. W ten sposób możesz połączyć lub dokonać przejścia między dwiema scenami BEZ segue w scenorysach.
BootMaker,
16

Dla kontrolerów znajdujących się w serii ujęć.

jhilgert00 czy tego właśnie szukałeś?

-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:@"HomeController"];
[self.navigationController pushViewController: myController animated:YES];

}

LUB...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];
użytkownik1723341
źródło
3

Chciałbym dodać wyjaśnienie ...

Powszechnym nieporozumieniem, które faktycznie miałem przez pewien czas, jest to, że prepareForSegue:sender:metoda wywołuje segment scenorysowy . Nie jest. Segment scenorysu zostanie wykonany, niezależnie od tego, czy zaimplementowano prepareForSegue:sender:metodę dla tego (odchodzącego od) kontrolera widoku.

Nauczyłem się tego z doskonałych wykładów Paula Hegarty'ego na iTunesU . Przepraszam, ale niestety nie pamiętam, który wykład.

Jeśli połączysz segue między dwoma kontrolerami widoku w serii ujęć, ale nie prepareForSegue:sender:wdrożysz metody, segue nadal będzie segregować do docelowego kontrolera widoku. Zostanie jednak segregowany do tego kontrolera widoku nieprzygotowanego.

Mam nadzieję że to pomoże.

andrewbuilder
źródło
3

Segmentów scenorysów nie należy tworzyć poza scenorysem. Będziesz musiał to załatwić, pomimo wad.

UIStoryboardSegue Reference wyraźnie stwierdza:

Nie tworzysz bezpośrednio obiektów segue. Zamiast tego środowisko wykonawcze scenorysu tworzy je, gdy musi wykonać podział między dwoma kontrolerami widoku. Nadal możesz programowo zainicjować segue za pomocą performSegueWithIdentifier: sender: metoda UIViewController, jeśli chcesz. Możesz to zrobić, aby zainicjować segue ze źródła, które zostało dodane programowo i dlatego nie jest dostępne w Konstruktorze interfejsów.

Nadal możesz programowo polecić scenorysowi przedstawienie kontrolera widoku za pomocą segue przy użyciu presentModalViewController:lub pushViewController:animated:wywołań, ale potrzebujesz instancji scenorysu.

Możesz wywołać UIStoryboardmetodę klasy s, aby uzyskać nazwaną scenorys z pakietem zero dla pakietu głównego.

storyboardWithName:bundle:

Cameron Lowell Palmer
źródło
2

Po pierwsze, załóżmy, że masz dwa różne widoki w serii ujęć i chcesz nawigować z jednego ekranu na drugi, więc wykonaj następujące kroki:

1). Zdefiniuj wszystkie swoje widoki za pomocą pliku klasy, a także identyfikatora scenorysu w inspektorze tożsamości.

2). Pamiętaj, aby dodać kontroler nawigacji do pierwszego widoku. Wybierz go w Storyboard, a następnie Editor> Embed In> Navigation Controller

3). W pierwszej klasie zaimportuj plik „secondClass.h”

#import "ViewController.h
#import "secondController.h"

4). Dodaj to polecenie w sekcji IBAction, która musi wykonać segue

secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
[self.navigationController pushViewController:next animated:YES];

5). @"second"to klasa kontrolera drugiego widoku, identyfikator scenorysu.

Sanket Chauhan
źródło
self.storyboardpowinno być:UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
masipcat
@masipcat i nazwa tablicy Story może zależeć od tego, jak skonfigurowałeś swój projekt Xcode, w moim przypadku był to „Main.storyboard”, więc użyłemstoryboardWithName:@"Main"
ammianus
@ sanket-chauhan, jeśli twój pierwszy kontroler nie jest osadzony w kontrolerze nawigacyjnym, możesz również wyświetlić następny widok za pomocą [self showDetailViewController:next sender:self];lub[self showViewController:next sender:self];
ammianus
1

Przeprojektowałem i wdrożyłem (ponownie) implementację argumentów UIStoryboard: https://github.com/acoomans/Segway

Za pomocą tej biblioteki można programowo definiować sekwencje (bez scenorysu).

Mam nadzieję, że to może pomóc.

acoomans
źródło
0

Kilka problemów, właściwie:

Po pierwsze, w przesłanym do nas projekcie segue nie ma identyfikatora „segue1”:

bez identyfikatora

Podaj ten identyfikator, jeśli jeszcze tego nie zrobiłeś.

Po drugie, gdy przesuwasz się z widoku tabeli do widoku tabeli, wywołujesz initWithNibName, aby utworzyć kontroler widoku. Naprawdę chcesz użyć instantiateViewControllerWithIdentifier.

jaydip
źródło
0

Oto przykładowy kod dla Creating a segue programmatically:

class ViewController: UIViewController {
    ...
    // 1. Define the Segue
    private var commonSegue: UIStoryboardSegue!
    ...
    override func viewDidLoad() {
        ...
        // 2. Initialize the Segue
        self.commonSegue = UIStoryboardSegue(identifier: "CommonSegue", source: ..., destination: ...) {
            self.commonSegue.source.showDetailViewController(self.commonSegue.destination, sender: self)
        }
        ...
    }
    ...
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 4. Prepare to perform the Segue
        if self.commonSegue == segue {
            ...
        }
        ...
    }
    ...
    func actionFunction() {
        // 3. Perform the Segue
        self.prepare(for: self.commonSegue, sender: self)
        self.commonSegue.perform()
    }
    ...
}
jqgsninimo
źródło
Dzwonisz self.prepare(for: self.commonSegue, sender: self)ze swojej metody akcji. Wtedy jaki jest sens porównywania if self.commonSegue == segue {...}w prepare(for:sender)metodzie?
nayem
@nayem: W prepare(for:sender:)można skonfigurować docelowy kontroler widoku przed jego wyświetleniem. Oczywiście możesz to również zrobić w actionFunction.
jqgsninimo
@nayem: Powodem, dla którego to robię, jest próba zachowania spójności z obsługą innych segu.
jqgsninimo