0

Lets say i have a ViewController with a textField and a button.

I want to use an unwind segue so i could get the information of the textField to my other viewController after i clicked the button.

I want to use the PrepareForSegue method so i could save the text from the textField in a property before i"m "unwiding".

How do i set an identifier to my segue manually? If it was a bar Button item i could use the IB to set the identifier ( to "Save" for example) and then use it. This is not the case, just a regular button.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Yevgeni
  • 1,533
  • 17
  • 32

2 Answers2

4

It is not possible to create segues programmatically. They cannot exist without storyboards.

See this question.

Community
  • 1
  • 1
Michal
  • 15,429
  • 10
  • 73
  • 104
  • Thanks, i sew it. Thought maybe things changed since 2012 in xcode5. Guess not. Thanks anyway. – Yevgeni Apr 09 '14 at 12:18
  • 2
    Maybe you should check this answer as correct then. – Marcal Apr 09 '14 at 17:04
  • You can create segues programmatically. I showed how in my answer below, and I've demonstrated this in a video on YouTube at: – James Bush Dec 18 '16 at 07:36
  • @JamesBush thanks for the heads up! Who would have guessed that 2,5 years later it would be possible! – Michal Dec 18 '16 at 09:47
  • @JamesBush I am going to ignore your statement with "others..." in order to keep this professional and about code only. – Michal Dec 21 '16 at 16:40
  • @JamesBush and actually my answer still holds. Segues are not to be created without a storyboard. Hence the name **UIStoryboardSegue**. You can read more about them here: https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html – Michal Dec 21 '16 at 22:13
  • You are incorrect. Anything that can be created in a storyboard can be done without one. Period. – James Bush Dec 21 '16 at 22:14
  • @JamesBush well, two other duplicate questions seem to think otherwise. Perhaps you could revisit the documentation yet again: http://stackoverflow.com/questions/36216582/create-and-perform-segue-without-storyboards and http://stackoverflow.com/questions/9674685/creating-a-segue-programmatically – Michal Dec 21 '16 at 22:23
  • @JamesBush and yes, you are absolutely right! But it's not a **Storyboard Segue**. – Michal Dec 21 '16 at 22:24
  • All I know is I created a true segue without using Interface Builder, and it is now in my app, which uses a it to transition from an asset chooser to a player window in lieu of a simple view controller transition; that's because peek-and-pop does not work without it. (You can peek at the assets in the chooser via 3D Touch, and then pop it into a player view.) Peek-and-pop is technically a segue; you can peek, but cannot pop without calling the view controller transition in the performWithSegue method). This is simply a fact. My app is proof. Your talk and all other talk is just talk. Produce. – James Bush Dec 21 '16 at 22:31
  • @JamesBush you have not created a true segue. You presented a view controller by pushing it to the navigation controller by calling a method *navigationController presentViewController:*. I appreciate your input, but unfortunately it's not a segue, it's presenting a view controller. Segues are made in a storyboard so you **don't have to** do it like this yourself. – Michal Dec 21 '16 at 22:40
  • That is a true segue when called first by performWithSegue, in that presentViewController is normally called in the subsequent perform method if you set the segue up in IB. Apple's documentation says that, if you call the presentViewController yourself, that the perform method is not called unless you explicitly override it. It is absolute nonsense to say that a segue is different than what I did. It is the same thing, exactly, and to the letter. – James Bush Dec 22 '16 at 00:39
-2

You can easily add a segue programmatically (without using storyboards). In the source view controller header file:

@property (nonatomic, strong) UIStoryboardSegue *segue;

In the source view controller implementation file, set up the segue property accordingly:

self.segue = [UIStoryboardSegue segueWithIdentifier:[NSString stringWithFormat:@"%lu", indexPath.item] source:self destination:[PlayerViewController sharedPlayerViewController] performHandler:^{
       // Insert whatever; nothing is needed for a basic segue
    }];

Also, in the source view controller, add the destination view controller set up and transition in the prepareForSegue method:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// The code in this method both sets up the destination view controller and transitions to it; you could optionally do set up here only, and then transition in an overridden -(void)perform method. This way is more convenient.
     [(PlayerViewController *)segue.destinationViewController setView:[PlayerView sharedPlayerView]];
     PHAsset *phAsset = (PHAsset *)AppDelegate.assetsFetchResults[segue.identifier.integerValue];
     [AppDelegate.cacheManager requestAVAssetForVideo:phAsset options:AppDelegate.videoOptions resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
         [[PlayerView sharedPlayerView] addSubview:[PlayerControls sharedPlayerControls]];
         [[PlayerControls sharedPlayerControls] setDelegate:(PlayerViewController *)segue.destinationViewController];
         [[PlayerView sharedPlayerView] addSubview:[AppDelegate playerControlsLabel]];
         [(PlayerViewController *)segue.destinationViewController setupPlaybackForAsset:asset completion:^{
             [((AssetsCollectionViewController *)sender).navigationController presentViewController:(PlayerViewController *)segue.destinationViewController animated:TRUE completion:^{
             }];
         }];
     }];
}

Also, add this line anywhere in the source view controller implementation file to perform the segue:

[self prepareForSegue:self.segue sender:self];

The call to the prepareForSegue method can be made inside an IBAction handler and anywhere else.

Note that the code that presents the destination view controller references a view controller that is not set up in a storyboard; the project from which this code was taken does not use storyboards for anything. BUT THAT DOESN'T MATTER because the segue code will be the same.

Also note that you do not have to modify the destination view controller in any way, shape or form. To programmatically create an unwind segue (in other words, build on the one-way segue shown here):

@implementation RootNavigationController

- (UIStoryboardSegue*)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
    return [toViewController segueForUnwindingToViewController:toViewController fromViewController:fromViewController identifier:identifier];
}

@end

Now, that's what I've done in my own app; here's what someone else has done to create a segue without using the storyboard (vs. not having a storyboard at all):

- (void)presentSignupViewController {
    // Storyboard ID
    UIStoryboard *modalStoryboard = [UIStoryboard storyboardWithName:@"MyStoryboard" bundle:nil];
    UINavigationController *navController = [modalStoryboard instantiateViewControllerWithIdentifier:@"MySignupViewController"];
    MySignupViewController *controller = [navController viewControllers][0];

    // Configure your custom view controller, e.g. setting delegate
    controller.delegate = self;

    // Show VC
    navController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

    BlurryModalSegue *segue = [[BlurryModalSegue alloc] initWithIdentifier:@"SignupScene" source:self destination:navController];

    [segue perform];
}

The difference is only in the use of perform and prepareForSegue: when using no storyboard at all, you have to call your transition method of choice (push... or present...) in the performForSegue method; however, the transition method is called for you by the perform method otherwise.

<iframe width="853" height="480" src="https://www.youtube.com/embed/y8Fu2PEO2zo?rel=0&amp;showinfo=0" frameborder="0" allowfullscreen></iframe>
James Bush
  • 1,485
  • 14
  • 19
  • Actually, this approach is quite a hack-y one. The method `prepareForSegue` should **only** do preparation stuff such as data passing, animation activation, cleanup etc. But in your implementation, you are *performing the segue* within the `prepareForSegue`. That itself is a bad practice, because it's inconsistent with the design of the API. The method is called automatically after you call the method `performSegueWithIdentifier:`. So in no way you are simulating the behavior of a real segue. – Michal Dec 18 '16 at 11:35
  • There is no data passing, animation or cleanup—and, that's a real segue. There's no simulating anything; and, what is the design of the API? – James Bush Dec 18 '16 at 20:49
  • Well, exactly - there isn't. But that's exactly the only purpose a function `prepareForSegue` should serve for. In your implementation, you're actually *performing* the segue in `prepareForSegue` - your `navigationController presentViewController:` is the emulation of your segue. That is a wrong approach and creates inconsistency in your codebase.When you have storyboard based application with a segue, you call a segue simply by `self.performSegue...` and the function `prepareForSegue` gets called automatically - aka **not** *by you in your code*.That's the design of the API provided by Apple. – Michal Dec 18 '16 at 21:08
  • @Michal Unwind Segue, Sample Code, Apple Developer Connection: - (void)perform { if (self.unwind) { QuizContainerViewController *containerVC = (QuizContainerViewController*)[self.destinationViewController parentViewController]; [containerVC popToViewController:self.destinationViewController animated:YES]; } – James Bush Dec 19 '16 at 22:19
  • How is this related to anything? – Michal Dec 20 '16 at 06:28
  • @Michal That sample code snippet from Apple shows that they performed a segue via a view-controller transitioning method, having overridden the perform method in a UIStoryboardSegue subclass. I called the transition method in prepareForSegue; it's faster, and more efficient. Clearly, this means that what I did is not duplicative or wrong or hacky; rather, it is wholly appropriate to call it in either the perform or method, and may even necessary if the view controller instance is referenced by a local variable within the scope of prepareForSegue. – James Bush Dec 20 '16 at 06:45
  • Unfortunately you have missed the point of the example code from the snippet and I highly recommend that you go back and study the code more thoroughly. The method you are referring to (~ `-(void)perform`) where the unwind segue happens is in class `QuizContainerFadeViewControllerSegue : UIStoryboardSegue` which, as you see, is of type **UIStoryboardSegue**. Your `prepareForSegue` is in a **UIViewController**. That's what's wrong about it - you are mixing two methods together, spreading the responsibility across your application into multiple classes. – Michal Dec 20 '16 at 14:25
  • @Michal Nope, I see no sample code from you; and, yes, I stated that the perform method was overridden in a subclass of UIStoryBoardSegue, but that is immaterial to the point. You said you shouldn't use the presentViewController method in a segue; I showed you an example demonstrating that this is valid to use, and the s method Apple, in fact, uses. Segues are not hard... – James Bush Dec 21 '16 at 14:36
  • There is no sample code from me. I am referring to the Apple code you quoted above. Take a look at it one more time. I can't put URLs in comments, perhaps we could talk about this in chat. You also didn't say your method is overriding the perform one in the UIStoryboardSegue. On the contrary, you are saying *Also, in the source view controller, add the destination view controller set up and transition in the prepareForSegue method:*. – Michal Dec 21 '16 at 16:33
  • Also, please try to understand what I was saying before. It is a **very bad** practice, to call a segue in a view controller by performing method `[self prepareForSegue:self.segue sender:self];`. It's a wrong approach and only creates confusion for somebody else to read your code. That method is called *automatically* after a segue is called by `[self performSegueWithIdentifier:@"..." sender:self];`. – Michal Dec 21 '16 at 16:36