How do I use a property in -prepareForSegue? - objective-c

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.

Related

How to show the data of a textfield to the label of a tableview cell?

I am new to iOS developing. I am building a project where I want to show the text of a textfield of a view controller to the label of a table view cell of another viewcontroller. Can anyone tell me just the logic how to do that?
Since you haven't posted any code, I will try to explain it to you.
You will have to create event for button, and there you can do something like [self performSegueWithIdentifier:#"yourSegue" sender:self];
Next thing is to create property in your new ViewController that will accept your value.
After that, in your prepareForSegue you can do NSString *text = [self.textField text];, get your new VC from segue.destinationViewController; and assign your value there.
So, in the end you will have :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if(segue.identifier isEqualToString:#"yourSegue"])
{
YourViewController *vc = segue.destinationViewController;
vc.accpetValue = [self.yourTextField text];
}
}
and in your action for button:
- (IBAction)yourButton_pressed:(id)sender
{
[self performSegueWithIdentifier:#"yourSegue" sender:self];
}
If you have tableViewController there, and you want to assign this value to the cell on the same screen, on button you can obtain the value with same way as above, and in you cellAtIndexPath you can do the following:
if(self.yourTextBox && [self.yourTextBox text] && ![[self.yourTextBox text] isEqualToString:#""])
{
cell.textLabel.text = [self.yourTextBox text];
}
else
{
//something else
}
And on your button event you can do
[self.tableView reloadData];
Hope it helps.
If the view controller with the cell is open from the view controller with the textfield, is quite easy.
You can create a property NSString in the second view controller .h and then you can call it when you open this from the first.
SecondViewController * cotroller = [[SecondViewController alloc]init];
controller.string = [textField text];
[self presentViewController:controller animated:YES completion:^{
}]

Retrieving UITableViewCell Label Values For Passing To Modal Edit View

I am trying to pass existing attribute values for a managed object, to a modal segue view for editing. From "editing mode," I'm going from a UITableViewController subclass (BreadRackTableViewController), using a modal segue to a UIViewController subclass (EditBreadRackTableViewController) by means of an embedded UINavigationController. Normal TableViewCell selection pushes to another UITableViewController subclass, but that's not important for the time being.
I've added properties to my editing view controller header (and #synthesized), to function as getters:
#property (nonatomic, strong) id existingBreadRackDataName;
#property (nonatomic, strong) id existingBreadRackDataDetails;
What I'm trying to do is populate the edit view's fields with the existing Core Data Entity Attributes, which would have been previously set by the user (in theory). I can get the data to show up if I pass along a literal string value, but I need to retrieve and pass the existing data.
Here's what I have currently:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"editBreadRackSegue"])
{
EditBreadRackTableViewController *ebrtvc = (EditBreadRackTableViewController *)[segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
BreadRackClass *breadRackClass = [[self fetchedResultsController]objectAtIndexPath:indexPath];
// [ebrtvc setBreadRackClass:breadRackClass];
ebrtvc.existingBreadRackDataName = breadRackClass.breadRackDataName;
ebrtvc.existingBreadRackDataDetails = [breadRackClass valueForKey:#"breadRackDataDetails"];
}
}
These last two lines of code...
ebrtvc.existingBreadRackDataName = breadRackClass.breadRackDataName;
ebrtvc.existingBreadRackDataDetails = [breadRackClass valueForKey:#"breadRackDataDetails"];
...they both return nil. If I specify a literal string though, that literal string shows up where it needs to.
All of that said, my question is:
How do I retrieve the attribute's key value for that selected row (or just the textLabel and detailTextLabels), and assign it to my setter?
Solution:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"editBreadRackSegue"])
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
BreadRackClass *breadRackClass = [[self fetchedResultsController]objectAtIndexPath:indexPath];
[segue.destinationViewController setExistingBreadRackDataName:[breadRackClass valueForKey:#"breadRackDataName"]];
[segue.destinationViewController setExistingBreadRackDataDetails:[breadRackClass valueForKey:#"breadRackDataDetails"]];
}
// code for other segues here...
}
I ended up removing the ebrtv variable, and cleaned up the code a little bit. What worked though, was using the indexPathForCell:sender instead of indexPathForSelectedRow when declaring my indexPath for breadRackClass.
I think the indexPathForSelectedRow could be a problem.
You can just pass the indexPath from the didSelectRowAtIndexPath to the prepareForSegue , like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"editBreadRackSegue" sender:indexPath];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"editBreadRackSegue"])
{
NSIndexPath *clickedIndexPath = (NSIndexPath*)sender;
if( [clickedIndexPath isKindOfClass:[NSIndexPath class]] ){
BreadRackClass *breadRackClass = [[self fetchedResultsController]objectAtIndexPath:clickedIndexPath];
EditBreadRackTableViewController *ebrtvc = (EditBreadRackTableViewController *)[segue destinationViewController];
ebrtvc.existingBreadRackDataName = breadRackClass.breadRackDataName;
ebrtvc.existingBreadRackDataDetails = [breadRackClass valueForKey:#"breadRackDataDetails"];
}
}
}
Let me know if it works for you.

Objective-C, Storyboard: instantiateViewControllerWithIdentifier returns nil

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.

How do I call the following method from this IBAction?

How do I call the following method from this IBAction? I want to call the prepareForSegue: method instead of the NSLog(#"clicked");
Here's the IBAction:
- (IBAction) annotationViewClick:(id) sender {
NSLog(#"clicked");
}
Here's the method:
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"ShowMoreInfo"]) {
// Get reference to the destination view controller
MoreDetailViewController *mdvc = [segue destinationViewController];
[mdvc setSelectedItemName:[NSString stringWithFormat:#"%#", placeName.text]];
[mdvc setSelectedItemAddress:[NSString stringWithFormat:#"%#", placeFormattedAddress.text]];
[mdvc setSelectedItemWeb:[NSString stringWithFormat:#"%#", placeWebsite.text]];
[mdvc setSelectedItemRating:[NSString stringWithFormat:#"%#", placeRating.text]];
// [mdvc setSelectedItemDistance:[NSString stringWithFormat:#"%#", placeDistance.text]];
}
}
thanks for the help!
prepareForSegue: method calls automatically when you performing a segue.
To perform a segue you should use performSegueWithIdentifier:,
for ex. [self performSegueWithIdentifier:#"showGuide" sender:self];,
in this case you need assign identifier to a segue in Xcode Interface Builder.
id try something like
UIStoryBoardSegue *segue = ... setup the one you want
[self performSelector:#selector(prepareForSegue:) withObject:seque afterDelay:0];
ive never worked with storyboards - but thats how you call a selector :)

How to set the delegate with a storyboard

I've been debating with this for a while now, hope you can help me.
I've been creating an app using storyboards mostly, I have a point where I popup a modal box to add a new record, popup works fine, the problem is dismissing it.
I've followed Apple's instructions on how to properly close modal boxes using delegates, and that works fine, except I need to add a navigation controller to my modal box, because the add process requires two steps (here fullscreen):
The problem lies in setting the delegate, so here are my two questions:
1- In my root view class (My Tab) is a delegate of the Add class (the modal), everything is set up right except this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAdd"]) {
[[segue destinationViewController] setDelegate:self];
}
}
The problem lies in that [segue destinationViewController] is returning the navigationcontroller and not the AddDrinkViewController class (see the storyboard). How do I get around this? If I remove the navigation controller altogether, the code works fine setting the appropriate delegate.
2- Is there any way to set the delegate by dragging the outlets in the storyboard?
Thanks!
You're right, the destinationViewController will be a UINavigationController in this case. I wrote a category to handle this common situation:
// category .h file
#interface UIStoryboardSegue (NavControllerExtensions)
// Gets destinationViewCotroller. But if that controller
// is a NavigationController, returns the nav controller's
// top level view controller instead.
#property (readonly) id topLevelDestinationViewController;
#end
// category .m file
#implementation UIStoryboardSegue (NavControllerExtensions)
- (id)topLevelDestinationViewController
{
id dest = self.destinationViewController;
if ([dest isKindOfClass:[UINavigationController class]]) {
UINavigationController* nav = dest;
dest = nav.topViewController;
}
return dest;
}
#end
So now you can just do this in any of your prepareForSegue methods, and not need to worry about whether there even exists a NavigationController:
[[segue topLevelDestinationViewController] setDelegate:self]
// another example:
MyViewController *vc = segue.topLevelDestinationViewController;
vc.delegate = self; // etc.
To answer your second question, I couldn't find a way to set the delegate within IB.
I found a shorter way in my case (same as yours):
AddDrinkViewController *controller=[[[segue destinationViewController]viewControllers]objectAtIndex:0];
Basically you need to create an
Instance of UINavigationController and assign destinationViewController to it
and grab its topView controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAdd"]) {
UINavigationController *navigationController = segue.destinationViewController;
AddDrinkViewController *addDrinkcontroller = (AddDrinkViewController *)navigationController.topViewController;
addDrinkcontroller.delegate = self;
}
}