Can't use segue more than once - ios

I'm trying to see a different viewcontroller whenever my app gets a push notification. I tried to use
[self performSegueWithIdentifier:#"shootPicture" sender:self];
and
[self performSelectorOnMainThread:#selector(performSegueWithIdentifier:sender:) withObject:#"shootPicture" waitUntilDone:NO];
and
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *BombaViewController = [storyboard instantiateViewControllerWithIdentifier:#"BombaViewController"];
[self presentViewController:BombaViewController animated:NO completion:NULL];
All af the above example works, but they work JUST ONCE! After the viewcontroller goes back to the main tabbed controller, it doesn't work anymore.
I've also used NSLog to make sure that the system can actually intercept the push notifications.
Any suggestions?

I just put a button and dragged it to the starting Tabcontroller
This would indicate you have a new push / modal segue.
So, you aren't really going back, you're going to a new copy of your tab controller which is sitting in front of everything else. What you should really be doing is unwinding the segue that got you there, or connecting the button to an action in your code and calling dismissViewControllerAnimated:completion:.

Related

Can't push viewController because navigationController is nil

I can't perform a transition between viewControllers because navigationController is nil. I have logged navigationController in different parts of the class but it returns nil everywhere. In storyboard the viewController is embedded in a navigationController. I have checked other threads on SO with the same issue, but none of the answer has helped or even really made sense to me.
Can't push because self.navigationController is nil
navigationController is nil,when push the viewcontroller
Why is it nil? And how do I solve this? An error message is also returned:
I have tried both using a segue:
[self.navigationController performSegueWithIdentifier:#"experienceDetails" sender:self];
as well as pushing:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Inspiration" bundle:nil];
ExperienceViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"experience"];
[self.navigationController pushViewController:viewController animated:NO];
Nothing happens using push but an error message is produced:
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior
I have looked for solutions on that error too, but there doesn't seem to be a clear and concrete answer to how to solve it. Again, those suggestions I read and tried didn't work.
I'm really at a loss here. Such a simple thing to do but I'm hindered by something I don't even understand.
EDIT
If it helps, I have a tab bar and in one item I have the viewController that is embedded in a navigationController and from there I want to push to another viewController within the same storyboard.
EDIT
I got this to work:
[self showViewController:viewController sender:self];
very likely because it doesn't use navigationController. Its presented as modular though and is not part of the navigation stack, which is not something I want. Just good to know that things would work if navigationController wasn't nil.
I reproduced the described behaviour when segue experienceDetails was created from navigation controller... and here is solution
1) deleted segue experienceDetails form navigation controller
2) created push segue with identifier experienceDetails from included view controller to detail view controller
3) in view controller performed segue from self as below
[self performSegueWithIdentifier:#"experienceDetails" sender:self];

incorrect navigation controller sequence results in blank navigation bar

I am doing something wrong, but not sure what it is. Bellow is the diagram of my storyboard.
/----(push)----login \
Start nav ctrl mainview nav ctrl
\----(modal) register /
When the user goes through login an event is received by appdelegate logindone, in login done I do:
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"MainView"];
This works just fine.
However if the user goes through register and completes registration with login the same loginDone is triggered but the code above results in blank view.
If I replace above code with
UIStoryboard* storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
MainCtrl *mainViewController = (MainCtrl*)[storyBoard instantiateViewControllerWithIdentifier: #"MainView"];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.window.rootViewController =navigationController;
This almost works, the view is displayed correctly, the only problem is that navigation bar is blank and it stays blank no matter where I go. Restarting app works of course because it bypasses register controller.
in register I do [self dismissViewControllerAnimated:NO completion:nil]; before calling loginDone, but this really makes no difference.
Any suggestions?
Edit:
self.window.rootViewController = [self.window.rootViewController.storyboard
instantiateViewControllerWithIdentifier:#"MainView"];
is the only thing that will work in normal flow i.e after login. Otherwise I get a crash "unavailable segue".
The way I fixed this is not exactly the way I wanted to fix it, but it works. I had to modify login done function that used to handle login finish work and transition to main view passing it parameter not to transition. On return I trigger a segue to main view from registration view. Still not sure why it was not working the way I had it done originally.

Trying to initialize a view controller from storyboard

I have a view controller I placed in the main storyboard but when I try to initialize and load up the view controller my app crashes. The xcode version I'm using doesn't really tell me the errors I get properly, but I did see that it was giving me a sigabrt signal. I don't know why it isn't working.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"SelectDateViewController"];
[vc setModalPresentationStyle:UIModalPresentationFullScreen];
[self presentModalViewController:vc animated:YES];
This is how I called it. All the names I used are correct but I still don't understand why it's crashing.
Set a breakpoint at the last line to verify vc (and also storyboard) are not nil.
presentModalViewController will crash if its viewController argument is nil.
If both are nil, then your storyboard name is likely incorrect (less likely).
If just vc is nil then the identifier is incorrect (either in code or the storyboard). Make sure the VC's Storyboard ID (as circled in the screenshot) matches your description. It is customary not to choose a class name for this (as you might have more than one instance of a given class in your storyboards).
The Storyboard ID is case sensitive and must be unique. I find the sure fire way to set it is type the name in that field after selecting the view controller and pressing return to end editing (don't just click off the field). I've noticed some corruption occurring in storyboards when converting from Xcode 4 to 5 and vice versa, so diving into the plist/xml might also help.
Edit:
Also the timing of when this is called is important, see
presentModalViewController not working.
Make sure the view controller receiving the message has already been shown (i.e. called in viewDidAppear, not viewDidLoad).
As mentioned by Abdullah, you should likely move off the deprecated method to:
[self presentViewController:vc animated:YES completion:nil];
In this case the completion block can be nil without issue, but vc must not be.
[self presentModalViewController:vc animated:YES]; is deprecated.
Try:
[self presentViewController:vc animated:YES completion:nil];
Did you set the initial ViewController from Storyboard?
If yes then Make sure the your Storyboard id is same.
If you are using storyboard you can directly present or dismiss the viewController no need to write the code for it
hope this help you :)

Moving from one view to another iOS

I'm relatively new to iOS development. I am to move from one viewController to another I use a modal segue transition on button click. This is a game so i want to allow the user to click images to essential move the the app menus.
I have a main page that displays several images, on clicking one i want to be able to move to another view. Currently doing this with a modal segue is causing odd problems with my touchesEnded event where if, for example, i navigate to a page 3 times the touchesEnded event is fired 3 times.
Is there a better way for me to do this or am i just missing thing fundamental?
Thanks
Yes, I think you must make the the navigation controller your root view controller then push views accordingly
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:YOUR_BASE_CONTROLLER]
self.rootViewController = nav;
this is in your app delegate.
Then in your action method
[self.navigationController pushViewController:secondViewController animated:YES]
Im assuming you are using the Storyboard to link VCs using segues.
Modal segues are great for simple transitions but really seem to limit what you can accomplish when they are just linked through SB. Ive found that creating an IBAction that includes the following for a VC segue will allow you to not only control your segues more efficiently but also allow you to have a clearer view of what is actually occurring during the transition.
-(IBAction)goToVc:(id)sender{
//Other code to take place during the segue here
//This will identify the Storyboard in use
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
//This will identify the View Controller to switch to
SecondViewController *vc2 = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewControllerID" ];
[self presentViewController:vc2 animated:YES completion:NULL];
}
Every time you perform a modal segue, you lose the UINavigationController that you were previously using. What you need to do is embed a UINavigationController in the view that you are performing a modal segue to.
Check out a question I answered the other day to help you visualize more clearly what I'm talking about.

iOS - Log out of app

I have an app that starts out in the login screen and when you log in it pushes a modal TabBarController. One of the tabs is settings which has a logout button, what would be the correct way to log out of my app and not have any issues such as memory leaks?
It really depends on how your users are logging in. What you probably need to do is the opposite of whatever you are doing to login. If all the login does is open the modal dialog, then closing it should be fine. It you are setting some kind of security token, then you will need to set it to nil.
Without knowing more about how your app works, there isn't much more I can say.
I know this is old, but if you want to present your login screen (because you logged out) as a modal, you can do this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
//Your login view controller, make sure you set the storyboard id
TTTLoginController *log = (TTTLoginController *)[storyboard instantiateViewControllerWithIdentifier:#"log"];
//wrap it in a navigation controller
UINavigationController *navBar=[[UINavigationController alloc]initWithRootViewController:log];
//present the modal view
[self.navigationController presentViewController:navBar animated:YES completion:nil];

Resources