Question was solved! See the code at the bottom.
I'd like to pass some data between 2 controllers. I have MainViewController class where GoogleMap is loaded. On click at custom info window for each GMap's marker I want to open new window with place details.
My storyboard is:
Segue was named: showPlaceDetails:
Several methods was written:
- (void) mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker {
[self performSegueWithIdentifier:#"showPlaceDetails" sender: nil];
}
(I also tried to use sender: [marker snippet]).
My prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showPlaceDetails"]) {
//[[segue destinationViewController] setDelegate:self];
PlaceDetailsViewController *destViewController = segue.destinationViewController;
//Pass some data
}
}
But I got the message:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (RelaxInKZViewController) has no segue with identifier 'showPlaceDetails''
I did these steps:
Add New View Controller on storyboard.
Add Objective-C Class called PlaceDetailsViewController.
Change custom class of newly added VC to "PlaceDetailsViewController"
Add that code
Clear Simulator's data and clean project
And nothing. I hope you can help me :)
Thx, Artem.
Mr_bem has adviced me to refuse segues and use pushViewController method. It's good!
UIStoryboard *iPhoneStoryboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
PlaceDetailsViewController *destViewController = [iPhoneStoryboard instantiateViewControllerWithIdentifier:#"PlaceDetailsViewController"];
destViewController.placeData = marker.placeData;
[self.navigationController pushViewController:destViewController animated:NO];
You are using a Push segue, this can only be performed with a navigationController, like:
[self.navigationController performSegueWithIdentifier:#"showPlaceDetails" sender: nil];
Note you should have a navigation controller before your ViewController, the one that pushes.
Or if you want, you can change its type to Modal, everything should work great for you (Y)
UPDATE
Okay, here's what I propose, remove the segue, make the storyboardID of your PlaceDetailsViewController to PlaceDetailsViewController from the IB, and add this code instead
- (void) mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker {
PlaceDetailsViewController *destViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PlaceDetailsViewController"];
[self.navigationController pushViewController:destViewController animated:YES]
se
}
THIS MUST WORK! LOL!
You need to embed your RelaxInKZViewController inside UINavigationController to use push segue. Select RelaxInKZViewController-> from the menu at the top select Editor->Embed In->Navigation Controller. I think that will solve your problem.
Related
I have a uibarbuttonitem called nextButton in storyboard that goes to another ViewController with a segue. But when I try to add this method on another file:
- (IBAction)nextButtonPressed:(id)sender {
NSLog(#"Hello")
}
the output does not show up on the console. The only way I have gotten the button to display this message is when I take out the segue in the storyboard. When I do that, I add the following method so that on clicking the button the segue is done programmatically:
- (IBAction)nextButtonPressed:(id)sender {
NSLog(#"Hello");
GameDetailsTableViewController *gameDetails = [[GameDetailsTableViewController alloc]init];
[self.navigationController pushViewController: gameDetails animated:YES];
}
But when I do this, the console shows an error:
'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
I need help to figure out how to make sure that the button displays the message and goes to the next view controller on click.
A button can be assigned to either an IBAction method or a segue.
If you use a segue, implement -prepareForSegue:sender: in your controller to run any additional code when the segue is executed. E.g.:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([[segue identifier] isEqual:#"gameDetailSegue"])
{
GameDetailsTableViewController* gameDetails = (id)[segue destinationViewController];
gameDetails.cheatMode = self.prefs.cheatModeEnabled;
}
}
If you use an action method then you have to present the view controller manually. You also have to instantiate the view controller from your storyboard:
- (IBAction)nextButtonPressed:(id)sender
{
GameDetailsTableViewController* gameDetails =
[self.storyboard instantiateViewControllerWithIdentifier:#"gameDetailController"]
gameDetails.cheatMode = self.prefs.cheatModeEnabled;
[self.navigationController pushViewController:gameDetails animated:YES];
}
In either case, you must assign identifiers to your segues and view controllers in your storyboard.
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 a button which leads to a popOver, all created in Interface Builder. The popOver is closed when I press somewhere outside of it, but I would also like to implement a button within the popOver which does that.
I found a solution by Giorgio Barchiesi dating back to 2011, however I fail to implement it. Here's his solution:
In the implementation file of the source view controller:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue destinationViewController] isKindOfClass:[MyDestViewController class]]) {
MyDestViewController* viewController = (MyDestViewController*)[segue destinationViewController];
UIStoryboardPopoverSegue* popoverSegue = (UIStoryboardPopoverSegue*)segue;
[viewController setPopoverController:[popoverSegue popoverController]];
}
}
In the header file of the destination view controller:
#property (weak, nonatomic) UIPopoverController* popoverController;
In the implementation file of the destination view controller:
#synthesize popoverController;
Same file, whenever you want to dismiss the popover:
[popoverController dismissPopoverAnimated:YES];
i could call the last function when the button is pressed.
My problem is that XCode gives me an error on the [viewController setPopoverController:[popoverSegue popoverController]] line: ARC Semantic Issue: No known class method for selector 'setPopOverController'
What did I miss to implement?
Here is the method I use:
Open your storyboard file, select the segue arrow and open the Attributes Inspector (Option - Command - 4) and identifier fill in a sensible name, like "myPopoverSegue".
In your Source View Controller define a variable right after #implementation :
#implementation ViewController
{
__weak UIPopoverController *myPopover;
}
Then, again in the Source VC:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:[dict objectForKey:#"myPopoverSegue"]]) {//#"segue" is your segue name. You can use isKindOfClass as you do currently, I prefer this method.
myPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
}
-(void)closePopover{
[myPopover dismissPopoverAnimated:YES];
}
In the end of your Source VC's viewDidLoad method write:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(closePopover) name:#"popoverShouldDismiss" object:nil];
Finally, whenever you want to dismiss the popover:
[[NSNotificationCenter defaultCenter] postNotificationName:#"popoverShouldDismiss" object:nil];
Hope this helps!
This way you will also be able to change the segue to a different controller without changing your code.
You can add the delegate < UIPopoverControllerDelegate > to your class and override the delegate method:
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return NO;
}
This will prevent the popover to be dismissed when user presses anywhere on screen.
Now you can dismiss your popover inside the button's selector method by using:
[popoverController dismissPopoverAnimated:YES];
In iOS 8 it's really easy. Just call
[self dismissViewControllerAnimated:YES completion:^{}];
Pop overs are regular presentation controllers, so it's more or less the exact same thing as a modal view controller.
Try this code
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue destinationViewController] isKindOfClass:[MyDestViewController class]])
{
MyDestViewController* viewController = (MyDestViewController*)[segue destinationViewController];
UIStoryboardPopoverSegue* popoverSegue = (UIStoryboardPopoverSegue*)segue;
popoverSegue.popoverController=[[UIPopoverController alloc] initWithContentViewController:viewController];
[popoverSegue.popoverController setPopoverContentSize:CGSizeMake(viewController.view.frame.size.width, viewController.view.frame.size.height)];
popoverSegue.popoverController.delegate=self;
[viewController setPopoverController:popoverSegue.popoverController];
}
}
I hope it helps you.
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.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I pass image ID to next view by using Storyboards in iOS
My code is:
- (IBAction) buttonPressed:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
DestinationViewController *destinationViewController = [storyboard instantiateViewControllerWithIdentifier:#"Destination"];
destinationViewController.title = #"image property";
[self.navigationController pushViewController:destinationViewController animated:YES];
[destinationViewController release];
}
But i got error is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Storyboard (<UIStoryboard: 0x68a0840>) doesn't contain a view controller with identifier 'Destination''
First throw call stack:
(0x13d4022 0x1565cd6 0x448fef 0x39f6 0x13d5e99 0x2114e 0x210e6 0xc7ade 0xc7fa7 0xc7266 0x463c0 0x465e6 0x2cdc4 0x20634 0x12beef5 0x13a8195 0x130cff2 0x130b8da 0x130ad84 0x130ac9b 0x12bd7d8 0x12bd88a 0x1e626 0x2902 0x2875)
terminate called throwing an exception(lldb)
Any thoughts?
Thanks in advance.
You need to set the view controller's identifier to "Destination" in IB. The following iextracted from Apple's document on UIStoryBoard.
An identifier string that uniquely identifies the view controller in the storyboard file. You set the identifier for a given view controller in Interface Builder when configuring the storyboard file. This identifier is not a property of the view controller object itself and is only used by the storyboard file to locate the view controller.
The answer involving setting the identifier correctly is probably your problem. However, it seems like you're doing this in a kind of hacky way by getting a reference to the storyboard and having it instantiate the controller.
Instead, try creating a push segue between the view controller and the DestinationViewController in the storyboard editor and do this:
- (IBAction)buttonPressed:(id)sender {
[self performSegueWithIdentifier:#"yourSegueIdentifierHere"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DestinationViewController *controller = [segue destinationViewController];
[controller setImage: ... ];
}
You can use the
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
method to pass data between view controllers using
myViewController *viewController = [segue destinationViewController];
to specify your destination.