I have an ad that presents as a modal over my main VC. The ad is timed - show once per app launch after the user has been using the app for 5 seconds. The following is in the main VC which has modal VC's being pushed.
-(void)viewDidLoad{
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"firstLaunch"] isEqualToNumber:[NSNumber numberWithInt:1]]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"paidIAP"] isEqualToNumber:[NSNumber numberWithInt:0]] ) {
[self performSelector:#selector(showAdUnit) withObject:self afterDelay:5.0f];
}
}
}
-(void)showAdUnit{
NSLog(#"got to show ad unit");
[ADNetwork showAd:ADNetworkShowStyleInterstitial rootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
However, if the user goes to another section of the app prior to 5 seconds, the ad will not launch because of this warning
Warning: Attempt to present on whose view is not in the window hierarchy!
Basically, the ad will never show. How can I make the ad show exactly once per launch over the main screen even if the user clicks to push other screens? If this is not possible, how can I push the ad over any screen they happen to be on?
You should present it over your main window rootViewController like this :
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:adViewController animated:YES completion:nil];
This one will always have its view in the view hierarchy and you are guaranteed that your ad view controller will be presented above everything else.
Related
I am working on an iOS app.
The view hierarchy is like,
-- HomePageNavigationView
-- LoginNavigationView
-- UserProfileView
The root view controller of the appDelegate is the HomePageViewController
And in the viewWillAppear of the homepage, I check if there is a validate token.
If there is no validate token, then I present the Login Navigation View as modal view.
After the Login/SignUp process, the user profile is required to be edited. The UserProfileView is presented as modal view.
The other circumstance is, after opening the App, the validate token is found, but the user profile is not completed, so I have to present the user profile as modal view upon the Homepage.
So how to achieve this hierarchy, how can I dismiss view controller twice when I'm presenting userProfile view upon the login navigation view which is presented upon the homepage?
So you can create block property for UserProfileViewController
#property(nonatomic,copy)void(^onDismissed)();
In LoginViewController:
UserProfileViewController *profileViewCtr = [[UserProfileViewController alloc] init];
profileViewCtr.onDismissed = ^{
[self(YOUR_LOGIN_VIEWCTR) dismissViewControllerAnimated:NO completion:nil];
};
and in UserProfileView:
[self(YOUR_PROFILE_VIEWCTR) dismissViewControllerAnimated:YES completion:^{
if (self.onDismissed) {
self.onDismissed();
}
}];
or more simply in UserProfileView
[self(YOUR_PROFILE_VIEWCTR) dismissViewControllerAnimated:YES completion:^{
[self.presentingViewController dismissViewControllerAnimated:NO completion: nil];
}];
I am using an UIActivityViewController. When the user sends my application to the background on the iPhone (when they press the home button, etc) I need to remove the spawned UIActivityViewController and any children that may have come from it. This is to say that if the user selects the "Mail" or "Message" option on the UIActivityViewController I need to remove the view (view controller?) that spawns as a result of this action.
I have tried the following code by found that it only removes the original UIActivityViewController and none of the additional views it spawned.
[_myActivityController dismissViewControllerAnimated:NO completion:nil];
Try this in your appDelegate applicationDidEnterBackground. Dismiss the top most view controller which is presented. Hope this will help you out.
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIViewController *mytopController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (mytopController.presentedViewController)
{
mytopController = mytopController.presentedViewController;
[mytopController dismissViewControllerAnimated:NO completion:nil];
}
}
Working with local push notifications if the notification receives its move to NotificationViewcontroller there is no notification it moves the main view controller .
Here I'm using code:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
if ([status isEqualToString:#"YES"])
{
NotificationViewcontroller *notify_view =[[NotificationViewcontroller alloc]init];
[self.window.rootViewController presentViewController:notify_view animated:YES completion:nil];
when i use this code means it should not move to NotificationViewcontroller i got this errorr:
whose view is not in the window hierarchy!`
// self.window.rootViewController=notify_view;
i use this code it move to NotificationViewcontroller. after seeing the notification put one back button to go view controller
NSLog(#"Go to some other view");
}
else
{
[self.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
}
After see the notification put one back button to go view controller that back button is not working.
-(IBAction)back_btn:(id)sender
{
[self.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
}
You are setting the rootviewcontroller as NotificationViewcontroller , so on press of back button change the rootviewcontroller as some other view controller.
Because there is no view hierarchy, to dismiss the view controller.
presentviewcontroller is used to open new viewcontroller from existing viewcontroller, then only you can dismiss your presentedviewcontroller and it will go back to your parent(e.g.) viewcontroller.
Within my app i have 2 storyboards, one has all the configuration screens and the other has the main screens of the app.
In one of the main screens of the app the user can press a button which allows them to go back and edit or add anything to the configuration.
The issue is if the user came from the main screens, once they press the done button on the configuration screen to confirm their changes, is there a way i can segue back to the main screen storyboard and then segue to the screen they were previously on before going to configuration ?
You need to have code to transition between the storyboards.
- (IBAction)showNextStoryboard:(id)sender
{
UIStoryboard *nextStoryboard = [UIStoryboard storyboardWithName:#"SecondStoryboard" bundle:nil];
UIViewController *nextRoot = [nextStoryboard instantiateInitialViewController];
[self presentViewController:nextRoot
animated:YES
completion:^{}];
}
And to return:
- (IBAction)returnToPreviousStoryboard
{
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{}];
}
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.