I have two buttons triggering segues to two different UIViewCOntrollers, using this code:
- (IBAction)newTransButton:(UIButton *)sender
{
[self performSegueWithIdentifier:#"newTransSegue" sender:self];
}
- (IBAction)switchAccountButton:(UIButton *)sender
{
[self performSegueWithIdentifier:#"selectAccountSegue" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
if ([[segue identifier] isEqualToString:#"newTransSegue"])
{
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
AddTransactionVC *atvc = (AddTransactionVC *)navController.topViewController;
atvc.delegate = self;
WMMGTransaction *addedTransaction = (WMMGTransaction *)[WMMGTransaction MR_createInContext:localContext];
addedTransaction.account = self.currentAccount.name;
atvc.thisTransaction = addedTransaction;
}
else if ([[segue identifier] isEqualToString:#"selectAccountSegue"])
{
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
AccountSelectVC *acctSelVC = (AccountSelectVC *)navController.topViewController;
acctSelVC.delegate = self;
}
}
Activation of either button segues to the appropriate view controller, but causes this warning:
Warning: Attempt to present <UINavigationController: 0x7fb99b4dd430> on <FirstViewController: 0x7fb99b565dd0> whose view is not in the window hierarchy!
I have a Save and a Cancel Navigation bar button on each View controller. Other than as mentioned above, everything works as expected, except for the Cancel button on the View controller at newTransSegue, which dismisses the VC, but crashes the app with this error:
EXC_BAD_ACCESS (code = 1, address = 0x7f87394af29)
Here is the delegate method I use to dismiss that VC:
-(void)addTransactionViewControllerDidCancel
{
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
I've been at this for a couple of days, and have tried deleting the segues and recreating them in storyboard, as well as doing the same for the navigation controllers. I've gone off the rails somewhere, but can't see exactly where.
I could sure use some guidance. :)
OK, I studied the reference kindly provided by #Jay. Turns out I had seen it before, but only Part 1. In Part 2, I discovered a reference to Enable Zombie Objects, which I did. Now, when the app crashed, I was provided with this message: [_UILayoutGuide isDescendantOfView:], which pointed to an issue in the Storyboard.
Upon examining the Storyboard, I discovered that, surprisingly, the representation of the view controller in question was shaped differently than the surrounding view controllers. I wish I'd made a screenshot, but in the heat of the hunt, I didn't.
In any case, further research turned up this question (and its associated comments). My investigation revealed that, while I had Size Classes enabled, for some reason I can't explain, the size of the relevant View Controller under Simulated Metrics had been set to "Freeform." I reset it to "Inferred" and things appear to be operating normally now--no crashes. Wish I could explain it in detail, but I'm happy with the result!
Related
When a logged in user opens my application, they are sent to the main TabBarController from my AppDelegate, like so:
UITabBarController *tabBar = (UITabBarController *)self.window.rootViewController;
tabBar.selectedIndex = 2;
// (this is MainViewController in the tab bar)
Now, the user is in MainViewController. When the user selects a particular chat they'd like to enter, they are sent to the ChatViewController (not on the TabBarController), like so:
[self performSegueWithIdentifier:#"showChatSeg" sender:self];
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.destinationViewController isKindOfClass:ChatViewController.class]){
ChatViewController *destinationViewController = (ChatViewController *)segue.destinationViewController;
if(self.createdDialog != nil){
destinationViewController.dialog = self.createdDialog;
self.createdDialog = nil;
}else{
QBChatDialog *dialog = [ChatService shared].dialogs[self.selectedChat];
destinationViewController.dialog = dialog;
}
}
}
When this happens, I see a spike in memory usage, which makes sense. However, when the user leaves the ChatViewController and return to the MainViewController, like so:
- (IBAction)backButton:(id)sender {
[self performSegueWithIdentifier:#"fromChatToDashSeg" sender:nil];
// This is a storyboard segue back to the MainTabBarController
}
I get the following warning:
Attempt to present <MainTabBarController: 0x17ef28d0> on <ChatViewController: 0x17d6c940> whose view is not in the window hierarchy!
And the memory usage remains the same. And when the user enters a chat again, the memory continues to increase. Am I not dismissing the sending view controllers properly?
What you are doing is not going "back" but rather, you are presenting a copy of the previous view on top of the one you already have. That's why memory is building up, because you just keep stacking more and more views on top of eachother. Assuming you are using a modal segue to present your chat view, try calling:
[self dismissViewControllerAnimated:YES completion:nil];
It's because you're trying to perform a segue just to get back to your original location. All you need to do is dismiss your current modal view controller by calling [self dismissViewControllerAnimated:YES completion:nil];. Whenever you add a modal view to the view stack you want to call this method to exit, unless your intent is to add yet another view on top of the modal.
App crashes with this error
NSInvalidArgumentException :Pushing the same view controller instance more than once is not supported
Getting this error when trying to push another navigationcontroller from another navigation controller's UIBarButtonItem.
Message shows in console:
UINavigationController pushViewController:transition:forceImmediate:]_block_invoke + 0
This is the segue coding
else if ([segue.identifier isEqualToString:#"showGroupView"]) {
GroupView *groupView = (GroupView *)segue.destinationViewController;
[self.navigationController pushViewController:groupView animated:YES];
}
If anyone can help resolve this error
The error message tells you exactly what the problem is: you're attempting to push a view controller instance which is already on the stack.
In other words, segue.destinationViewController has already been pushed, such that it is either one of the parents of the current view controller, or the current controller itself.
The actual reason for this is impossible to determine without seeing your code. It's possible that you've got a similar problem to this question, in that the runtime is allowing the event to occur twice. It's equally possible that something to do with the segue is off.
As a first step, I would suggest adding log statements to the segue (not breakpoints, as they will change the observable behaviour) in order to see if it is being called more than once (and is thus similar to the linked question).
Well this i how i checked if my view controller is on the navigation stack or not
but that actually solved my problem
if ([[self.navigationController topViewController] isKindOfClass:[groupView class]]){
self.navigationController.navigationBarHidden = YES;
}else{
self.navigationController.navigationBarHidden = NO;
}
So the whole code goes like this for anyone reference though
else if ([segue.identifier isEqualToString:#"showGroupView"]) {
GroupView *groupView = (GroupView *)segue.destinationViewController;
if ([[self.navigationController topViewController] isKindOfClass:[groupView class]]){
self.navigationController.navigationBarHidden = YES;
}else{
self.navigationController.navigationBarHidden = NO;
}
NSLog(#"showGroupViewsegued");
}
For your suitison ,you should remove "[self.navigationController pushViewController:groupView animated:YES];", and it will jump to the storyBpard of the specific segue.destionViewController.
I test it myself.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// 只作为传值使用 不用再进行导航控制器的 跳转操作
// No need for navigation to push or show
if ([segue.destinationViewController isKindOfClass:[AddCategory class]]) {
AddCategory *controller = (AddCategory *)segue.destinationViewController;
controller.categoryText = sender;
}
if ([segue.identifier isEqualToString:#"AddCategory"]) {
//进入到下一步骤 :只作为传值使用
//specilized
}else if ([segue.identifier isEqualToString:#"AddCategory2"]){
}
}
In my case the first push was not fast enough, so I run into the same problem. Fix was to check the situation:
if (targetViewController == self.navigationController?.topViewController) {
NSLog("ignore double")
} else {
self.navigationController!.pushViewController(targetViewController, animated: true)
}
I have a simple app with 2 screens.
When I press a button to go from the first to the second, everything is performed successfully (including animation). However, when I click the back button on the second screen, I get the following warning:
Warning: Attempt to present <getTextViewController: 0x8f6aa30> on <SecondViewController: 0x946cc80> whose view is not in the window hierarchy!
EDIT: Please don't refer me to other questions regarding above warning - I already saw those, and they refer to other issues.
However, it still switches back to the first screen. Yet, the animation of the segue does not perform.
Also: Information (such as inputted text) in the first screen remains when I return to the first screen, while information in the second screen resets every time the screen comes up.
Here is how I call both operations:
Segue from View 1 to View 2:
Name: F21, Style: Modal, Transition: Cross Dissolve, Animation: True.
Segue from View 2 to View 1:
Name: F12, Style: Modal, Transition: Cross Dissolve, Animation: True.
Code in getTextViewController.m (View 1):
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"F21"]){
UIViewController *v = [segue destinationViewController];
[self dismissViewControllerAnimated:NO completion:nil];
v = self;
}
}
-(void)performSegue:(NSString*)str{
[self performSegueWithIdentifier:str sender:self];
}
//In some other method:
[self performSegue:#"F21"];
Code in SecondViewController.m (View 2):
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"F12"]){
UIViewController *v = [segue destinationViewController];
[self dismissViewControllerAnimated:NO completion:nil];
v = self;
}
}
-(void)performSegue:(NSString*)str{
[self performSegueWithIdentifier:str sender:self];
}
- (IBAction)goBack:(id)sender {
[self performSegue:#"F12"];
}
I would very much appreciate any help to understand why the first segue works while the second doesn't.
Thank you,
Dean
NOTE: Here is the full project - https://github.com/dean13-meet/firstIOSApp
EDIT: Updated git.
Im not exactly sure what you're trying to do in your prepareForSegue, their is no need to be dismissing VC's there. If you want to have a simple app where you go from VC1 to VC2 and then back again, your best bet is to use a segue and an unwindSegue.
So in your storyboard control drag from a button on VC1 to VC2 and select your segue type. Then in VC1.m setup the unwind segue such as:
- (IBAction)unwindFromViewController:(UIStoryboardSegue *)segue
{
//empty implementation
}
Finally, in your VC2 control drag from the back button to the green exit icon on VC2 and select your unwindFromViewController method.
That should do what you're looking for.
For the sake of simplicity, I would suggest using a push segue opposed to modal because it takes care of all the back buttons for you. If you don't like the idea of a navigation controller however, try dismissing the view with the following: Moving back from a Controller to a previous one
I have a modal segue which goes from a view controller to a tab bar controller which is embedded in a table view as you can see from this screenshot:
I want to pass data between the login screen and the table view. I use the following code:
-(IBAction)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"Enter App"]){
//Pass on username so that app can set everything up for that specific user
GamesListTableViewController *gamesList = (GamesListTableViewController *)segue.destinationViewController;
if ([segue.destinationViewController isKindOfClass:[GamesListTableViewController class]]) {
NSLog(#"ENTERING PROPER CLASS");
}
else{
NSLog(#"ENTERING WRONG CLASS");
}
gamesList.userID = self.userID;
}
}
My app crashes due to the last line of code. And i checked why (as you can see in the code above) and the reason is that the segue destination is not the GamesListTableViewController, but what i am assuming is the tab bar controller. How to i work around this problem?
UITabbarController *tabController = (UITabbarController*) segue.destinationViewController;
UINavigationController *nav = (UINavigationController*) tabController.viewControllers[0]; // or some other nr
GamesListTableViewController *tvc = nav.topViewController;
I have an application with two segues. In one of the segues, the current view controller becomes a delegate and the other does not.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"MoreOptions"]) {
UINavigationController *navigationController = segue.destinationViewController;
MoreOptionsViewController *controller = (MoreOptionsViewController *)navigationController.topViewController;
controller.delegate = self;
} else if ([segue.identifier isEqualToString:#"FullStoryView"]) {
SingleStoryViewController *detailViewController = segue.destinationViewController;
detailViewController.urlObject = sender;
}
}
All of this is working fine, but I would like to try and understand the code better. What I don't understand is that I have to get a reference to the MoreOptionsViewController by grabbing it from navigationController.topViewController rather than simply getting it from segue.destinationViewController like I do in the second if condition. Is it because I'm setting the current view controller (self) as the delegate? Again, I'm not trying to solve a problem, just trying to get a better understanding of what's going on.
Take a look at your storyboard and it should be evident why this is the case. You have embedded MoreOptionsViewController in a UINavigationController and connected a segue to the navigation controller, thus making it the destinationViewController. This is fairly common.
The delegate is largely irrelevant in the context of your question.
Your first segue's destination is a navigation controller, which contains the view controller you are really interested in. Therefore to get to that view, you need to go through the navigation controller since that won't have any properties you are interested in setting.
Your second segue goes directly to a single view controller, so you can access it directly.