Programmgesteuertes Erstellen eines Segues

202

Ich habe eine Gemeinsamkeit UIViewController, die sich alle darauf UIViewsControllerserstreckt, einige gemeinsame Operationen wiederzuverwenden.

Ich möchte einen Abschnitt über dieses "Common" einrichten, UIViewControllerdamit alle anderen UIViewControllerserben.

Ich versuche herauszufinden, wie ich das programmgesteuert mache.

Ich denke, die Frage könnte auch sein, wie ich ein seguefür alle meine festlegen kann, UIViewControllersohne in das Storyboard zu gehen und sie von Hand zu machen.

Tiago Veloso
quelle

Antworten:

169

Per Definition kann ein Segue nicht wirklich unabhängig von einem Storyboard existieren. Es ist sogar dort im Namen der Klasse : UIStoryboardSegue. Sie erstellen Segues nicht programmgesteuert - es ist die Storyboard-Laufzeit, die sie für Sie erstellt. Normalerweise können performSegueWithIdentifier:Sie den Code Ihres View Controllers aufrufen. Dies setzt jedoch voraus, dass im Storyboard bereits ein Abschnitt eingerichtet ist, auf den verwiesen wird.

Ich denke, Sie fragen sich jedoch, wie Sie in Ihrem gemeinsamen Ansichts-Controller (Basisklasse) eine Methode erstellen können, die zu einem neuen Ansichts-Controller übergeht und von allen abgeleiteten Klassen geerbt wird. Sie können dies tun, indem Sie eine Methode wie diese für Ihren Basisklassen-Ansichtscontroller erstellen:

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

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

Rufen Sie dann in Ihrer abgeleiteten Klasse diese Methode auf, wenn Sie auf die entsprechende Schaltfläche klicken oder die Tabellenzeile ausgewählt ist oder was auch immer.

Jonkroll
quelle
4
Vielen Dank. Schade, dass wir das nicht programmgesteuert machen können. Dies würde die Qualität des Quellcodes erheblich verbessern (weniger Duplizierung ist immer gut). Ich werde Ihren Vorschlag ausprobieren.
Tiago Veloso
2
@jonkroll ist es möglich, einen Übergang von der switch-Anweisung aufzurufen / durchzuführen, dh basierend auf dem Index, den ich habe?
Codejunkie
5
@codejunkie: Ja, das kannst du machen. Sie würden die dafür genannte UIViewControllerMethode verwenden performSegueWithIdentifier:sender:.
Jonkroll
2
Ich habe Segue programmgesteuert erstellt und durchgeführt (siehe meine Antwort). Stimmt also etwas mit meinem Code nicht, wenn Ihre Antwort richtig ist?
Jean-Philippe Pellet
13
Update für iOS 6 +: UIView‚s presentModalViewController:animated:ist veraltet. Aus den Dokumenten - (In iOS 6.0 veraltet. Verwenden Sie presentViewController: animiert: Abschluss: stattdessen.)
Benutzer
346

Ich dachte, ich würde eine weitere Möglichkeit hinzufügen. Sie können unter anderem zwei Szenen in einem Storyboard mithilfe eines Segues verbinden, das nicht an eine Aktion angehängt ist, und dann den Segue programmgesteuert in Ihrem View Controller auslösen. Die Art und Weise, wie Sie dies tun, besteht darin, dass Sie vom Eigentümer-Symbol der Datei am unteren Rand der Storyboard-Szene, die die Segue-Szene darstellt, ziehen und nach rechts in die Zielszene ziehen müssen. Ich werde ein Bild einwerfen, um es zu erklären.

Geben Sie hier die Bildbeschreibung ein

Ein Popup wird für "Manual Segue" angezeigt. Ich habe Push als Typ gewählt. Tippen Sie auf das kleine Quadrat und stellen Sie sicher, dass Sie sich im Attributinspektor befinden. Geben Sie ihm eine Kennung, mit der Sie im Code darauf verweisen.

Geben Sie hier die Bildbeschreibung ein

Ok, als nächstes gehe ich mit einem programmgesteuerten Balkenschaltflächenelement über. In viewDidLoad oder anderswo erstelle ich ein Schaltflächenelement in der Navigationsleiste mit folgendem Code:

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

Ok, beachten Sie, dass der Selektor buttonizeButtonTap: ist. Schreiben Sie also eine void-Methode für diese Schaltfläche, und innerhalb dieser Methode rufen Sie den Segue wie folgt auf:

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

Der Absenderparameter ist erforderlich, um die Schaltfläche beim Aufruf von prepareForSegue zu identifizieren. prepareForSegue ist die Framework-Methode, mit der Sie Ihre Szene instanziieren und ihr alle Werte übergeben, die sie für ihre Arbeit benötigt. So sieht meine Methode aus:

- (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, habe es gerade getestet und es funktioniert. Hoffe es hilft dir.

smileBot
quelle
@MichaelRowe wie beseitigt dies die Notwendigkeit von Segues? Wie ich es sehe, müssen Sie noch auf Storyboard auf den Ziel-Controller ziehen und dort
ablegen
@MichaelRowe dies beseitigt nicht die Notwendigkeit von Segues. Auf diese Weise können Sie zwischen Ansichts-Controllern wechseln, die im Code und nicht im Interface Builder erstellt wurden.
Matthew
@Matt es geht eigentlich nur so, dass ich völlig überdenke, wie ich meine App einrichte ... Nach einem vollständigen Umschreiben der gesamten Benutzeroberfläche verwende ich keine
Michael Rowe
@cocoanut Ich erhalte die Fehlermeldung "Anwendung hat versucht, einen aktiven Controller modal darzustellen" jede Hilfe in Bezug auf diese ..
Bala
1
Manueller Segue "Push" ist veraltet, verwenden Sie "Show". Diese Antwort enthält weitere Details. @smileBot bitte aktualisieren Sie die Antwort.
NAbbas
81

Ich habe diesen Code verwendet, um meine benutzerdefinierte Segue-Unterklasse zu instanziieren und programmgesteuert auszuführen. Es scheint zu funktionieren. Stimmt etwas damit nicht? Ich bin verwirrt und lese alle anderen Antworten, die besagen, dass dies nicht möglich ist.

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
quelle
4
Was ist in MyCustomSegue?
Victor Engel
3
Es ist eine benutzerdefinierte Unterklasse von UIStoryboardSegue.
Jean-Philippe Pellet
7
@ MarkAmery Viele Leute (einschließlich mir) vermeiden die Verwendung von Storyboards. Sie sind schwer zusammenzuführen, und es gibt keine Überprüfung zur Kompilierungszeit, ob die ID, an die ich übergebe, performSegueWithIdentifier:wirklich im Storyboard definiert ist. Ich vermeide alle Probleme, wenn ich den Segue selbst erstelle.
Jean-Philippe Pellet
3
Ich stimme Jean-Philippe zu. Das Verwalten des Storyboards ist ein Problem. Natürlich ist es einfach, sich durch das Erstellen von wenigen Ansichten und das Hinzufügen eines Segues hier und eines Segues dort zu klicken, aber das Verwalten von 6 Ansichten mit 16 in XML definierten Segues, wenn Sie drei Entwickler haben, die alle damit herumspielen, ist schrecklich. Der Punkt ist jedenfalls: Code gibt Ihnen die Kontrolle, von xcode generierte XML-Dateien nicht.
Krystian
3
Ich sehe einen Absturz in [segue perform] in iOS7, nicht sicher, ob dies bei jemand anderem auftritt.
Eric Chen
45

Ich denke, dies wird beantwortet und akzeptiert, aber ich möchte nur ein paar weitere Details hinzufügen.

Was ich getan habe, um ein Problem zu lösen, bei dem ich eine Anmeldeansicht als ersten Bildschirm präsentierte und dann zur Anwendung wechseln wollte, wenn die Anmeldung korrekt war. Ich habe den Übergang vom Login-View-Controller zum Root-View-Controller erstellt und ihm eine Kennung wie "myidentifier" gegeben.

Nachdem ich den gesamten Anmeldecode überprüft hatte, ob die Anmeldung korrekt war, würde ich anrufen

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

Mein größtes Missverständnis war, dass ich versucht habe, den Segue auf einen Knopf zu drücken und den Segue zu unterbrechen, sobald er gefunden wurde.

qrikko
quelle
4
Wie ich als weiteren Kommentar schrieb: Ich habe programmgesteuert benutzerdefinierte Segmente erstellt und ausgeführt (siehe meine Antwort).
Jean-Philippe Pellet
32

Sie müssen Ihren Code mit dem Code verknüpfen, den UIStoryboardSie verwenden. Stellen Sie sicher, dass Sie in YourViewController in Ihren gehen UIStoryboard, auf den Rand um ihn herum klicken und dann das identifierFeld auf ein Feld setzen, NSStringdas Sie in Ihrem Code aufrufen.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];
Jeff Grimes
quelle
1
Ich verstehe das, aber was ist, wenn der viewController, den ich präsentieren möchte, in einen NavigationController im Storyboard eingebettet ist? Nach allem, was ich finden kann, kann ich einen NavigationController initialisieren, um ihn einzubetten, aber im Storyboard habe ich bereits Push-Segues für die Ansicht eingerichtet, die präsentiert werden muss.
jhilgert00
1
Können Sie das näher erläutern? Ich denke, das ist das Problem, das ich habe, aber ich kann nicht finden, wie / wo ich das tun soll ...
jesses.co.tt
1
Auch diese Lösung ist richtig. Es geht darum, einen Übergang zu vermeiden, aber die Frage betrifft den Übergang. Auf diese Weise können Sie eine Verbindung herstellen oder einen Übergang zwischen zwei Szenen vornehmen, ohne in den Storyboards zu wechseln.
BootMaker
16

Für Controller, die sich im Storyboard befinden.

jhilgert00 ist das was du gesucht hast?

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

}

ODER...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];
user1723341
quelle
3

Ich möchte eine Klarstellung hinzufügen ...

Ein häufiges Missverständnis, das ich seit einiger Zeit hatte, ist, dass durch die prepareForSegue:sender:Methode ein Storyboard-Übergang ausgelöst wird . Es ist nicht. Ein Storyboard-Übergang wird ausgeführt, unabhängig davon, ob Sie eine prepareForSegue:sender:Methode für diesen (abweichenden) Ansichts-Controller implementiert haben .

Das habe ich aus Paul Hegartys hervorragenden iTunesU-Vorträgen gelernt . Ich entschuldige mich aber leider nicht daran welche Vorlesung.

Wenn Sie einen Abschnitt zwischen zwei Ansichts-Controllern in einem Storyboard verbinden, aber keine prepareForSegue:sender:Methode implementieren , wird der Abschnitt weiterhin mit dem Ziel-Ansichts-Controller getrennt. Es wird jedoch unvorbereitet zu diesem Ansichts-Controller übergehen.

Hoffe das hilft.

andrewbuilder
quelle
3

Storyboard-Segues dürfen nicht außerhalb des Storyboards erstellt werden. Sie müssen es trotz der Nachteile verkabeln.

In der UIStoryboardSegue-Referenz heißt es eindeutig:

Sie erstellen keine Segue-Objekte direkt. Stattdessen erstellt die Storyboard-Laufzeit sie, wenn eine Trennung zwischen zwei Ansichts-Controllern durchgeführt werden muss. Sie können einen Übergang weiterhin programmgesteuert mit der performSegueWithIdentifier: sender: -Methode von UIViewController initiieren, wenn Sie möchten. Sie können dies tun, um einen Übergang von einer Quelle zu initiieren, die programmgesteuert hinzugefügt wurde und daher im Interface Builder nicht verfügbar ist.

Sie können das Storyboard weiterhin programmgesteuert anweisen, einen Ansichts-Controller mithilfe eines Segue mit presentModalViewController:oder mit pushViewController:animated:Aufrufen anzuzeigen. Sie benötigen jedoch eine Storyboard-Instanz.

Sie können die UIStoryboardKlassenmethode s aufrufen , um ein benanntes Storyboard mit dem Bundle nil für das Hauptpaket zu erhalten.

storyboardWithName:bundle:

Cameron Lowell Palmer
quelle
2

Angenommen, Sie haben zwei verschiedene Ansichten im Storyboard und möchten von einem Bildschirm zum anderen navigieren. Gehen Sie also folgendermaßen vor:

1). Definieren Sie alle Ihre Ansichten mit der Klassendatei und der Storyboard-ID im Identitätsinspektor.

2). Stellen Sie sicher, dass Sie der ersten Ansicht einen Navigationscontroller hinzufügen. Wählen Sie es im Storyboard und dann Editor> Einbetten> Navigations-Controller

3). Importieren Sie in Ihrer ersten Klasse die "secondClass.h"

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

4). Fügen Sie diesen Befehl in die IBAction ein, die die Übergabe ausführen muss

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

5). @"second"ist die Secondview-Controller-Klasse, Storyboard-ID.

Sanket Chauhan
quelle
self.storyboardsollte sein:UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
masipcat
@masipcat und der Name des Storyboards hängen möglicherweise davon ab, wie Sie Ihr Xcode-Projekt eingerichtet haben. In meinem war es "Main.storyboard", also habe ich es verwendetstoryboardWithName:@"Main"
ammianus
@ sanket-chauhan Wenn Ihr erster Controller nicht in einen Navigations-Controller eingebettet ist, können Sie auch die nächste Ansicht mit [self showDetailViewController:next sender:self];oder[self showViewController:next sender:self];
ammianus
1

Ich habe die Open-Source-Implementierung der UIStoryboard-Segmente rückentwickelt und (neu) implementiert: https://github.com/acoomans/Segway

Mit dieser Bibliothek können Sie Segmente programmgesteuert definieren (ohne Storyboard).

Hoffe es kann helfen.

acoomans
quelle
0

Eigentlich ein paar Probleme:

Erstens trägt in diesem Projekt, das Sie für uns hochgeladen haben, der Segue nicht die Kennung "segue1":

keine Kennung

Sie sollten diese Kennung eingeben, falls Sie dies noch nicht getan haben.

Zweitens rufen Sie beim Wechseln von Tabellenansicht zu Tabellenansicht initWithNibName auf, um einen Ansichtscontroller zu erstellen. Sie möchten wirklich instantiateViewControllerWithIdentifier verwenden.

jaydip
quelle
0

Hier ist das Codebeispiel für 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
quelle
Sie rufen self.prepare(for: self.commonSegue, sender: self)von Ihrer Aktionsmethode aus auf. Was ist dann der Punkt des Vergleichs if self.commonSegue == segue {...}in prepare(for:sender)Methode?
Nayem
@nayem: In prepare(for:sender:)können Sie den Zielansichts- Controller konfigurieren, bevor er angezeigt wird. Natürlich können Sie es auch in tun actionFunction.
jqgsninimo
@nayem: Der Grund, warum ich das mache, ist zu versuchen, mit dem Umgang mit anderen Segues konsistent zu sein.
jqgsninimo