I currently have a button within an app that I am working on, and when I press the button it seems to not call the method when it is pressed. I thought added all the proper code, and wired everything up correctly, but that doesn't seem to be the case.
ViewControllerRootHome.h
#property (weak, nonatomic) IBOutlet UIButton *btndev;
- (IBAction)showDev:(id)sender;
ViewControllerRootHome.m
#synthesize btndev = _btndev;
- (IBAction)showDev:(id)sender {
NSLog(#"dev button pressed");
//dev button pressed
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerDev *dev = (ViewControllerDev *) [storyboard instantiateViewControllerWithIdentifier:#"dev"];
[self presentModalViewController:dev animated:YES];
NSLog(#"dev button press End");
}
The execution is not even reaching the firs log statement in the method.
Things are wired up as such,
On a side note the button that reads "Logs" seems to be loading the scene fine, but for whatever reason "dev" button does not want to call the method / load the scene.
Ohh the source code for everything can be found here, https://github.com/ipatch/KegCop
That button works fine, so if you're having problem, I'd refer you to AliSoftware's comment about making sure to clean your build. But if you look to the left of the method name, showDev, you'll see a solid bullet when the IBOutlet is hooked up correctly:
If you compare that to another method, this one, saveMasterEmail is not currently hooked up correctly, evidenced by the empty circle to the left of the method:
Unrelated, your code:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerDev *dev = (ViewControllerDev *) [storyboard instantiateViewControllerWithIdentifier:#"dev"];
[self presentModalViewController:dev animated:YES];
could be simplified to:
UIViewController *dev = [self.storyboard instantiateViewControllerWithIdentifier:#"dev"];
[self presentModalViewController:dev animated:YES];
Or, even easier, define a segue for that button and you don't need to do anything with code.
Finally, I'd make sure you avoid circular references between your scenes. You shouldn't be doing a modal segue (or presentViewController or presentModalViewController) from A to B and then again from B to A. If you do a modal segue from A to B, you should call dismissViewControllerAnimated to return back to A. Otherwise, you'll have two instances of A floating around.
Related
UPDATED FOR COMMENTS
I am trying to make this code work, but whenever I press the "nextButton", the program ends and I cannot figure out what went wrong. Can you please look at my code and figure out if anything is wrong.
- (void)viewDidLoad {
[super viewDidLoad];
Player1.placeholder = #"Player 1";
Player2.placeholder = #"Player 2";
Notes1.placeholder = #"Notes";
Notes2.placeholder = #"Notes";
[nextButton setAction:#selector(nextButtonPressed:)];
}
-(IBAction)nextButtonPressed:(id)sender {
if ([self.delegateP respondsToSelector: #selector(addPlayerViewController:didFinishEnteringPlayer1:didFinishEneteringPlayer2:)]) {
[self.delegateP addPlayerViewController:self didFinishEnteringPlayer1:Player1.text didFinishEneteringPlayer2:Player2.text];
NSLog(#"Works");
}
[self performSelector:#selector(nextButtonPressed:) withObject:nil afterDelay:0.25];
}
I also have another question to do with this code. To pass information through different view controllers using delegates, do you have to use a button or a bar button item?
The error that is displaying:
-[UIStoryboardPushSegueTemplate nextButtonPressed:]: unrecognized selector sent to instance
First you can´t put both lines:
[self presentModalViewController:gameDetails animated:YES];
[self.navigationController pushViewController: gameDetails animated:YES];
Second go to your viewController PlayersViewController and click in storyboard click on editor (top menú XCode) Embed In > Navigation Controller now you with your code change the next lines:
-(void) nextButtonPressed {
NSLog(#"Hi my friend");
[self performSegueWithIdentifier:#"YOUR_SEGUE_NAME_HERE" sender:#"hi"];}
And create new function:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:sender];
}}
The ViewController "YourViewController.h :
#interface YourViewController : UIViewController
#property (nonatomic, strong) NSString *MyObjectHere;
#end
Try setting a symbolic breakpoint on all Objective-C exceptions and see if that gives you your crash line numbers and error messages again.
The code you posted for setting up the target/action on your bar button item looks correct.
Edit: Disregard this part about changing the selector.
(I'm leaving it for continuity, but ignore it.)
One thing you might try is changing the selector to
:#selector(nextButtonPressed:) (Colon added)
And then adding the button as a parameter to your action method:
-(IBAction) nextButtonPressed: (UIButton *) sender
Action methods should work with or without the sender parameter, but
it's worth trying.
(Also you should add the IBAction keyword for clarity even if you're not hooking up the action in IB.)
EDIT: rmaddy pointed out that your code is trying to both modally present and push the same view controller. Don't do that. Do one thing or the other.
You should use either
[self presentModalViewController:gameDetails animated:YES];
(To present it as a modal)
or
[self.navigationController pushViewController: gameDetails animated:YES];
(To push it onto the navigation stack.
But not both. Doing both is almost certainly the cause of your crash.
I have been searching around about this problem, but haven't got any answer yet. My problem is: I have a push button that when clicked will push to another view.
Here is how I push it:
ViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil]
instantiateViewControllerWithIdentifier:#"Main_View"];
[self.navigationController pushViewController:vc animated:YES];
The ViewController vc is the view that I pushed. So in the new view, there is a Back button that can drop the current view and comeback to the old view. When I click the push button, the vc been load again from allover.
How can I make the instance of vc stay in memory, so when I go back to old view and come to vc again, it doesn't need to reload again?
I guess it is something related to navigationController stack, but don't know how to do that.
Thanks
Just keep a ViewController instance as property in your first ViewController
#property (strong,nonatomic)ViewController * vc;
Then when push it,check if it is in memory
if (self.vc == nil) {
self.vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil]
instantiateViewControllerWithIdentifier:#"Main_View"];
}
[self.navigationController pushViewController:self.vc animated:YES];
Are you using a UINavigationController? If so, can call the following line of code in you back button 'IBAction':
[self.navigationController popViewControllerAnimated:YES];
If you really need to keep a instance of the view controller, do what Leo suggested.
I tried many answers here but none of them had the same exact scenario.
I'm trying to navigate to a UIViewController that's within a separated storyboard in a different bundle, so far I was able to navigate to it but am unable to return to the previous UIViewController. The method that invokes the external view controller (TabBarController) is implemented as follows:
+(void) launchExternalUI: (UIViewController *) previousViewController {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: [self frameworkBundle]];
TabBarController *vc = (TabBarController*) [sb instantiateInitialViewController];
[previousViewController.navigationController pushViewController:vc animated:YES];
[vc release];
}
Now the method within TabBarController that should return to the previous view controller:
- (void) navigateToPreviousViewController: (UIGestureRecognizer *)gesture {
[self.navigationController popViewControllerAnimated:YES];
}
In TabBarController, if I print all the viewcontrollers within self.navigationController, all I see is the TabBarController, shouldn't I see the previous view controller that pushed this on launchExternalUI ? The [self.navigationController popViewControllerAnimated:YES]; has no effect at all. I'm a bit lost on this.
It's also important to notice that previousViewController is defined in a local storyboard and TabBarController is implemented in a different .framework, would that cause the issue?
Thanks in advance for all the help!
**Edit: The navigation flow I need is storyboard1:VC1->storyboard2:VC2->storyboard1:VC1, I can get storyboard1:VC1->storyboard2:VC2 part to work but not storyboard2:VC2->storyboard1:VC1
I often split projects up into various Storyboards, and have created a dynamic view controller that handles the task of loading the appropriate controller from a secondary storyboard, whilst maintaining the navigation tree.
I've created a sample project and uploaded to github as it's easier than explaining all the steps here. The key part to note is the User Defined Runtime Attributes for each of the DynamicStoryboardViewControllers in the Main.stoyboard. Note also that each of the secondary storyboards need the "is initial View Controller" checked for one of your viewControllers. Not included in the example is loading a specific scene from a storyboard. This is no more than adding the "sceneName" dynamic runtime attribute much in the same way as the storyboardName attribute is added.
it's a quick sample so a little rough, but you'll get the idea of how it all works. Feel free to ask questions if you get stuck.
Cheers!
EDIT:
It dawned on me that perhaps you don't have a navigationController in the view hierarchy (as i do in my sample). And in any event, seemingly, you won't have much control over where your tab bar is introduces. So without a navigationController the [self.navigationController popViewControllerAnimated:YES] won't work;
You should test for this and either call the popViewControllerAnimated as you do, or call dismissViewControllerAnimated ;
if(self.navigationController){
[self.navigationController popViewControllerAnimated:YES];
}else{
[self.presentingViewController dismissViewControllerAnimated:Yes completion:nil];
}
Hope this helps, if not, perhaps you can supply some sample code.
Create an unwind segue within your original view controller, and use segues instead of pushing/popping view controller views onto the view array.
To create an unwind segue you should create an unwind method in the ORIGINAL viewcontroller:
- (IBAction)unwindToOriginalViewControllerSegue:(UIStoryboardSegue*)sender {
; //TODO: anything special you need, reference via sender
}
Then, in your NEW viewcontroller, in the storyboard, drag from the controller icon to the Exit icon. Link that to the Unwind segue you named above.
UIViewController * YourViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Storyboard Identifier"];
[self.navigationController pushViewController:YourViewController animated:TRUE];
I have an UITabBarController that has 3 buttons. The second button points to ViewController1 which is connected to another view called ViewController2. After I tap a button in ViewController2 I programmatically present ViewController1 again, that works perfect except one thing. After I "arrived" to ViewController1 the tab bar disappears.
I'm using this method to navigate back to ViewController1. (exactly I navigate to its navigation controller, but already tried with the view)
- (void)presentViewControllerAnimated:(BOOL)animated {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"storyboard" bundle:nil];
UINavigationController *firstViewNavigationController = [storyboard instantiateViewControllerWithIdentifier:#"destination"];
[self presentViewController:firstViewNavigationController animated:animated completion:nil];
}
I call here the first method
- (void)didTapButton:(id)sender {
UIButton *button = (UIButton *)sender;
CGPoint pointInSuperview = [button.superview convertPoint:button.center toView:self.tableView];
[self presentViewControllerAnimated:YES];
}
This method hides the tab bar in the ViewController2, I already tried without it, therefore there is no problem with it.
-(BOOL)hidesBottomBarWhenPushed
{
return YES;
}
I can't figure out why this thing happens, I think it's a fair solution, that worked well for a several times when I needed to present views. I've read it can happen with segues, but I'm doing it with code without segues.
Actually your code works right. There should not be tab bar when you present FirstViewController from SecondViewController. Because when you call instantiateViewControllerWithIdentifier its basically creates a new instance of that view controller, and of course, there is no tab bar.
The right way to go back to your first view controller is to pop SecondViewController (or dismiss it, if it presented modally). So your final code should be like this
- (void)didTapButton:(id)sender {
// If this view controller (i.e. SecondViewController) was pushed, like in your case, then
[self.navigationController popViewControllerAnimated:YES];
// If this view controller was presented modally, then
// [self dismissViewControllerAnimated:YES completion:nil];
}
And of course, your view controller hierarchy in storyboard must be like this:
-- UINavigationController -> FirstViewController -> SecondViewController
|
->UITabBarController____|
-...
-...
I've tried the same and got the same result.
My solution was simple, on the push do this :
UINavigationController *firstViewNavigationController = [storyboard instantiateViewControllerWithIdentifier:#"destination"];
firstViewNavigationController.hidesBottomBarWhenPushed = true; // Insert this and set it to what you want to do
[self presentViewController:firstViewNavigationController animated:animated completion:nil];
and then remove your
-(BOOL)hidesBottomBarWhenPushed
{
return YES;
}
I am in need of some help please. I have been stuck on this issue all day and I cannot find a fix. I will try and be as clear as possible with my problem.
I have 2 ViewControllers. One is called ViewController and the other is called GameViewController. In the ViewController I have my home screen menu and on the GameViewController I have the Game screen AND the Game Over screen. When you press play from ViewController it takes you to the Game, and then when you die you stay in the same GameViewController but in a Game Over view. If you press restart in the Game Over screen a segue takes you back modally to the Game Screen.
So here is the problem I am having.
When you die for the first time and go to the Game Over screen, you can click on home and it will take you home, BUT if you press restart, then die again (that's twice ) and then try to press home from the Game Over Screen it will NOT run the code inside the home button. The button actually works, I have an NSLog in their and it shows in the debugger screen. Weird I know. Please see below code that will show everything I have done to try and make this work.
In GameViewController.h //Above all the #imports
#class GameViewController;
#protocol GameViewControllerDelegate <NSObject>
-(void) GameViewControllerDidCancel:(GameViewController *)controller;
- (IBAction)homeBtn:(id)sender;
#end
And this just after the the close } in #Interface
#property (nonatomic, weak) id <GameViewControllerDelegate> delegate;
In GameViewController.m after the #Implementation
#synthesize delegate = _delegate;
And this in my homeBtn
- (IBAction)homeBtn:(id)sender {
NSLog(#"home btn pressed");
[self.delegate GameViewControllerDidCancel:self];
}
Then in my ViewController.h
#import "GameViewController.h"
#interface ViewController : UIViewController <GameViewControllerDelegate>
ViewController.m
When I press play
- (IBAction)playButton:(id)sender {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"Main_iPad"
bundle:nil];
GameViewController* controller= [sb instantiateViewControllerWithIdentifier:#"GML"];
controller.delegate = self;
[self presentViewController:controller animated:NO completion:nil];
[audioPlayer stop];
}else {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"Main_iPhone"
bundle:nil];
GameViewController* controller= [sb instantiateViewControllerWithIdentifier:#"GML"];
controller.delegate = self;
[self presentViewController:controller animated:NO completion:nil];
[audioPlayer stop];
}
}
And the method that I try to call from my homeBtn is also in here
#pragma mark - GameViewControllerDelegate
- (void) GameViewControllerDidCancel:(GameViewController *)controller {
[self dismissViewControllerAnimated:NO completion:nil];
}
I am pretty sure that is it. So like I said before, the restart button is just linked to a segue to return back to the Game screen.
Please guys, I am really desperate now, I am completely out of ideas.
Thank you
Quite a convoluted view hierarchy and hard to understand.
What exactly does the restartGameButton do. Is it an unwind segue?
When you press play from ViewController it takes you to the Game, and then when you die you stay in the same GameViewController but in a Game Over view. If you press restart in the Game Over screen a segue takes you back modally to the Game Screen.
What is the GameOver view thing? Once you say its a view in the GameViewController, but then again you say if you press restart a segue takes you back modally to the Game Screen (i.e. GameViewController) which implies the GameOver Screen is also a ViewController.
Why use modal presentation when it is a actually a navigation stack that you want?
Best solution in my opinion:
Make 3 ViewControllers, one for the Start, one for the Game, one for GameOver. Embed the ViewControllers in a navigation controller and push and pop with this one instead of presenting many modal views on top of one another.