GADInterstitial presentFromRootViewController causes current View Controller to close - ios

I'm attempting to show a GADInterstitial ad when a user of my app clicks a certain button to go to a certain ViewController. In the new ViewController's viewDidLoad method I check to see if the GADInterstitial ad is ready, and if it is I attempt to present it. The problem is, when I dismiss the ad I notice that the ViewController that presented the ad is no longer there and instead it has sent me back to the ViewController that launched the ViewController that presented the ad.
Here's my code:
- (void)viewDidLoad {
[super viewDidLoad];
BTRInterstitialHelper *helper = [BTRInterstitialHelper sharedHelper];
if([helper isInterstitialReady]) {
[helper.interstitial presentFromRootViewController:self];
}
My helper method just looks like this:
-(BOOL)isInterstitialReady {
if ([self.interstitial isReady]) {
return YES;
} else {
return NO;
}
}
I do notice thought that when I present the ad, this message shows up in the logs:
Presenting view controllers on detached view controllers is discouraged BTRIndividualLocationViewController: 0x7fd63bcd9c00.
Same problem occurs if I try to present it in viewWillAppear.
What am I doing wrong here?
EDIT:
I have also tried the following, to try to pass in my app's actual rootViewController:
UINavigationController *nav=(UINavigationController *)((BTRAppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
UIViewController *rootController=(UIViewController *)[nav.viewControllers objectAtIndex:0];
[helper.interstitial presentFromRootViewController:rootController];
Still the same result though, except that message doesn't pop up in the logs.

VC2.h
#import "GADInterstitial.h"
#import "GADInterstitialDelegate.h"
set delegate
#interface VC2 : UIViewController <GADInterstitialDelegate>
and a property
#property(nonatomic, strong) GADInterstitial *interstitial;
VC2.m
in viewdidappear
self.interstitial = [[GADInterstitial alloc] init];
self.interstitial.adUnitID = #"ca-app-pub-yourNumber";
self.interstitial.delegate = self;
GADRequest *request = [GADRequest request];
for test ads in simulator
request.testDevices = #[ GAD_SIMULATOR_ID ];
coud also add location and keywords in the request (optional)
then do request
[self.interstitial loadRequest:request];
listen to delegate
-(void)interstitialDidReceiveAd:(GADInterstitial *)ad {
if ([self.interstitial isReady]) {
[self.interstitial presentFromRootViewController:self];
}
}
If you want to show the interstitial only when pressed from a certain button use NSUserdefaults.
In VC1 when button pressed
[[NSUserDefaults standardUserDefaults] setValue:#"yes" forKey:#"showInterstitial"];
[[NSUserDefaults standardUserDefaults] synchronize];
and wrap all the code for the interstitial in VC2 in viewdidappear in:
if ([[[NSUserDefaults standardUserDefaults] valueForKey:#"showInterstitial"] isEqualToString:#"yes"]) {
}
and add to -(void)interstitialDidReceiveAd:(GADInterstitial *)ad {
[[NSUserDefaults standardUserDefaults] setValue:#"no" forKey:#"showInterstitial"];
[[NSUserDefaults standardUserDefaults] synchronize];
p.s. you could also use a Bool for NSUserdefaults of course, I do use strings caus I had some errors with Bools, but Bools would be more correct I guess

A very similar scenario for me in 2018.
My current ViewControllers look like this:
ViewController 1
-> ViewController 2 (via presentViewController:animated:completion)
-> GADOInterstitialViewController (presentFromRootViewController:)
When GADOInterstitialViewController is dismissed via rewardBasedVideoAdDidClose:, ViewController 2 is instantly dismissed also.
A simple solution to this issue that I have figured out. Simply override ViewController 2 function dismissViewControllerAnimated like this:
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
if(self.presentedViewController != nil){
[self.presentedViewController dismissViewControllerAnimated:flag completion:completion];
} else {
[super dismissViewControllerAnimated:flag completion:completion];
}
}
This code block checks if ViewController 2 has a presented view controller and dismisses it, if not then ViewController 2 gets dismissed.
This works perfectly in my case :)

Related

Calling ViewController's method that pushes another ViewController from App Delegate

Before I begin, I have searched Stackoverflow for how to do this, and I saw a lot of related posts, but none worked for me and I'm not sure why.
So basically I have a loginViewController, and in it, I have a method that call's GoogleSignIn:
- (void)googleTap:(id)sender
{
[[GIDSignIn sharedInstance] signIn];
}
Now the way GoogleSignIn is set up, the result of that sign in call is handled inside AppDelegate.m
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
// Perform any operations on signed in user here.
if (!error) {
NSString *userId = user.userID; // For client-side use only!
NSString *idToken = user.authentication.idToken; // Safe to send to the server
NSString *name = user.profile.name;
NSString *email = user.profile.email;
NSLog(#"Name: %#, User: %#, Token: %#, Email: %#",name, userId, idToken, email);
// ...
}
}
Inside this AppDelegate method, I want to call a method from my loginViewController:
-(void)onSuccessfulLogin{
NSLog(#"On Successful Login");
[self.navigationController pushViewController:[collectionViewController new] animated:YES];
}
I tried these answers: Calling UIViewController method from app delegate
want to call ViewController's method from appdelegate
and the NSLog is called, but the new ViewController is never pushed...why is that and how can I get that to work?
If this is your app delegate you have no self.navigationController. You probably changed the name of your NavigationController to something like UINavigationController *navigationController = [[UINavigationController alloc] init] You need to set a #property for a nav controller on the delegate class. Then where you initialize the nav controller
UINavigationController *navigationController = [[UINavigationController alloc] init]`
self.navigationController = navigationController// You need this line.
//In your method
[self.navigationController pushViewController:[collectionViewController new] animated:YES];
Declare your onSuccessfulLogin method in YourViewController.h (header file)
In (void)signIn:didSignInForUser:withError: method, put below code at bottom
if ([self.navigationController.viewControllers.lastObject respondsToSelector:#selector(onSuccessfulLogin)]) {
[((YourViewController *)self.navigationController.viewControllers.lastObject) onSuccessfulLogin];
}
I was thinking about this wrong all along. You can move the GDSignInDelegate from AppDelegate to viewcontroller.h.
Then you can simply move the -(void)signIn: didSignInUser: method to your ViewController. And you can call your method from there!

Getting the error "Unknown receiver coachMarksView; Did you mean WSCoachMarksView error?"

I'm currently implementing the WSCoachMarksView framework to introduce users to features when using the app for the first time. The code in my viewDidAppear however, gives me the following error: Unknown receiver 'coachMarksView'; Did you mean 'WSCoachMarksView'?
I'm not sure why this is happening, since I've instantiated coachMarksView already in viewDidLoad, so it should recognize it. Am I missing something?
- (void)viewDidLoad
{
[super viewDidLoad];
// Setup coach marks
NSArray *coachMarks = #[
#{
#"rect": [NSValue valueWithCGRect:(CGRect){{50,168},{220,45}}],
#"caption": #"Just browsing? We'll only notify you periodically of new matches. Need it soon? We'll notify you more frequently, and match you with items that are closer to you."
},
];
WSCoachMarksView *coachMarksView = [[WSCoachMarksView alloc] initWithFrame:self.navigationController.view.bounds coachMarks:coachMarks];
[self.navigationController.view addSubview:coachMarksView];
coachMarksView.animationDuration = 0.5f;
coachMarksView.enableContinueLabel = YES;
[coachMarksView start];
}
- (void)viewDidAppear:(BOOL)animated
{
// Show coach marks
BOOL coachMarksShown = [[NSUserDefaults standardUserDefaults] boolForKey:#"WSCoachMarksShown"];
if (coachMarksShown == NO) {
// Don't show again
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"WSCoachMarksShown"];
[[NSUserDefaults standardUserDefaults] synchronize];
// Show coach marks
[coachMarksView start];
// Or show coach marks after a second delay
// [coachMarksView performSelector:#selector(start) withObject:nil afterDelay:1.0f];
}
}
You need to make coachMarksView a property so you can access the same instance. coachMarksView is undefined in viewWillAppear: because that scope has no knowledge of the scope in viewDidLoad.
To create a property for coachMarksView you need to do the following in your viewController:
#interface UIViewController ()
#property (nonatomic, strong) WKCoachMarksView *coachMarksView;
#end
and then in viewDidLoad
- (void)viewDidLoad
{
self.coachMarksView = [[WSCoachMarksView alloc] initWithFrame:self.navigationController.bounds]];
}
Now to access that instance just use self.coachMarksView.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.coachMarksView start];
}
Here is more info on getters, setters, and properties in Objective-C http://rypress.com/tutorials/objective-c/properties.html
You've declared coachMarksView as a local variable inside viewDidLoad. "Local" means it's only visible where you declared it.
Try changing it to a property of the class instead so that your object can access it from all of its methods. (Using self.coachMarksView.)

Game Center authentication, cannot dismiss login viewController

I'm writing a game app for iOS that optionally uses Game Center with leaderboards for storing highscores. What I'm trying to achieve is:
first time the app get's started present the Game Center login viewController
if user logs in successfully everthing's fine
if user cancels the login process don't bother him anymore (not even on next app start)
if user selects a function that requires Game Center give him a chance to login
The builtin authenticateHandler doesn't capture a secong login attempt (already reported here) so the login viewController won't come up a second time if it was cancelled once before. So I tried to go this way and save a reference to the login viewController and present it when appropriate. When my highscores button is pressed the login viewController comes up as supposed. When the user cancels the login the viewController is dismissed (but only the first time). That means if he presses the button a second time the login viewController will come up again but can't be dismissed anymore by pressing the cancel button on the upper left corner.
Have a look at my code:
#interface GameKitHelper () {
UIViewController __weak *presentingVC;
}
#property (nonatomic, strong) NSArray *leaderBoards;
#property (nonatomic, strong) completionBlock completion;
#property (nonatomic, strong) UIViewController *gcLoginVC;
#end
- (void) authenticateLocalPlayerForce: (BOOL) force completion: (void (^)(BOOL)) completion {
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
if (localPlayer.authenticated && completion) {
completion(localPlayer.authenticated);
return;
}
self.completion = completion;
if (self.gcLoginVC && !localPlayer.authenticated && [[NSUserDefaults standardUserDefaults] boolForKey: CUserDeactivatedGC] && force) {
[self presentViewController: self.gcLoginVC force: force];
return;
}
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
[self setLastError: error];
if ([GKLocalPlayer localPlayer].authenticated) {
[[NSUserDefaults standardUserDefaults] setBool: NO forKey: CUserDeactivatedGC];
if (self.completion) {
self.completion([GKLocalPlayer localPlayer].authenticated);
}
return;
}
if (viewController) {
self.gcLoginVC = viewController;
[self presentViewController: viewController force: force];
return;
}
[[NSUserDefaults standardUserDefaults] setBool: YES forKey: CUserDeactivatedGC];
[[NSUserDefaults standardUserDefaults] synchronize];
};
}
//on app start don't force presenting login viewController
- (void) viewDidLoad {
[super viewDidLoad];
[[GameKitHelper sharedGameKitHelper] authenticateLocalPlayerForce: NO completion:^(BOOL authenticated) {
if (authenticated) {
[[GameKitHelper sharedGameKitHelper] loadLeaderBoards];
}
}];
}
//when highscores button is pressed do force presenting login viewController
- (IBAction) highscoresButtonTapped: (UIButton *) sender {
[[GameKitHelper sharedGameKitHelper] authenticateLocalPlayerForce: YES completion:^(BOOL authenticated) {
if (authenticated) {
SGGameLeaderboardVC *vc = [[SGGameLeaderboardVC alloc] init];
[self.navigationController pushViewController: vc animated: YES];
}
}];
}

Dealloc is called on UIViewControllers which are stored in a NSMutableDictionary and presented by UIViewControllerContainment

I've built a custom UITabBarController with Storyboards/Segues and UIViewController containment. Here is a link to it: https://github.com/mhaddl/MHCustomTabBarController
The UIViewControllers which will be presented by the Container are stored in a NSMutableDictionary (keys are the segues' identifiers). Everything is working fine until the point is reached where I come back to a earlier presented ViewController. At this moment "dealloc" gets called on this ViewController before it is presented.
How can I prevent "dealloc" from getting called so it can be used to unsubscribe from Notifications, and nil delegates.
MHCustomTabBarController:
#implementation MHCustomTabBarController {
NSMutableDictionary *_viewControllersByIdentifier;
}
- (void)viewDidLoad {
[super viewDidLoad];
_viewControllersByIdentifier = [NSMutableDictionary dictionary];
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.destinationViewController.view.frame = self.container.bounds;
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![_viewControllersByIdentifier objectForKey:segue.identifier]) {
[_viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
for (UIButton *aButton in self.buttons) {
[aButton setSelected:NO];
}
UIButton *button = (UIButton *)sender;
[button setSelected:YES];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [_viewControllersByIdentifier objectForKey:self.destinationIdentifier];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([self.destinationIdentifier isEqual:identifier]) {
//Dont perform segue, if visible ViewController is already the destination ViewController
return NO;
}
return YES;
}
#pragma mark - Memory Warning
- (void)didReceiveMemoryWarning {
[[_viewControllersByIdentifier allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
if (![self.destinationIdentifier isEqualToString:key]) {
[_viewControllersByIdentifier removeObjectForKey:key];
}
}];
}
#end
MHTabBarSegue:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
"At this moment "dealloc" gets called on this ViewController before it is presented." -- no, not really. Dealloc is being called on a controller that never gets on screen, not the one you came from initially or are going back to. The way your segue is set up, and the fact that you keep a reference to your controllers in the dictionary, means that they never get deallocated. Segues (other than unwinds) ALWAYS instantiate new view controllers, so what's happening is that a new instance of, say VC1 is created when you click on the first tab (and a segue is triggered), but you never do anything with that controller (which would be self.destinationViewController in the custom segue class) so it's deallocated as soon as the perform method exits.
Depending on where you setup any delegates or notification observers, this might not be a problem -- this controller that's created, and then immediately deallocated never has its viewDidLoad method called, so if you do those things in viewDidLoad, they won't ever happen for this transient view controller.
If you don't want this to happen, then you need to make your transitions in code without using segues.

Create a log in screen on iOS that will always show on top of all views

EDIT: Check below for solution.
I am working on a login screen for my app and I have it working for the most part except for a few edge cases. Ive set things up so that I have a segue from my UITabBar in the story board that I trigger in the app delegate applicationDidBecomeActive: method. As I said it works fine on all but one edge case I've found so far.
My app uses some modal view controllers, some of which are UIActivityViewControllers if that makes a difference, to enter and edit some core data entities. If one of these modal view controllers is opened when the app goes to the background, it will always show up when the app is reopened and my login doesn't show. I get the following console msg
Warning: Attempt to present <UINavigationController: 0x1d51e320> on <MPTabBarViewController: 0x1d5b4810> which is already presenting <UIActivityViewController: 0x1e38fc40>
Here is my code
- (void) displayLogin{
NSLog(#"%s", __PRETTY_FUNCTION__);
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
NSDate *lastDate = [[NSUserDefaults standardUserDefaults] objectForKey:MPLastCloseDate];
NSTimeInterval timeDiff = [[NSDate date] timeIntervalSinceDate:lastDate];
int seconds = timeDiff;
if ([[NSUserDefaults standardUserDefaults] integerForKey:MPPassCodeDelay] == MPScreenLockAlways || seconds >= 300) {
NSLog(#"Should see login");
[tabBarController performSegueWithIdentifier:#"loginScreen" sender:self];
}
}
I understand exactly what this msg is telling me, the tab bar is already presenting a modal controller so it can't present another one. So my question is this, Is there a better way to implement this so that the login will always show, even over top of the modal views?
Okay here is my current solution
as suggested by Bartu and requested to be shared by Shawn
I have a working singleton loginManager class that requires 1 call in app delegate and 1 call in any view controller that could be called to present as modal. I was unable to figure out how to do this as suggested with a ViewController category, but hey a few includes and method calls aren't so bad. I included it in App-Prefix.pch, so its available everywhere. It is written for ARC, so if you like managing your own memory you'll need to modify the singleton for that. The last caveat, at current you will need to roll your own viewController for the login screen. Just look for the commented section in the implementation with all the stars, and put your own view controller there. Mine is still in my app storyboard, its basically 4 digit pin that checks for a match in the keychain and dismisses itself for the correct pin. I may pull that out of my storyboard and nib it so it could be packaged with the loginManager and let it become my first gitHub project at some future date though.
You can configure it to display login for every time the app opens or after a delay with properties. The delay time is also a property set in seconds. It will also block out your apps UI for the few seconds it takes to get the login displayed with a splash using your apps Default.png. This is also configurable with a property.
I would love to get some feedback on this, and if anyone can tell me how to do a category so the extra call in viewControllers is not needed that would be great! Enjoy!
AppDelegate:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[self.window makeKeyAndVisible];
// these calls are all optional
[[VHLoginManager loginManager] setShouldBlockUIWithSplashOnResume:NO];
[[VHLoginManager loginManager] setSecondsRequiredToPassBeforeLockDown:1000];
[[VHLoginManager loginManager] setScreenLockRequirment:VHLMScreenLockDelayed];
// this is the only required call to run with defaults - always login and block UI with splash while login loads
[[VHLoginManager loginManager] presentLogin];
}
Any viewController that may presented as modal at some point
- (void)viewDidLoad
{
[super viewDidLoad];
[[VHLoginManager loginManager] registerViewControllerIfModal:self];
}
The loginManager class
header:
// VHLoginManager.h
// Created by Victor Hudson on 5/31/13.
// Copyright (c) 2013 Victor Hudson. All rights reserved.
// Use if you like but be nice and leave my name
#import <Foundation/Foundation.h>
#define VHLMLastCloseDate #"VHLMLastCloseDate"
#define VHLMPassCodeDelay #"VHLMPassCodeDelay"
typedef enum {
VHLMScreenLockAlways = 0,
VHLMScreenLockDelayed = 1,
} VHLMScreenLockRequirement;
#interface VHLoginManager : NSObject
#property (nonatomic) BOOL shouldBlockUIWithSplashOnResume;
// defaults to yes so app contents arent visible before the login screen appears
#property (nonatomic) int secondsRequiredToPassBeforeLockDown;
// defaults to 5 minutes (300)
#pragma mark - Class Methods
+ (VHLoginManager *)loginManager;
// returns the singleton login manager
#pragma mark - Manager Methods
- (void) presentLogin;
// will determine if login should be presented an do so if needed
- (void) registerViewControllerIfModal:(UIViewController *)controller;
// any view controllers that are presented modally should call this with self as controller in viewDidLoad - the pupose of this manager is so login shows even over top of modals
- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement;
// deafaults to always if not adjusted
#end
implementation:
// VHLoginManager.m
// Created by Victor Hudson on 5/31/13.
// Copyright (c) 2013 Victor Hudson. All rights reserved.
// Use if you like but be nice and leave my name
#import "VHLoginManager.h"
static VHLoginManager *loginManager = nil;
#interface VHLoginManager ()
#property (nonatomic, strong) UIViewController *currentModalViewController;
#property (nonatomic) VHLMScreenLockRequirement screenLockrequirement;
#end
#implementation VHLoginManager
#pragma mark - Manager Methods
- (void) presentLogin
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if ([[NSUserDefaults standardUserDefaults] integerForKey:VHLMPassCodeDelay] == VHLMScreenLockAlways || [self timeSinceLastClose] >= self.secondsRequiredToPassBeforeLockDown) {
//NSLog(#"User should see login");
// determine who the presenting view controller should be
UIViewController *viewController;
if (self.currentModalViewController && self.currentModalViewController.presentingViewController != nil) {
// NSLog(#"We have a modal view controller on top");
viewController = self.currentModalViewController;
} else {
// NSLog(#"We have NO modal view controller on top");
// get the root view controller of the app
viewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
}
//********************************************************************************************************************************************************************************
// *** This is still tied into my app storyboard and should be made into a viewcontroller with nib to be portable with loginManager for now implement and present your own loginViewController
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
UINavigationController *navController = [storyboard instantiateViewControllerWithIdentifier:#"appLoginScreen"];
//********************************************************************************************************************************************************************************
// present the login to user
[viewController presentViewController:navController animated:NO completion:nil];
}
}
- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement
{
_screenLockrequirement = requirement;
[[NSUserDefaults standardUserDefaults] setInteger:self.screenLockrequirement forKey:VHLMPassCodeDelay];
}
- (void) registerViewControllerIfModal:(UIViewController *)controller
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if (controller.presentingViewController) {
NSLog(#"Registering a modalViewController");
self.currentModalViewController = controller;
}
}
#pragma mark - Private Methods
- (void) timeStampForBackground
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:VHLMLastCloseDate];
[self setDisplaySplashForBackgroundResume];
}
- (int) timeSinceLastClose
{
return [[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:VHLMLastCloseDate]];
}
#pragma mark Splash Screen management
- (void) setDisplaySplashForBackgroundResume
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.shouldBlockUIWithSplashOnResume) {
// dismiss all keyboards and input views
UIView *topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
[topView endEditing:YES];
// Don't show a splash screen if the application is in UIApplicationStateInactive (lock/power button press)
UIApplication *application = [UIApplication sharedApplication];
if (application.applicationState == UIApplicationStateBackground) {
UIImageView *splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default"]];
splash.frame = application.keyWindow.bounds;
[application.keyWindow addSubview:splash];
}
}
}
- (void) removeSplashScreen
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.shouldBlockUIWithSplashOnResume) { // we should have a splash image up if true
// so remove it
UIWindow *thewindow = [[UIApplication sharedApplication] keyWindow];
if ([[thewindow subviews] count] > 1) {
[NSThread sleepForTimeInterval:1.0];
[[[thewindow subviews] lastObject] removeFromSuperview];
}
}
}
#pragma mark - Class Management
//prevent additional instances
+ (id)allocWithZone:(NSZone *)zone
{
return [self loginManager];
}
+ (VHLoginManager *)loginManager
{
if (!loginManager) {
//Create The singleton
loginManager = [[super allocWithZone:NULL] init];
}
return loginManager;
}
- (id) init
{
// If we already have an instance of loginManager
if (loginManager) {
//Return The Old One
return loginManager;
}
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(timeStampForBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(removeSplashScreen)
name:UIApplicationDidBecomeActiveNotification
object:nil];
self.shouldBlockUIWithSplashOnResume = YES;
self.secondsRequiredToPassBeforeLockDown = 300;
if (![[NSUserDefaults standardUserDefaults] integerForKey:VHLMPassCodeDelay]) {
[self setScreenLockRequirment:VHLMScreenLockAlways];
}
}
return self;
}
#end
I had the same problem a short time ago, my solution for this problem is to have a reference to any modal view which is currently presented in your app delegate. So, you can know if your tab bar controller is already presenting a modal controller and if it's the case, you can present your login view over your current modal view.
What I did was to have a switch in my appDelegate. when the app started, if the user had not logged in, I created the login view and make it the window's rootViewController. When the user successfully logged in, I used an animation block to set that view's alpha to 0, then created a UITabBarController, populated it, made it the window's rootViewController (with an alpha of 0, then animated it's alpha to 1). Worked really well. Not sure how to do this with storyboards though.
EDIT: now getting familiar with storyboards. So what you would do is not use the Main.storyboard per se (remove it from info.plist), then add a LoginViewController as a view, and have your UITabbarController there too - but nothing is the initial view controller. You obviously have to name each view so you can create it in code, but asking the Storyboard to create such and such a view controller
So in App Delegate, if logged in, instantiate the tab bar controller and add it as the root view controller. If the user has not logged in, create the LoginView and add it as rootview controller. If the user does login, have some method on the LoginViewController so it can ask the delegate to switch to the tab bar controller.

Resources