I am trying to call the method addObjectToArray from SecondViewController.m. The NSLog works, however I cannot add "foo" to _myArray (an NSMutableArray that is the data source for the UITableView). If I call [self addObjectToArray] in viewDidLoad, then it works fine.
FirstViewController.m
-(void)addObjectToArray {
[_myArray addObject:#"foo"];
[_myTableView reloadData];
NSLog(#"it works");
}
SecondViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"identifier"]) {
FirstViewController *controller = [segue destinationViewController];
[controller addObjectToArray];
}
}
As per Nofel Mahmood's comment, you are creating two separate instances of FirstViewController, one named firstViewController (created from the storyboard) and one called myObject (created with the 'new' method). You then call the addObjectToArray method on myObject, but you present firstViewController. Your myObject is essentially redundant. Amend your code as follows:
-(IBAction) doSomething:(id)sender {
FirstViewController *firstViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"firstViewController"];
[firstViewController addObjectToArray];
[self presentViewController:firstViewController animated:YES completion:nil];
}
EDIT
Since you want to return to an existing instance of FirstViewController, you need to use an unwind segue. There's a detailed explanation here: what-are-unwind-segues, but in your case:
In FirstViewController, add a new method:
- (IBAction)unwindToFirst:(UIStoryboardSegue *)unwindSegue {
[self addObjectToArray];
}
Then in your storyboard, ctrl-drag from the SecondViewController (or if you prefer, from a specific control in the view) to the green "Exit" icon in the bar below the SecondViewController. You should then select the unwindToFirst action in the small popup that appears.
If you want to use this unwind segue from code, look in the Document Outline on the left hand side of your storyboard for the Unwind Segue you just created. Select this, and then add an identifier in the attributes inspector on the right hand side. You can then call this segue from code using the normal [self performSequeWithIdentifier:...] method.
Related
I am trying to transition between view controllers with a segue. It is navigating correctly but it is running twice.
Here is my code:
- (IBAction)onActionNext:(id)sender {
if ([[nameField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]isEqualToString:#""]) {
}else if([[emailField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:#""]){
}else if([[mobileField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:#""]){
}else{
[self performSegueWithIdentifier:#"VerifyNext" sender:sender];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"VerifyNext"]) {
// Get destination view
OTPSceneViewController *vc = [segue destinationViewController];
[vc setTitle:#"myapp"];
}
}
If even after commenting this line segue still run then as #mikealter said segue is connected your button. Check if when you check for when you click segue what it highlights.
A button like:
Or A View Controller like:
If you fall in first case that means your segue is connected to a button and, that is the reason why it opens next page even if you comment your code. And 2 times when you keep code(one from storyboard and another from code).
Edit:
If you want to call next page from code only. Remove Segue. Then reconnect it with view controller like:
First, whenever you create a segue (i.example) FROM a Button TO a View Controller, you are creating the link between VC1 and VC2.
If you run your project you will see that the segue works navigating from one VC to the other one.
This means that it already performs the segue, so if you declare an ACTION for your button and make a call to - perform segue... - it will run twice.
Note: if you call performSegueWithIdentifier is probably because you want to pass some data via SENDER. In that case you (obviously) need to call the segue ONCE, so instead of creating the segue from the button to the next VC, (first delete that segue), create a new one FROM the VCOrigin to VCDestination -> there are many ways to do it, but from the Interface Builder is as simple as right click on the top yellow rounded icon and drag&drop on the destinationVC. Don't forget to set the identifier on the Attributes Inspector.
Now, in your button~Action make the call to performSegueWithIdentifier and pass the data.
if you stop storyboard segue use following method:
override func shouldPerformSegueWithIdentifier(identifier: String,sender: AnyObject?) -> Bool {
return false
}
You don't even add
[self performSegueWithIdentifier:#"VerifyNext" sender:sender];
in onActionNext method if you add the segue in storyboard
If you want to navigate to next view controller use above line.
Preparing for the Segue
In order to pass information from one view controller to another using
the UIStoryboardSegue object we’ll need to take advantage of the
current view controller’s prepareForSegue:sender: method.
But If you call both,it calls two times.So make sure that whether you want to pass information or data to next view controller or just navigate the view.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"prepareForSegue: %#", segue.identifier);
if([segue.identifier isEqualToString:#"VerifyNext"])
{
VerifyNextViewController *verifyNVC = segue.destinationViewController;
verifyNVC.name = #"Prashabt";
verifyNVC.id = #"1234";
}
else if([segue.identifier isEqualToString:#"VerSeg"])
{
AnotherViewController *anotherVC = segue.destinationViewController;
anotherVC.name = #"Sharma";
anotherVC.id = #"3245";
}
}
have a look of this question this may sure rectify
or there is another way to navigate to another view controller using below code.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"viewControllerName"];
[self.navigationController pushViewController:vc animated:YES];
make sure you give identity to the view controller using identity inspector see image.
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 two ViewController (ViewController, ResultViewController).
There have three button in the ViewController,and there have a back button in ResultViewController.
In the ResultViewController I want to get the value from the ViewController passing.
So I add the below code in the ResultViewController
#interface ResultViewController : UIViewController
- (IBAction)backEvent:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *resultLb;
#property (strong, nonatomic) NSString *selectedVal;
#end
.m
- (void)viewDidLoad {
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO];
self.resultLb.text = self.selectedVal;
}
- (IBAction)backEvent:(id)sender {
[self.navigationController popViewControllerAnimated:NO];
}
In the ViewController have three button action:
- (void)viewDidLoad {
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO];
rvc = [self.storyboard instantiateViewControllerWithIdentifier:#"ResultViewController"];
}
- (IBAction)passAEvent:(id)sender {
rvc.selectedVal = #"A";
[self.navigationController pushViewController:rvc animated:NO];
}
- (IBAction)passBEvent:(id)sender {
rvc.selectedVal = #"B";
[self.navigationController pushViewController:rvc animated:NO];
}
- (IBAction)passCEvent:(id)sender {
rvc.selectedVal = #"C";
[self.navigationController pushViewController:rvc animated:NO];
}
When I first click the button A, that can correct pass the "A" value to the ResultViewController.
But when I click the back button in the ResultViewController.
Then click the B or C button action, that still pass the "A" value.
I see the log, that was not enter B, C event.
I try to run again program, when I click the first button with B button, It was correct enter and pass "B" value to ResultViewController.
But when I click the back button, then click the A or C button, it was not correct value in the ResultViewController.It still show "B" value.
I don't know why? how can I resolve the problem if not use delegate or notify?
Thank you.
----- edit ----
To the #Gaurav Singh
I am not find the option about the animate .
It's because you're keeping a strong reference to the UIViewController, via your variable rvc.
You're setting the value of resultLb in the viewDidLoad method of your view controller. When you push the view controller onto the stack, your view controller's setting the label by taking what's stored in it's variable selectedVal.
When you pop the view controller from the stack, because you've got it stored in a variable, it won't get unloaded from memory. It still exists. You may be setting selectedVal again when tapping the second button, however viewDidLoad isn't going to get run again, as it's not getting re-instantiated.
What you could do is overload setSelectedVal on ResultViewController as follows:
- (void)setSelectedVal:(NSString *)selectedVal {
_selectedVal = selectedVal;
self.resultLb.text = selectedVal;
}
This way, when you set selectedVal from the calling view controller, the above method will be run, setting the value of your property in ResultViewController, whilst also modifying your label.
EDIT - Better still:
Rather than setting the value of the label in viewDidLoad:, do it in viewWillAppear: instead. This way, you don't need to override any setters or anything like that. When viewWillAppear is called, it'll read the string you've set and set the label's text accordingly.
Hope this helps!
You are just making it complex. I would do it like this:
First I will connect create a storyboard segue from ViewController to Result Controller (View Controller to View Controller segue) and assign a suitable identifier to that segue (say "resultSegue")
then following code would work
- (IBAction)passAEvent:(id)sender {
[self performSegueWithIdentifier:#"resultSegue" sender:#"A"];
}
- (IBAction)passBEvent:(id)sender {
[self performSegueWithIdentifier:#"resultSegue" sender:#"B"];
}
- (IBAction)passCEvent:(id)sender {
[self performSegueWithIdentifier:#"resultSegue" sender:#"C"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
if([segue.identifier isEqualToString:#"resultSegue"]){
ResultViewController *rvc = segue.destinationViewController;
rvc.selectedVal = sender; //Here SENDER will have value: A,B or C which we have passed in performSegueWithIdentifier:sender: method
}
}
Edit: code in prepareForSegue:sender: method modified so that it check the segue identifier before working on destinationViewController
First, you should give the segue an "id",like "resultSegue".
Then, use the code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender
{
if([segue.identifier isEqualToString:#"resultSegue"]){
ResultViewController *rvc = segue.destinationViewController;
rvc.selectedVal.text = someUILabel.text;
than use the [self.navigationController pushViewController:rvc animated:NO];//.........
}
I will give you more detail when I m at home, if you don`t solve the problem....
Thanks!
put
rvc = [self.storyboard instantiateViewControllerWithIdentifier:#"ResultViewController"];
in viewWillAppear
I have two view controllers inside a Navigation Controller.
In the first view controller I have two buttons. Both of them call the second view controller using a Push segue, but:
I need to know which button sent me in the second view controller. How?
In the second view controller I have a UIDatePicker and a Button "Ok": how can I send the chosen date to the first view controller when Ok is pressed? (And how do I receive them?)
EDIT:
I don't know if my problem is clear: now I know how to pass data from the first view controller to the second view controller with prepareForSegue, but what I really need is to pass data (the picked date) from the second view controller to the first, and how can I do it without a prepareForSegue (when Ok is pressed)?
EDIT2:
I made it. It was so simple, guys...
I decided to use modal segue:
Firstviewcontroller.h:
+(FirstViewController *)getInstance;
Firstviewcontroller.m:
static FirstViewController *instance =nil;
+(FirstViewController *)getInstance
{
return instance;
}
and in its ViewDidLoad:
instance = self;
Secondviewcontroller.m, in the OkButton IBAction:
SecondViewController *secondViewController = [SecondViewController getInstance];
//...
//modify what I need to modify in secondviewcontroller
//...
[self dismissModalViewControllerAnimated:YES];
That's it.
Thank you all anyway.
Assign Identifier to each segue in storyboard and implement
- (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];
[vc setDelegate:self];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
For more info about How to use storyboard and pass value check this article or this discussion on stackoverflow
for the second question you can use delegate pattern
IN SecondViewController.h
#protocol SomethingDelegate <NSObject>
- (void)dateChanged:(NSString *)dateStr; //you can use NSDate as well
#end
#interface ViewController2 : UIViewController
#property(weak) id<SomethingDelegate> delegate;
#end
in .m file
-(void) OkClicked{
[_delegate dateChanged:#"YOUR_DATE_VALUE"];
}
In FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController<SomethingDelegate>
in .m
-(void)dateChanged:(NSString *)dateStr{
// do whatever you need with dateStr
//also i made some change in prepareForSegue method
}
Note:- take care your naming convenes for VC
just pass the button id to the second viewcontrol.
use delegates to sent the data from second viewcontroller back to first view controller
regards
Johan
I am trying to segue to a ABPersonViewController from a button in the Storyboard.
But if I do that the screen is completely black.
If I use a IBAction for the button and use the following code it works:
ABPersonViewController *person = [[ABPersonViewController alloc]init];
[self.navigationController pushViewController:person animated:YES];
Why is that? am I doing wrong?
EDIT: I found a work around but I don't think this is a proper way to do it. I subclassed ABPersonViewController and overrode the initWithCoder method with the following:
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [self initWithNibName:nil bundle:nil];
return self;
}
Since you are using storyboards you should should name the segue you are using and then this in your IBAction:
[self performSegueWithIdentifier:#"abPersonViewControllerSegue" sender:self];
This way you do not even need to manually call alloc/init. Then in your prepareForSegue you could set any attributes:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"abPersonViewControllerSegue"])
{
[segue.destinationViewController setAttribute:#"whatever you want"];
...
}
}
If this is not what you are looking for please let me know.
You don't set the displayedPerson property ...
#property(nonatomic, readwrite) ABRecordRef displayedPerson
Since you are using Storyboard, why do you still call [[ABPersonViewController alloc]init]?The Storyboard will handle the creation and pushing of the ABPersonViewController(if you specified Push action for the segue). As long as you control dragged from the Button to the ABPersonViewController in story board, you do not need to write a single line of code. If you control dragged from the view controller that contains the button, then you should be calling performSegue to trigger the segue(you need to set the identifier for the segue in this case) that adds the ABPersonViewController to the navigation controller.