I'm having an app crash in iOS7, but is working on iOS6. While debugging the next code from my AppDelegate, I checked that in iOS7 the next function is executed and then the modal view controller is loaded.
- (void)presentModalWebViewWithURL:(NSURL *)url title:(NSString *)title
{
[self.modalWebViewController dismissModalViewControllerAnimated:YES];
self.modalWebViewController = [[[MyModalWebViewController alloc] initWithURL:url] autorelease];
self.modalWebViewController.title = title;
UINavigationController *nav = [self.modalWebViewController modalNavigationControllerWithTarget:self dismissSelector:#selector(dismissModalWebView)];
[self.window.rootViewController presentViewController:nav animated:YES completion:NULL];
}
In iOS6, I checked that the function stops the execution in the last line until the modal view controller is loaded.
What happens in iOS7 is that when the modal view controller tries to load running viewWillAppear, I was able to check that the modal view controller has changed all the values and even the properties are pointing to objects of different types. I guess that they are being deallocated but I can't figure out why and how to fix it. Any suggestions?
When you dismiss a modal view controller, you're supposed to call the dismiss method on the view controller that presented the view controller. Also the dismissModalViewControllerAnimated: method is deprecated, you should instead use dismissViewControllerAnimated:completion:. So looking at your code, you should probably be calling the dismiss method on self.window.rootViewController, since that's what you're presenting modal views from.
Also, not knowing how the rest of your code looks, I'm assuming the first time this gets called, self.modalWebViewController is nil, so you probably want to check if self.modalWebViewController is set to something before you call dismiss, and also to set it to nil any time you do dismiss it.
Related
I want to show a view only one time when a main view controller is shown for the first time.
-(void) viewDidAppear:(BOOL)animated
{
if (!self.isMainViewShowedBefore)
{
self. self.isMainViewShowedBefore = YES;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: temporaryViewController];
[self presentViewController:navigationController animated:NO completion:NULL];
}
}
It works but the main screen appears for a short time before the temporary view controller is shown.
I tried to add above code in viewWillAppear but I got below error
'NSInternalInconsistencyException', reason: 'Attempting to begin a
modal transition from to
while a transition is already in
progress. Wait for viewDidAppear/viewDidDisappear to know the current
transition has completed'
How can I hide the main controller before the temporary view controller is shown?
Add temporaryViewController.view as a subview of self.view (your main view controller)
[self.view addSubview:temporaryViewController.view];
You got the error in viewWillAppear because you can't do two animations together in iOS, it might get your app crashed but most of the time iOS is nice to us, it just gives us a warning.
Now my main concern, why would you add this main view controller if you don't want to use it at all? and this is not flickering, this is a normal behaviour, if you want the UINavigationController to appear, simply sow it instead of this "main view controller".
If you have more hidden logic that would prevent you from doing what I said, please tell me.
I'm implementing my own 'back' button. Where onClick, the following code is executed in the ViewController (VC) being dismissed:
Dismiss current VC (VC#1)
Pop current VC (VC#1) off my custom navigationStack
Get the last VC (VC#2) from the navigationStack, and present it using
presentViewController
What happens is the back works visually works - i.e. current VC disappears, previous VC appears. However, the viewDidLoad method is not called. So the screen isn't updated with data updates from viewDidLoad.
[self dismissCurrentViewController:self completion:^{
[TWStatus dismiss];
FHBaseViewController *vcToDisplay = [[FHDataManager sharedInstance] popNavigationStack];
[vcToDisplay.homeVC presentViewController:vcToDisplay animated:NO completion: ^{ }];
}];
Questions:
I was under the impression that viewDidLoad always gets called when presentViuewController is used??
I 'build' the screen using a method called ONLY from viewDidLoad in VC#2. How is iOS displaying the screen without coming into viewDidLoad?
btw, I'm not using storyboards. Any help is appreciated!
My guess is that viewWillAppear is being called but viewDidLoad is not, at least not when you expect it is. viewDidLoad should be called once, but depending on how you're managing the view controllers, viewDidLoad may not be triggered every time your view appears (which happens after loading).
The completion handler is called after the viewDidAppear: method is called on the presented view controller. from presentViewController doc
so put this in your code with a breakpoint on the call to super and verify it is getting called when this transition occurs.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
edit: since you verified that viewWillAppear is getting called, then I would say that it's coming down to how you are managing the view controller life cycle. Even with a standard UINavigationController, viewDidLoad is not called when a view is shown as a result of popping items on the navigation stack. I would move your logic to viewWillAppear if you are dead set on not using UINavigationController
When I make a back button pragmatically I use:
[self.navigationController popViewControllerAnimated:YES];
This will invoke the viewDidLoad method. Use that instead of your current code.
My problem
I have a standard UIViewController. With the press of a button, it loads a form sheet modal view controller. When dismissing this modal view with the press of a UIBarButtonItem I call a method by doing:
ViewController *main = [[ViewController alloc] initWithNibName:nil bundle:nil];
[main updateLabel];
In the method -(void)updateLabel in the main ViewController I'm setting the text of a label, but the label won't change. But I know the function gets called, because if I do a NSLog(#"Method call test); instead of label.text = #"Test" I can see the message in console.
What am I doing wrong? It must be the way I'm calling the method in the main ViewController, because I can easily change the label anywhere else.
What I want to do:
When dismissing a modal view controller, I want a method to be called in the main view controller, and in this case change the text of a label.
Thanks for your help!
You're creating a new instance of ViewController with that code, not getting a pointer to the one you already have.
If ViewController is the controller that presented the modal view, then you can get a pointer to it with,
ViewController *main = self.presentingViewController;
A better way to do this would be to use the delegate pattern.
https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html
The following is a design pattern suggestion
The modal view controller shouldn't know how to dismiss itself, that is the responsibility of the presenting view controller. After all, it could have been presented in many different ways (modally, popover, push navigation). Using the delegate pattern, the modal view controller would tell its delegate that it should be dismissed when the bar button item gets pressed. The delegate, which is the presenting view controller, would then dismiss the modal view and update the label mentioned in your question.
I have a view that requires user to be logged in. When the user attempts to open that view an he is not logged in I will call the login view for him to login and after he is done I will call the original view that he intended to see.
On iPhone this works fine as I push view controllers there.
But on iPad where I present view controller this does not work. It says that dismissal in progress, can't show new controller. Here is the code:
- (void) buttonPressed
{
if (!userLoggedIn) { // userLoggedIn getter calls new screens of login if needed
return; // this is executed if user declined to login
}
MyViewController *temp = [[MyViewController alloc] init];
[self.navigationController presentViewController:temp animated:YES]; // this returns warning that dismissal in progress and does not work
}
What can I do about that? On iPhone all of my logic works fine, but on iPad it fails. I use it in many places and completely rewriting code is not good.
EDIT: more code:
- (BOOL) userLoggedIn {
// code omitted
[centerController presentViewController:navController animated:YES completion:nil];
// code omitted
[centerController dismissViewController:navController animated:YES]; // setting to NO does not fix my problem
return YES;
}
EDIT2:
This is the code for iPad. I have removed iPhone-related code. What it does on iPhone - instead of presenting controller it uses pushing, and in that situation everything works fine.
You cannot present another view as long as the dismissing of your 1st view is not complete. The animation of dismissing view should be completed before presenting new view. So, either you can set its animation to NO while dismissing, or use
performSelector:withObject:afterDelay:
and present the next view after 2-3 seconds.
Hope this helps.
You've not posted enough code to really see what you're doing, but one approach to the problem of dismissing and pushing view controllers clashing in this way is to make a the pop+posh into a single atomic operation operation, rather then seqential operations.
You can do this by using the setViewControllers:animated: method on UINavigationController. This allows you to effectively remove one or more view controllers, and add one or more view controllers, all as one cohesive operation, with one seamless animation.
Here's a simple example:
[self.navigationController pushViewController:loginController];
// ... later on, when user login is validated:
NSMutableArray *viewControllers =
[self.navigationController.viewControllers copy];
[viewControllers removeLastObject];
[viewControllers addObject:[[MyNewViewController alloc] init]];
[self.navigationController setViewControllers:viewControllers animated:YES];
If you do things this way, your code will be more predictable, and will work across iPhone and iPad.
For more info, see the API docs.
Update
Since your problem involves a modal dialog on top, try using setViewControllers:animated:NO to change the nav controller stack underneath the modal login dialog before you dismiss the modal.
I have a button in my popover controller. I want to use it to dismiss the popover, so I am trying to access a method (dismissPopover) of the presenting view controller (the "root" view controller).
Note: the method to dismiss the popover is already set up and working, in the root VC, which is the delegate. If I call it it will dismiss the popover. I just need to access the method from the popover.
To do this I set up a property in the AppDelegate, and get an instance of the rootVC like this: self.rootController = (ViewController*)self.window.rootViewController;. Then I imported the root VC class and the AppDelegate to the popover's view controller's class, as below. Seems to give me access to the rootVC, and the methods, but the results do not fire the method. Any idea what I am missing here?
#import "ViewController.h"
#import "AppDelegate.h"
Action connected to button:
- (IBAction)dismissPopover:(id)sender {
//Checking the button works, it does:
NSLog(#"dismissPopover, from popover");
//Trying to get an instance of the rootViewController, the "presenting view controller"
ViewController *rootVC = [(AppDelegate *)[[UIApplication sharedApplication] delegate] rootController];
//trying to access the method in the rootVC that dismisses the popover
[rootVC dismissPopover];
//Tried the following code, does nothing:
//[self dismissPopoverAnimated:YES];
}
NOTE: I ended up abandoning the use of a popover for this as it became a bit over complicated. I tried loading my view controller into a UIView (so I could load the contents of a nib to a pop-up view). That also became a bit complicated. So, for now I am just building my desired interface in a UIView programatically. So far works great.
dismissPopoverAnimated: is a method of UIPopoverController class. so, you need a popover controller reference in your 'root' view controller.
MyRootViewController.myPopoverController = thePopover;
the button is in your 'root' view controller, and in it's action method:
[self.myPopoverController dismissPopoverAnimated:YES];
In iOS 8, you can dismiss the popover (if it's coming from a segue, at least) with dismissViewControllerAnimated:completion: from within the popover. Doesn't work in iOS 7 (or below), however.
Popover automatically dismissed when clicking outside it , as you order a button to dismiss it you can simply use the following code inside your dismissPopover method :
[self.popoverController dismissPopoverAnimated:YES];
you don't need all this tedious work !
[self dismissViewControllerAnimated:YES completion:nil];
is the solution;
you just need an IBoutlet or add target to your button and then call above line
I had the same problem
just do in your buttonClickMethod:
[yourPopoverController dismissPopoverAnimated:YES];
hope you help!
cheers