In my CollectionView I display two “object types” in CollectionCells. What I would like to do is when the user clicks on a cell, in the “didSelectItemAtIndexPath” determine the object selected and use “performSegueWithIdentifier” to segue to the appropriate DetailTableViewContoller to display the details of the “object”.
The way I have the setup is NavigationController segue (root) to MyCollectionView segue (push) to DetailTableView. The CollectionCell in the MyCollectionView is configured to segue to the DetailTableView.
Starting simply, when I have the following code in place, select a cell in MyCollectionView it segues to the DetailTableView correctly but when I select the Back button in the DetailTableView it fails.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"MySegueIdentifier" sender:self];
}
I get the dreaded
[6313:a0b] nested push animation can result in corrupted navigation bar
[6313:a0b] Finishing up a navigation transition in an unexpected state.
Navigation Bar subview tree might get corrupted.
If I comment out the performSegueWithIdentifier… line it segues correctly and the Back button in the DetailTableView works perfectly.
Similar code pattern works fine in TableViewController seque to DetailViewController.
Any suggestions? Is it proper to set the segue up from the CollectionCell to the DetailViewController?
Figured this out. Having the segue at the cell is incorrect. Setting it at the Controller level is correct.
Related
This has been a headache for few hours now and I finally found out what is actually happening, but I don't know how to solve this issue.
I've got List.h with UITableView properly connected from storyboard:
#property (strong, nonatomic) IBOutlet UITableView *tableView;
Then there's List.m where I set delegates and datasource for my UITableView:
// Set tableview datasource and register class for cell reuse
self.tableView.dataSource = self;
[self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:#"cell"];
// Set tableview delegate
self.tableView.delegate = self;
// Set tableview cells style
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = [UIColor colorWithWhite:0.1f alpha:1.0f];
// Set tableview frame
self.tableView.frame = CGRectMake(0.0, 35.0, self.tableView.frame.size.width, self.tableView.frame.size.height-35.0);
Then on NSNotification I'm trying to [self.tableView reloadData]:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doUpdateAppBefore:) name:UIApplicationWillEnterForegroundNotification object:nil];
Also I'm reloading data of tableView on every UIApplicationBecomeActive notification.
Before I close my app and open it up from background to find out if it's reloads the data, I'm moving to another UIViewController and going back, which causes that somehow identifier of my self.tableView changes. I'm checking it in:
NSLog(#"Calling reloadData on : %#");
and at the beginning it gives me:
Calling reloadData on <UITableView: 0x9b09400;....
but after I segue back from another UIViewController it gives me:
Calling reloadData on <UITableView: 0x9b4b000;
which causes that it doesn't actually reload the data after I open up the app from the background state.
I've been thinking... when I segue back from another viewcontroller, viewDidLoad fires again, is it possible that it somehow sets tableView.delegate again and changes something? Just thinking...
Thank you very much for your answers.
It sounds like you have a view controller, push a modal view on top of it, and then want to go back to the original view controller when you're done. So, you set up a modal segue in your storyboard, and then a second modal segue to go back. The problem is that your second modal segue doesn't return to the original view controller, but it creates a new instance of that view controller, and now your have the original view controller, the second view controller, and an unwanted third view controller. Instead of creating a segue, which creates the third view controller, you need to dismiss the second view controller, which then gets you back to your original view controller, and therefore also your original table view. So what you want to do is get rid of the second segue and replace it with an IBAction, put something like
- (IBAction)goBack:(id)sender;
in your .h file. Connect that to your button or whatever you're using to trigger the segue now. Then, in your implementation file, dismiss the modal view like so:
- (IBAction)goBack:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
That should get you what you want.
Alternately, and this is probably better, you can use an unwind segue as well. Go to List.h, and create this method:
- (IBAction)unwind:(UIStoryboardSegue *)segue;
Then just implement it, you can leave it blank for now. Then, drag from the button that currently triggers the segue to Exit, and select the Action Segue unwind. That will also get you back.
Currently I have a UIView with a UItableview cell added to the subview, like so,
When the button is pressed, it pushes to another navigation controller whos subclass is a uitableviewcontroller....
So When the user clicks on one of these tableviewcells... I want the viewcontroller to be poped from the navigation stack and go back to the previous view that is listed first.
I implemented the UITableviewcontroller method listed below with the following implementation.... but nothing occurs :P....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Any ideas?
If you are pushing the view controller onto the navigation controller's stack i.e. pushViewController:animated:, you should pop out current view controller using popViewControllerAnimated: method.
If you want to go to a view controller not just one level below the current view controller (or top view controller in UINavigationController's jargon), you can use popToViewController:animated:. And to go to the root view controller of the navigation controller, use popToRootViewControllerAnimated:.
But if you are presenting a view controller modally i.e. presentViewController:animated:completion:, then only you use the dismissViewControllerAnimated:completion: method to dismiss the presented view and go to the presenting view controller.
I have UITabBarController, one of the tab points to a UINavigationController. The UINavigationController rootViewController is of class BGProfileView which shows users' profile
At viewDidAppear, I arranged that if users didn't logged in it will push a BGLogin view controller.
[BGLogin alreadyLoggedin:self.navigationController hideBackButton:YES anddoBlock:^{
[self whatToDoAfterLogin];
}];
Now everything is fine but with one minor issue. If I press the tab again, BGLogin will be poped out of UINavigationController.
I have no idea what makes that BGLogin poped out.
If I select a different tab and then click back to the BGProfile tab, this doesn't happen. It just happens when I click the same active tab. So I am in BGProfile tab, and I click that tab again. Basically it happens when I select the active tab that should simply do nothing. In fact, it does do nothing on others.
I put a breakpoint in viewWillDisappear and this is what I see:
As you see, viewDidAppear is called by the main loop. But why the hell the mainloop call viewDidAppear? Usually there is a code saying things like nav popViewController
Chances are there . that your tab bar controller is pushing new navigation controller with root view controller. and you interpreting that it's popping out. When same tab is selected you need to tell your tabBarController to not to do anything explicitly.
Example
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
UIViewController *tbSelectedController = self.tabBarController.selectedViewController;
if ([tbSelectedController isEqual:viewController]) {
return NO;
}
return YES;
}
I am currently facing a problem that I wanted to segue back to the same view controller by click on a table view cell. For your information, my table view is generated programatically and each of the cells are using NIB files to control.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath (NSIndexPath *)indexPath
{
if( indexPath.row == 0 ){
NSLog(#"Clicked");
}
}
When I clicked on the cell at position number 0, the log does came out. So now I wanted to segue back to the same view controller instead to the others. From the storyboard, it is impossible for me the drag the segue it is because the view controller is only have a scroll view.
E.g: Inside View Controller A have 5 cells, when I clicked on the first cell, it will segue back to View Controller A and pass data.
Please advise ya if you need to see more of my codes. Thanks viewer!
If I understand you correctly, you want to select a row in your tableView and have it performSegueWithIdentifier back to this same/current UITableViewController?
To do this:
In storyboard, ctrl+drag from the exposed table view cell to the yellow view controller icon in the black bar below. This creates the segue to self view controller.
Click on the segue, and add an identifier; e.g. "segueToSelf"
In your didSelectRowAtIndexPath method, add the following:
[self performSegueWithIdentifier:#"segueToSelf" sender:self];
I have a universal app, where I am sharing the same controller for a IPad and IPhone storyboard.
I have put a UILongPressGestureRecognizer on a UITableView, that when a cell is pressed on iPhone it calls an action that perform a segue:
-(IBAction)showDetail:(id)sender {
UILongPressGestureRecognizer *gesture = (UILongPressGestureRecognizer*)sender;
if (gesture.state == UIGestureRecognizerStateBegan) {
CGPoint p = [gesture locationInView:self.theTableView];
NSIndexPath *indexPath = [self.theTableView indexPathForRowAtPoint:p];
if (indexPath != nil) {
[self performSegueWithIdentifier:SEGUE_DETAIL sender:indexPath];
}
}
}
the segue is a detail view performed as a 'push'. The first thing you should notice is that the sender is an NSIndexPath, is the only way I found for passing the selected cell. Maybe there's a better solution.
Everything works fine, in a sense that the segue is performed, and before the prepareForSegue is called too.
However it happens that on iPad, I have changed the segue identifier to Popover.
Now things are working in part, the segue is performed, but prepareForSegue is not called and therefore the destination view controller is not set up as it should be.
What am I doing wrong ?
What I have discovered so far, is that with any segue identifier that is not popover these are the invocations made by iOS:
prepareForSegue (on source controller)
viewDidLoad (on destination controller)
while in popover segue the invocation order is:
viewDidLoad (on destination controller)
prepareForSegue (on source controller)
just because I put all my logic in viewDidLoad, the controller was not properly initialized, and a crash happened. So this is not exactly true that prepareForSegue is not called, the truth is that I was getting an exception, and I wrongly mistaken as prepareForSegue not getting called.
I couldn't put everything in viewWillAppear because a call to CoreData had to be made and I didn't want to check if entities were ok each time the view display.
How did I solve this ? I created another method in destination controller
-(void)prepareViewController {
// initialization logic...
}
and changing the prepareForSegue method in source controller itself:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
MyViewController *mvc = (MyViewController*)[segue destinationViewController];
// passing variable
// with segue style other than popover this called first than viewDidLoad
mvc.myProp1=#"prop1";
mvc.myProp2=#"prop2";
// viewWillAppear is not yet called
// so by sending message to controller
// the view is initialized
[mvc prepareViewController];
}
don't know if this is expected behavior with popover, anyway now things are working.
I've noticed that the boiler plate code for Xcode's Master-Detail template (iPhone) uses the following pattern for configuring the detail VC's view:
the detail VC's setters (for properties) are overwritten in order to invoke the configureView method (configureView would update all your controls in the view, e.g., labels, etc.)
the detail VC's viewDidLoad method also invokes the configureView method
I did not follow this pattern the other day when I was trying to re-use a detail VC in my movie app, and this gave me trouble.
I don't have much experience with popovers; however, if the pattern above is used with a detail VC that is displayed inside a popover, then wouldn't the detail VC's view get configured when you set the detail VC's properties from within the prepareForSegue method?