performSegueWithIdentifier on didSelectRowAtIndexPath - ios

With this code every time user click on any cell will perform the segue again and again , I am wondering how could I keep track of the loaded view to keep data when switching views and not an infinite new viewcontroler.
Thanks -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
[self.navigationController
performSegueWithIdentifier:#"rep" sender:self];
} else if (indexPath.row == 1) {
[self.navigationController
performSegueWithIdentifier:#"rep1" sender:self];
}
}

Try my other approach first, but if you really need a maintain a pointer to the new view controller you could try this approach. This should perform the segue once, creating the reference to the view controller which will subsequently be manually pushed into the navigation controller.
Override the view controller methods:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
if(self.myViewController == nil){
return YES;
}else{
[self.navigationController pushViewController:self.myViewController animated:YES]
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
self.myViewController = (MyViewController*)segue.destinationViewController;
self.myViewController.customVar = 1; //perform initial customization
}
What do I know though, I've never used Storyboards...

Perhaps an alternative to maintaining a reference to the view controller would be to customize the view controller prior to seque.
Override the view controller method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
MyViewController *targetController = (MyViewController*)segue.destinationViewController;
targetController.customVar = 1;
}
The default implementation of this method does nothing. Your view controller overrides this method when it needs to pass relevant data to the new view controller. The segue object describes the transition and includes references to both view controllers involved in the segue.

You don't need to take new View controller each and every time for each row unless and until you want it customized. This will make large amount of view controllers on storyboard.
So, Just command drag segue from controller A to B. Example: if A is tableViewController and B is simple VC where you are displaying data of table's row then command drag from whole tableViewController to B. Now this will act as common segue with one identifier only.
So in your code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Set your B's label/property etc to cell's data or anything so that it will reflect in B.
B.label = cell.text;
[self performSegueWithIdentifier:#"Identifier" sender:self];
}
Hope this helps.

I think you can keep the new viewController's pointer. Then next time you can use it like this:
[self.navigationController pushViewController:thePointer animated:YES]
I very seldom use Storyboard. So I am not sure it will work.

Related

UIViewController appears as Black screen when doing programmatically

UIViewController appears as black screen when handling programmtically
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *user = (NSString *) [self.friends objectAtIndex:indexPath.row];
ChatViewController *chatController = [[ChatViewController alloc] initWithUser:user];
[self presentModalViewController:chatController animated:YES];
}
This below given code is in the chatviewcontroller
- (id) initWithUser:(NSString *) userName {
if (self = [super init]) {
chatWithUser = userName;
}
return self;
}
and when i do it using storyboard segue then only tableview row gets selected but doesn't shows ChatViewController
else if ([segue.identifier isEqualToString:#"showChatView"]) {
ChatViewController *viewController = (ChatViewController *)segue.destinationViewController;
viewController.chatWithUser = friends;
}
If anyone can figure out what i m doing wrong. Will appreciate so much.
Thanks for help though.
presentModalViewController:animated: is deprecated (since iOS 6), you should use presentViewController:animated:completion:
However, it looks like you are using a segue to get to your ChatViewController, so you shouldn't even have to present the view controller since this is handled by Interface Builder. If your segue is set up correctly, replace presentModalViewController:animated: with [self performSegueWithIdentifier:#"showChatView" sender:nil];
EDIT
You should just move your ChatViewController setup to the prepareForSegue:sender: method, like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *user = (NSString *)[self.friends objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"showChatView" sender:user];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChatView"]) {
NSString *user = (NSString *)sender;
ChatViewController *chatVC = (ChatViewController *)[segue destinationViewController];
// No need to have an init method with the user property since Interface Builder does that for you.
chatVC.chatWithUser = user; // Expose this property in ChatViewController's header file if it's not already
}
That should be all you need to do in your code.
Presenting Versus Showing a View Controller
The UIViewController class offers two ways to display a view controller:
The showViewController:sender: and showDetailViewController:sender: methods offer the most adaptive and flexible way to display view controllers. These methods let the presenting view controller decide how best to handle the presentation. For example, a container view controller might incorporate the view controller as a child instead of presenting it modally. The default behavior presents the view controller modally.
The presentViewController:animated:completion: method always displays the view controller modally. The view controller that calls this method might not ultimately handle the presentation but the presentation is always modal. This method adapts the presentation style for horizontally compact environments.
The showViewController:sender: and showDetailViewController:sender: methods are the preferred way to initiate presentations. A view controller can call them without knowing anything about the rest of the view controller hierarchy or the current view controller’s position in that hierarchy. These methods also make it easier to reuse view controllers in different parts of your app without writing conditional code paths.
Refer this link to know about what is the difference between segue programmatically and using interface builder. I hope it is helpful.
#timgcarlson's answer is great for solving your problem.
As per #Sneha's suggestion I have added a paragraph that feel is useful.

How to populate a cell in UITableView with an edited content?

Maybe I didn't ask it properly, because I'm not really sure how, so I will explain myself:
I have a simple notes app with two VC's:
table view controller - to list all the notes.
view controller - to create new notes.
In the table vc I have an unwindToList method that create an instance of the create notes page to get the note object that will be passed and if its not nil I will add it to the notes array and reload the data;
- (IBAction) unwindToList: (UIStoryboardSegue *) segue
{
NMCreateNotesViewController *source = [segue sourceViewController];
NMNote *note = source.note;
if (note != nil) {
[self.notes addObject:note];
[self.tableView reloadData];
}
}
and an prepareForSegue method that will identify if this segue is a note segue (this is the segue I JUST want to preform editing in the create note page and when a user taps the save button, to save it in the same cell that the segue came from...):
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(UITableViewCell *)sender
{
if ([[segue identifier] isEqualToString:#"noteSegue"]) {
NMCreateNotesViewController *destination = [segue destinationViewController];
NSInteger indx = [self.tableView indexPathForCell:sender].row;
NMNote *note = self.notes[indx];
destination.passedInString = note.content;
}
}
In the view controller that create the notes I have a prepareForSegue method that looks like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) return;
if (self.textField.text.length > 0) {
self.note = [[NMNote alloc] init];
self.note.content = self.textField.text;
}
}
Now, my problem is that when i'm editing a note from the noteSegue i'm creating a new note with the edited content instead of populating the same cell of the segueNote with the new edited content...
this is how the problem looks like:
Please help me to figure this out :/
Appreciate it!
I don't quite understand your explanation.
You have a table view controller that is a master view controller and a note view controller that is a detail view controller.
The usual way to handle communications back from a detail view controller to it's master is to set up a delegate.
Define a delegate property in the detail (notes) VC that will point back to the master.
In the master's prepareForSegue, set destination.delegate = self.
Then, define a protocol for the delegate that the detail view controller will use to tell the master that the contents of a list item has changed.
You will probably need to tell the detail view controller the indexPath of the item that's being edited in the master-to-detail segue, and then before unwinding to the master, invoke a delegate method that gives the edited item to the master, along with it's indexPath so the master can save the changes and redisplay that cell.

How do I segue to different detail view controllers?

In a split view controller app,how can I segue to different detail view controllers upon selecting a table row in the master view controller?
Just to be clear, I need the detail view controller to be replaced when I select a row in the master view controller. How do I wire up the view controllers? From the split view controller? or from the detail view navigation controller?
Implement tableView:didSelectRowAtIndexPath: in the master table view's delegate. Depending on the value of the indexPath parameter, call [detailViewController performSegueWithIdentifier:sender:] with the segue identifier of your choice.
In your tableView:didSelectRowAtIndexPath: method, do this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"YourSegueIdentifier" sender:self];
}
If you need to perform different segues based on the selected row, do this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *segueIdentifier = nil;
switch(indexPath.row) {
case 0:
segueIdentifier = #"YourSegueIdentifier";
break;
case 1:
segueIdentifier = #"ADifferentSegueIdentifier";
break;
.
.
.
}
if (segueIdentifier != nil) {
[self performSegueWithIdentifier:segueIdentifier sender:self];
}
}
// Get detail navigation controller
UINavigationController *detailNavigationController = [splitViewController.viewControllers objectAtIndex:1];
// Push the detail view controller
[detailNavigationController pushViewController:anyDetailViewController animated:NO];
// You also might need to set the splitview controller's delegate to this view controller
splitViewController.delegate = anyDetailViewController;
Use this code:
UINavigationController *detailNavigationController =[[[self splitViewController] viewControllers] objectAtIndex:1];
[detailNavigationController pushViewController:"your_view_controller" animated:YES];
In your segue, set your style to "Push", and your destination to "Detail". Current will push the destination view controller onto your Master view, whereas Detail will push it into the "Detail" view. It's that simple. Then wire it up the same way you wire everything else up.
But be careful, if you don't implement a way for it to wait for a previous segue, you can get an "Unbalanced calls" error if a new Controller is pushed onto the detail view before it's done dismissing/pushing another one. Double tapping a cell in a table will do it.

Storyboards and programmatically views

I'm totally lost in this issue. I have been working with storyboards, I have created a navigation controller with tableviews and some stuff. There are Services in each row of the tableview and I need to create one detail view for each service.
There are a lot of services, so I can't create them in the storyboard. The idea is to download the Services from a webservice (number of parameters, types of each one, etc..) and add as textfields / buttons as appropriate to the Service.
So, my problems and questions are:
1) Can I combine Storyboards and views programmatically? When I create a NewView in MyTableviewClass, should I do it in my prepareforsegue method? How can I show it in the screen without loosing my navigation controller? this is what I have (it doesn't work: it says to me that there is no segue with name 'Service1' ) :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([[segue identifier] isEqualToString:#"NextLevel"]) {
[segue.destinationViewController setActualNodo:[actualNodo getSonAtIndex:indexPath.row]];
} else if ([[segue identifier] isEqualToString:#"Service1"]) {
CGRect bounds = self.view.bounds;
UIView *myview = [[UIView alloc] initWithFrame:bounds];
[myview setBackgroundColor: [UIColor redColor]];
[self.view addSubview:myview];
[self.view bringSubviewToFront:myview]; }
Any book or reference is welcomed but I couldn't find anything similar. Is this very complicated in iOS? I have done a similar thing in Java. I have read about generating interfaces dynamically with XIBs but, sincerely, I don't know what it is..
Thanks for all.
Yes you can create a StoryBoard with a view and then add views programmatically to it.
You should not try creating a view within your prepareForSegue method. This really should be used for passing objects to another ViewController.
I would suggest this to you. Go back to your StoryBoard and create a new UIViewController scene. Click on your first scene and CTRL drag to the new scene. Next, click on your segue and give it a name.
Step 1:
Create a new class called ServicesViewController and make sure it's a subclass of `UIViewController:
Step 2:
Go back to your StoryBoard and click on scene so that it is selected. Next, click on the Files Owner and finally click on the class info button (the third button) and finally select your ServiceViewController class you just created.
Step 3:
Back in your ServicesViewController in the didSelectRowAtIndex method call your seque:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"YOUR_SEGUE_NAME" sender:nil];
}
For now, clean out all the code in your prepareForSegue method and just get the transition down first.
In addition to Flea's answer, if you need to keep the navigation controller, just create a push segue in your storyboard by control dragging from the file owner icon (the yellow box under your view controller's view) of the table view controller to the ServiceViewController you added to the storyboard, this should show a popup window where you can select "push" as the type of the segue. Next, select the segue and in the attribute inspector (the fourth button, next to the one in the snapshot) and in the "Identifier" text field type in a unique identifier for your segue, such as serviceSegue.
At this point, using Flea's code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"serviceSegue" sender:nil];
}
And in the code you posted:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([[segue identifier] isEqualToString:#"serviceSegue"])
[segue.destinationViewController setActualNodo:[actualNodo getSonAtIndex:indexPath.row]];
}
I'm not sure what you are trying to do with the other segue "Service1", but if you want to change the view of the TableViewController, segues are not the way to do it. If anything you should do it in the didSelectRowAtIndexPath method depending on the row selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (You want to transition to other view controller)
[self performSegueWithIdentifier:#"serviceSegue" sender:nil];
else
Change your view here.
}
I hope this helps!
It sounds like you just have multiple cells in a tableView. Instead of using segues you can simply create different cells with different identifiers and show or hide them based on what services are detected in your services array which is populated from your web service.

Nested push animation, but there's no push?

I am getting this error when I try to preform a segue...
2012-11-14 20:24:54.133 MyApp[26266:c07] nested push animation can result in corrupted navigation bar
2012-11-14 20:24:54.486 MyApp[26266:c07] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2012-11-14 20:24:54.487 MyApp[26266:c07] Unbalanced calls to begin/end appearance transitions for <SheetDrillDownViewController: 0xa1bb980>.
Here is my setup: Initial view --> next view (UITableView) --> last view (UITable)
Each cell pushes to another view until the last view. I do not have a modal segue, i have a push segue... I have the push segues linked from the cell to the next view, each with different names... I have a perform segue line in my selectedRowAtIndex method. I have tried removing that method, with people saying I am double calling the segue, but then the cell when clicked turns blue, and doesn't push to anywhere... How can I fix this error? Thanks.
Here is my code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath) {
[self performSegueWithIdentifier:#"subjectDrillDown" sender:nil];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Attempting to identify segue...");
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"subjectDrillDown"]) {
NSLog(#"Segue has been identified...");
// Get reference to the destination view controller
SubjectDrillDownViewController *vc = [segue destinationViewController];
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
NSMutableDictionary *object = [tableDataSource objectAtIndex:selectedIndex];
NSString *key = [object valueForKey:#"key"];
[vc setSelectedItem:key];
NSLog(#"Switching to %# detail view...",key);
NSLog(#"Passing dictionary to %# view... (Source below)\n\n %#",key,[NSString stringWithFormat:#"%#", [tableDataSource objectAtIndex:selectedIndex]]);
[vc setDetailDataSourceDict:[tableDataSource objectAtIndex:selectedIndex]];
}
else {
NSLog(#"ERROR, DOUBLE CHECK SEGUE'S NAME!");
}
}
I have seen all of the other Stack Overflow questions, but most of the correct answers don't work for me...
EDIT:
The next view is able to load, but when I click the back button, there are suddenly two more views...
UPDATE:
I will try to post the number of times Attempting to identify segue... NSLogs, but my compiler suddenly won't run :(
You surely don't need to perform segue in didSelectRowAtIndexPath, if you have already configured it like cell -> next view.
But, removing the call from there doesn't work for you then you can try the segue from view controller (ctrl+drag from view area) to next view and keep the segue call in didSelectRowAtIndexPath
I always use one of the following methods
Method 1:
Segue from UITableViewCell to Next View Controller
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Nothing
}
Method 2:
Segue from Main View Controller to Next View Controller
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"subjectDrillDown" sender:nil];
}
The error you are getting is because you have segue and you perform the segue manually when selected - hence, it is resulting in nested push animation
Make sure the name of the segue is correct and you are using ONLY one of the above methods

Resources