city parking
city parking

Reputation: 3

How to perform segue after information from protocol is recieved

I have a button in a cell which calls a protocol that has data that needs to be passed to the view controller by the segue. The segue is happening through storyboard. My current code uses the shouldperformsegue to return no when the button is pressed as the first segue that happens does not have the data. Im guessing the segue and the protocol are being handled asynchronously.

But before I return NO I tell it to perform the segue at a delay. This delayed segue does have the data and works fine.

My question is there a way to wait for the protocol to finish and then perform the segue? Maybe with an execution block?

Upvotes: 0

Views: 398

Answers (3)

Duncan C
Duncan C

Reputation: 131418

The other responders have hinted about this, but haven't stated it explicitly, so here goes.

Do not tie a segue directly to the button. Instead, control-drag from the source view controller SCENE to the destination scene to create a segue at the view controller level. Give the segue a unique identifier.

Then, in your button IBAction code, do the async network download. You may want to display a "loading, please wait" message or something while the download is taking place. Most async network calls take a completion block. In that completion block, wrap a call to performSegueWithIdentifier in a call to dispatch_async(dispatch_get_main_queue() so the segue gets invoked on the main thread. (@SantaClaus's answer shows the syntax for that.)

So your button IBAction code might look like this:

- (IBAction) buttonAction: (UIButton *) sender;
{
  //Display a "please wait"message to the user if desired
  doAsyncCallTakingBlock( completion: ^(NSData *data)
  {
    //parse the data, (or whatever)

    dispatch_async(dispatch_get_main_queue(), ^
    {
      //This call uses the button as the sender. That might be appropriate,
      //or not. 
      [self performSegueWithIdentifier:@"jumpToOtherViewController" 
        sender: sender];
    });
  }
}

With this approach the segue doesn't get called until the async method (Which I called doAsyncCallTakingBlock in my example code) has finished it's work. You might call an Alamofire request method, use an NSURLSession, or any other async method that takes a completion block.

Upvotes: 1

Andrew
Andrew

Reputation: 15377

Check out performSegueWithIdentifier on UIViewController. If you set up a segue between the view controllers in your storyboard (not from the button) and give it an identifier, you can perform the segue as soon as the data is ready.

Since you mentioned that your data is being fetch asynchronously, you might need to dispatch the performSegueWithIdentifier call to the main thread like so:

dispatch_async(dispatch_get_main_queue(), ^{
  [self performSegueWithIdentifier:@"jumpToOtherViewController" sender:self];
});

To actually pass the data on to the next view controller, you can use prepareForSegue as described here.

Upvotes: 0

John Farkerson
John Farkerson

Reputation: 2632

Yes, I would use blocks. For example:

[Api getDataWithCompletion:^(BOOL success, NSString *data) {
    if (success) {
        self.data = data;
        [self performSegueWithIdentifier:@"MySegue" sender:self];
    } else {
        NSLog(@"Failed to get data");
    }
}];

Then, to pass it to the next view controller:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"MySegue"]) {
        TargetViewController *targetVC = (TargetViewController*)segue.destinationViewController;
        targetVC.data = self.data;
    }
}

Upvotes: 0

Related Questions