I am attempting to perform a segue after a done button is pressed from a custom signature alertView. The segue works perfect if I call it from a button inside the same view, but when I call from my signature class, I get a crash that says:
`'Receiver (<SuperSigApproveTableViewController: 0x79eea280>) has no segue with identifier 'approveSwap''
SignatureAlertViewController is the name of the class that calls the done method
- (IBAction)dismissAlertView:(id)sender {
[linerSig sendSigImageToServer];
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{[sp sendToPDF];}];
}
inside the completion block is the method I am trying to call which is located inside of SuperSigApproveTableViewController
-(void)sendToPDF {
NSLog(#"sendtopdf");
[self performSegueWithIdentifier: #"approveSwap" sender: self];
}
I have alloc and init the SuperSigApproveTableView in the viewDidLoad method of SignatureAlertViewController. I am not sure why it is crashing does anyone have any clues?
Related
I've encountered something that I've never seen. The scenario is as follows. My initial VC has an enroll button that when hit bring up a couple of separate view controllers deal with the enrollment procedure (I ask for user credentials, and if required by the server, I ask for a verification code). When this process is complete, I save a custom object to NSUserDefaults that is needed later. Then the initial VC is shown again.
Basically, I'm making sure I'm in the main thread and that the custom object is saved. If those conditions are met, I then push the main part of the app.
Here is the relevant code, all my logs are displayed in the output window, but the segue does not happen.
- (void)viewDidLoad {
[super viewDidLoad];
self.complete = false;
if([NSThread isMainThread]){
NSLog(#"Main Thread-Yes");
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"CUSTOMOBJECT"]) {
NSLog(#"Will begin pushToAuthenticate");
[self pushAuthenticateController];
}
} else {
NSLog(#"Main Thread-No");
}
}
- (void)pushAuthenticateController{
[self performSegueWithIdentifier:#"pushMainVC" sender:self];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"pushMainVC"]) {
NSLog(#"PrepareForSegue-pushMainVC");
[segue.destinationViewController.navigationItem setHidesBackButton:YES];
[segue.destinationViewController.navigationItem setTitle:#"MainVC"];
}
}
In storyboard I have a segue that connects the initial VC with the MainVC, the identifier for that segue matches "pushMainVC", and if I close and reopen the app (after enrolling) the segues does occur. The console output for both instances (reloading the initial VC after enrolling and closing and reopening the app) are as follow:
APPNAME[4106:1628238] Main Thread-Yes
APPNAME[4106:1628238] Will begin pushToAuthenticate
APPNAME[4106:1628238] PrepareForSegue-pushMainVC
Any idea why this could happen, any small thing I'm missing, or any help will be greatly appreciated. Thank you so much.
You are attempting to perform a push segue from viewDidLoad. But you can't do that because it's too soon; viewDidLoad means that the view exists but is not yet in the interface. You need to wait until there is an interface so you have something to push from.
You cant try to load it from the storayboard itself.
like this:
pushMainVC* pushMainV = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([pushMainVC class])];
[self.navigationController pushViewController:pushMainV animated:YES];
NOTICE:
Dont forget to give your viewcontroller Storyboard ID.
I have a UIBarButtonItem on my UINavigationBar named selectRoleButton. Within this class (AEFeedsViewController) I have a protocol:
#protocol AERoleSelectProtocol <NSObject>
- (void)presentLoginViewController;
#end
The selectRoleButton is hooked up to AERoleSelectViewController by a target action IBAction, which holds a container that contains AERoleSelectTableViewController.
Here is a picture of my Storyboard
Below is the IBAction for selectRoleButton:
- (IBAction)selectRole:(UIBarButtonItem *)sender
{
NSLog(#"Sender: %#", sender); // Unable to breakpoint, NSLog won't log...?
AERoleSelectTableViewController *roleViewControler = [self.storyboard instantiateViewControllerWithIdentifier:#"RoleTableVC"];
roleViewControler.delegate = self; // delegate is not being passed/set
[self presentViewController:roleViewControler animated:YES completion:nil];
}
So when tapping the button, it presents AERoleSelectViewController, with AERoleSelectTableViewController in a container. AERoleSelectTableViewController has a delegate property of
AERoleSelectTableViewController.h
#property (weak, nonatomic) id <AERoleSelectProtocol> delegate;
The IBAction should be passing and setting the delegate object, but it is nil. Breakpoints do not seem to work, it won't break at all on the method. I've even tried writing my own method and wiring the button to that, but the breakpoint still won't catch it. I also cannot print anything using NSLog method for some reason.
In AERoleSelectTableViewController.m, my didSelectRowAtIndexPath: has a switch statement, one case uses the delegate and calls a method on AEFeedsViewController which conforms to the AERoleSelectProtocol.
AERoleSelectTableViewController.m
...
if (self.delegate) {
[self.delegate presentLoginViewController];
}
But since the delegate is nil, it obviously never gets called.
Edit:
I have tried disabling the target action IBAction on the selectRole button and using a segue to present it modally.
My segue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showRoleView"])
{
NSLog(#"Sender: %#", sender);
AERoleSelectTableViewController *roleViewController = (AERoleSelectTableViewController *)[segue destinationViewController];
roleViewController.delegate = self;
}
}
The method is being called, I can breakpoint and it is crashing on
roleViewController.delegate = self;
With the crash log:
[AERoleSelectViewController setDelegate:]: unrecognized selector sent to instance
As mentioned above, I have a delegate property on AERoleSelectTableViewController.h so it should be passing no problem. But it crashes instantly, viewDidLoad doesn't even get called on AERoleSelectTableViewController.
Is this something to do with how I'm setting it on a class within a container? I tried setting the delegate on the container class (AERoleSelectTableViewController) on [segue destinationViewController] and when viewDidLoad gets called, the delegate object is set, but only the container view is displayed (the just the tableView, no background etc, RoleSelectViewController doesn't present at all, which is expected given this code.
When setting the[segue destinationViewController] to the class which holds the container (RoleSelectViewController), the containers' class viewDidLoad is called first. So the delegate is not set. Then viewDidLoad gets called on the class holding the container, with the delegate set successfully. Am I able to pass the delegate from the class which holds the container, to the container even though the container gets called first? Is using a container even the correct approach? Is a container needed or can I just put a tableView straight in there?
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?
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.
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;
}
}
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.
I have an InterfaceController with a table view. I created a segue from a cell of the tableview to another InterfaceController. If the segue type is "Push" contextForSegueWithIdentifier gets called. If the type is "Modal" contextForSegueWithIdentifier does not get called.
Never mind. According to the documentation contextForSegueWithIdentifier only gets called on a Push Segue. I needed to use the
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
method of the tableview to call
-(void)presentControllerWithNames:(NSArray *)names contexts:(NSArray *)contexts
When using a table view, use this segue method to pass an object to your segue.
- (id)contextForSegueWithIdentifier:(NSString *)segueIdentifier inTable:(WKInterfaceTable *)table rowIndex:(NSInteger)rowIndex {
return yourObject;
}
I’m I downloaded MZFormSheetController library for my app.
I’ve got a problem on my popup. When I am on my TableViewController, I tap on a row to get popup to open up so that I can change the name. The popup opens, I set the name and when I tap on the button to correct the name, I call the button method but i can’t close my popup while reload my list.
- (IBAction)modifierTournoi:(id)sender {
//code to update database
//this method close the popup but don't call method viewWillAppear to reload database
//I don't know what method i can use..?
[self dismissFormSheetControllerAnimated:YES completionHandler:^(MZFormSheetController *formSheetController) {
}];
}
Before that, I used the method popViewControllerAnimated to come back to my list while recharging my list.
- (IBAction)modifierJoueur:(id)sender {
//code to update database
[self.navigationController popViewControllerAnimated:true];
}
Can you help me please ?
Thank you very much.
It looks like there is a specific completion handler for this purpose built into the library you are using:
- (IBAction)modifierTournoi:(id)sender {
//code to update database
//this method close the popup but don't call method viewWillAppear to reload database
//I don't know what method i can use..?
[self dismissFormSheetControllerAnimated:YES completionHandler:^(MZFormSheetController *formSheetController) {
// Reloading your database should work in here.
}];
}
The reason viewWillAppear will not be being called is because rather than placing a viewController modally above your window, I imagine MZFormSheetController will be adding a UIView above all presented UIViews, so viewWillAppear will never be called. But as I said above, you should be able to reload in the completion handler block.