Objective-C, Storyboard: instantiateViewControllerWithIdentifier returns nil - objective-c

I have a UITableViewController with a storyboard push segue linking from the prototype cell to a detail page, a regular old UIViewController. In the storyboard, the detail ViewController has an identifier, and the segue has an identifier which is the same as the detail identifier except that the first letter is lowercase. Furthermore, the detail ViewController has a "custom class" (AttractionDetailViewController) selected in the class pulldown.
Doesn't work. The problem is that instantiateViewControllerWithIdentifier:#"AttractionDetails returns nil.
Relevant code. First the prepareForSegue method which the debugger has never entered.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"attractionDetails"])
{
AttractionDetailViewController *attrDetailVC = [segue destinationViewController];
}
}
Instead it goes into this method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//AttractionDetailViewController *attrDetailVC = [[AttractionDetailViewController alloc] init];
AttractionDetailViewController *attrDetailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"AttractionDetails"];
NSIndexPath *selIndexPath = [self.tableView indexPathForSelectedRow];
attrDetailVC.theAttraction = [attractions objectAtIndex:indexPath.row];
[self.navigationController pushViewController:attrDetailVC animated:YES];
}
Since instantiateViewControllerWithIdentifier returns nil it throws an exception of course. The really interesting thing is, if I use the alloc init line instead, it works, but the screen is all black.
Anyway, I've read up about this and tried a few different things and I'm still stymied. Does anyone have any suggestions?

The problem is that you didn't instantiate your master view controller (the UITableViewController) from the storyboard, so its storyboard property is nil.

Related

Can not get correct value of indexPath.row in prepareForSegue when UILabel or UIImageView in a cell is tapped,

I am developing iOS App with UITableView.
When UILabel or UIImageView in a cell is tapped, segue is called and move to another screen.
I am writing down the following code, however I can not get correct value of indexPath.row in prepareForSegue.The value is always 0.
- (void)goToDetail: (UITapGestureRecognizer *)sender{
[self performSegueWithIdentifier:#"showDetail" sender:self];
NSLog(#"aaa");
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *detailViewController = [segue destinationViewController];
detailViewController.article = _article[indexPath.row];
NSLog(#"bbb%d",indexPath.row);
}
}
NSLog says following.
2014-08-08 17:50:23.267 eleventhtest[27374:60b] bbb0
2014-08-08 17:50:23.269 eleventhtest[27374:60b] aaa
Could you tell me how to solve this problem?
As just one of the possible options, you can set tag for label/imageView and gesture recognizer when you are creating cells. The tag would be the same as the indexPath.row.
In goToDetail method you can assign this tag to some internal property, and then use it in prepareForSegue.
If you're calling goToDetail in the didSelectRowAtIndexPath method AND have a segue wired from the prototype row to your detailViewController, then the segue fires BEFORE the goToDetail method.
Solution: segue directly from your view controller to your detailViewController and call [self performSegueWithIdentifier:#"showDetail" sender:self]; in the didSelectRowAtIndexPath method.
Also you can create a NSIndexPath property and set the value in the didSelectRowAtIndexPath method, the segue would then use that property instead of trying to access the [self.tableView indexPathForSelectedRow].

How to segue from a custom delegate and datasource

I have a view with 2 tableviews in it. This view has the controller PlayerDetailController
Now to control the 2 tableviews I have 2 other controllers.
tablePlayersDataSourceDelegate
tablePlayerNewsDataSourceDelegate
I ctrl-dragged from the PlayerDetail view to the newsView to make a segue. In my tablePlayerDataSourceDelegate I've the following methods to preform this segue.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"showPlayerDetailNews" sender:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = (NSIndexPath *)sender;
PlayerNews *news = [_tableSource objectAtIndex:indexPath.row]; // ask NSFRC for the NSMO at the row in question
if ([segue.identifier isEqualToString:#"show detail"]) {
[segue.destinationViewController setImageURL:[NSURL URLWithString:news.image]];
[segue.destinationViewController setNewsTitle:news.title];
[segue.destinationViewController setNewsDescription:news.content];
[segue.destinationViewController setNewsCopy:news.image_copyright];
[segue.destinationViewController setNewsUrl:news.url];
[segue.destinationViewController setNewsShortDescription:news.summary];
}
}
But when I test I get the following error.
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<tblPlayerNewsDatasourceDelagete: 0x1e5e44c0>) has no segue with identifier 'showPlayerDetailNews''
I get why this is giving the error. Because the playerDetail view uses the class playerDetailController and I am trying to do te segue in the tablePlayerNewsDelegate class. Do you maybe know a way to work around it?
EDIT
Here you see a picture of what I am talking about
So you can see the two tableviews in the playerDetailView. When a cell in the bottom tableview is clicked it should go to the next view.
EDIT2
This is what I do in my playerDetailController to fill up my tableview. I've put this in my viewDidLoad.
tabelPlayerNews=[[tblPlayerNewsDatasourceDelagete alloc]init];
[tabelPlayerNews setTableSource:_newsArray];
tblNews.dataSource=tabelPlayerNews;
tblNews.delegate=tabelPlayerNews;
tabelPlayerNews.view=tabelPlayerNews.tableView;
I'm not sure whether I understand that or not but in general....
To use performSegueWithIdentifier: the drag for the segue should start at the controller that will make the call, not at a view or other object. I think that's what the error message is telling you: assuming there's a segue called "showPlayerDetailNews" in your storyboard, it doesn't belong to the correct object.

iOS 5, Storyboards, ARC: Navigation Controller doesn't work in custom delegate

The prequel for this question is here. Now I've got another problem. I cannot navigate despite of the didSelectRowAtIndexPath getting invoked while tapping on cell
KMList *detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"KMs"];
detailViewController.fromPeriod=self.fromPeriod;
detailViewController.period1=self.entityID;
[self.navigationController pushViewController:detailViewController animated:YES];
What the problem. Is it because I am kind of off the storyboard?
P.S No warnings or errors. Just nothing happens.
Thanks in advance.
You should not push the new view controller in code when using a storyboard. This is done automatically. Instead make a push-Segue in Interface Builder and give it an identifier. Then in your view controller you overwrite prepareForSegue:sender: and check for the identifier like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"<your segue identifier>"]) {
KMList *detailViewController = [segue destinationViewController];
detailViewController.fromPeriod=self.fromPeriod;
detailViewController.period1=self.entityID;
}
}

UIWebView on top of UINavigationController stack not shown

I have a UIViewController "PdfViewController" with a view containing an UIScrollView wich is containing an UIWebView, designed by storyboard. I'm using storyboard in Xcode 4.2 with ARC.
If I start PdfViewController as initial ViewController the webview is shown as expected.
I want the PdfViewController to show a PDF from a list of PDF's. So I made a NavigationController containing a TableViewController with the document titles.
If I try to show the PdfViewController by "tableView didSelectRowAtIndexPath" the view stays black and doesn't load the selected document.
There is no errer message from Xcode.
The viewDidLoad and the viewWillAppear from PdfViewController get called.
Here the initialisation of the PdfViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PdfViewController *pdfViewController = [[PdfViewController alloc]init];
[pdfViewController setFileName:#"test"];
[self.navigationController pushViewController:pdfViewController animated:YES];
}
-It seems, the problem is that the PdfViewController does not load the corrsponding nib by itself.
If I make a seque from the TableViewCell, the PdfViewController is loaded correctly.
I changed the code to:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.navigationController.viewControllers.lastObject setFileName:#"test"];
}
The only thing to know here, is that the viewDidLoad und viewWillAppear are called before the execution of tableView didSelectRowAtIndexPath, so the PDF has to be loaded in viewDidAppear.
With Xcode 4.2 or later, you can pass the data between two view controllers via segue.
Please Check:
How to pass prepareForSegue: an object
Your code may look like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([[segue identifier] isEqualToString:#"__YOUR_SEGUE_STRING__"]) {
PdfViewController *pdfViewController =
(PdfViewController*)[segue destinationViewController];
//
// write your code here
//
[pdfViewController setFileName:#"test"];
}
}

How do I use a property in -prepareForSegue?

Here is my code, posted in view1:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([segue.identifier isEqualToString:#"toLevels"])
{
levelViewController *vc = [segue destinationViewController];
vc.currentEnchantmentSelected = self.title;
}
}
This doesn't send the value of "self.title" to the variable in "levelViewController". Why is this? It sends it if I use a string.
Edit (levelViewController):
#synthesize enchantment = _enchantment;
- (enchantmentViewController *)enchantment
{
if (!_enchantment){
_enchantment = [[enchantmentViewController alloc] init];
}
return _enchantment;
}
the #property enchantment is declared in the header.
So from the chat, the issue was that you were setting this property in didSelectRowAtIndexPath:, which is actually called after prepareForSegue:sender: with storyboards. (when the segue is attached to a cell in a table)
So the solution is just to do that work directly in prepareForSegue:sender: instead. The sender will be the cell that was tapped. And if you need the indexPath for that cell you can use:
[self.tableView indexPathForSelectedRow]
Good luck with the app.