Using applicationShouldTerminateAfterLastWindowClosed with Mac Catalyst - ios

I have a Mac Catalyst app that's essentially a one-window app, but I added multi-window support (using scenes) to allow opening a second window for one function, which a small portion of users will use. Now Apple has rejected the app because with multi-window support, the app doesn't quit when a user clicks the red button at the top of the main window. One solution is to provide a menu item to reopen it, but I think it would be more intuitive for users if the app simply quit as it did before.
I found a similar problem on the Apple forums and am trying to implement the provided solution. Using this tutorial that provides more setup instructions, I have added a macOS bundle as a new target, embedded that into the iOS target, and added this class to the bundle:
#import "AppKitBridge.h"
#implementation AppKitBridge
#synthesize application;
#synthesize window;
- (id)init {
NSLog(#"AppKitBridge init");
self = [super init];
self.application = [NSApplication sharedApplication];
self.window = [[self.application windows] firstObject];
if (self.window) {
self.application.delegate = self;
self.window.delegate = self;
} else {
NSLog(#"AppKitBridge error: window is nil");
}
return self;
}
- (void)test {
NSArray *windows = NSApplication.sharedApplication.windows;
for (NSWindow *window in windows) {
NSLog(#"AppKitBridge window: %#", window);
}
}
- (void)applicationDidUpdate:(NSNotification *)notification {
NSLog(#"AppKitBridge applicationDidUpdate");
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
NSLog(#"AppKitBridge applicationShouldTerminateAfterLastWindowClosed");
return TRUE;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
NSLog(#"AppKitBridge applicationShouldTerminate");
return TRUE;
}
#end
Then in viewDidLoad of the initial view controller of the iOS app, I call this method to load the bundle:
- (void)enableAppKit {
NSString *pluginPath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:#"AppKit.bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:pluginPath];
[bundle load];
NSObject *appKit = [[[bundle classNamed:#"AppKitBridge"] alloc] init];
[appKit performSelector:#selector(test) withObject:nil afterDelay:0];
}
When I run the app, the console shows the AppKitBridge init, AppKitBridge window and AppKitBridge applicationDidUpdate lines. So it seems like the overall setup is working. But when I click the red window button, it does not show the AppKitBridge applicationShouldTerminateAfterLastWindowClosed or AppKitBridge applicationShouldTerminate lines, and the app does not quit.
Should this do what I'm expecting, and if so, what am I missing in the setup?

The problem is this line:
NSObject *appKit = [[[bundle classNamed:#"AppKitBridge"] alloc] init];
Your appKit object is a local variable so your AppKitBridge instance goes out of existence one line later. You need this object to persist if it is to function as the app/window delegate. Assign it to an instance property of some persistent object.

Related

iOS navite framework views push won't work from react-native app

I cannot make a react-native app push new controllers from an iOS native framework. I'm trying to wrap it up into a react-native library, and I managed to make the base view be displayed, but when I interact with it, new screens won't be pushed. Is it possible to navigate between native screens located into the native framework?
I wrapped the framework into a react-native library successfully, using react-native-create-library. I am able to display the base view, and when clicking on buttons that present views on the same screen (such a datepicker) it works fine. But when I click on a button that should pushes a new view controller, it won't react.
I'm testing it into a dummy brand-new react-native app, and changed the AppDelegate.m to use a UINaVigationController instead of the default UIViewController, as follows:
UIViewController *rootViewController = [UIViewController new];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[[RNHellLib sharedInstance] configureSDK: navigationController];
Then in my library I'm doing this:
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_MODULE(RNSearchBox)
RCT_EXPORT_VIEW_PROPERTY(rootController, UIViewController)
SearchBox *mySearchBox;
- (UIView *)view {
return mySearchBox;
}
+ (instancetype)sharedInstance {
static RNHellLib *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[RNHellLib alloc] init];
});
return sharedInstance;
}
- (void) configureSDK: (UINavigationController *)rootController {
mySearchBox = [[SearchBox alloc] init];
mySearchBox.rootController = rootController;
}
On the SDK side, I'm pushing new view controllers using viewController.show(viewControllerToBePushed, sender), and also tried with viewController.present(viewControllerToBePushed, animation: true), but screens don't change either way.
I've read tones of docs and tutorials that use react-navigation or similar libraries to navigate between react-native screens, and also between existing (and known) swift views built up into the same app. But this means that the reat-native side has to know in advance the views that we display when interacting with the elements, instead of letting the framework work as a "black-box" on it's own.
Is it possible? Can it work as a "black-box" as does work in other native apps? Or I will need to compulsorily expose all view controllers and trigger them manually from JSX?
I haven't implemented it by myself but it works fine for me in the following library https://github.com/troublediehard/react-native-braintree-xplat
shortly
// RCTBraintree.h
#interface RCTBraintree : UIViewController <RCTBridgeModule, ...>
#property (nonatomic, strong) UIViewController *reactRoot;
...
#end
// RCTBraintree.m
#import "RCTBraintree.h"
#implementation RCTBraintree
...
RCT_EXPORT_METHOD(showApplePayViewController:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_main_queue(), ^{
...
PKPaymentAuthorizationViewController *viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:paymentRequest];
viewController.delegate = self;
[self.reactRoot presentViewController:viewController animated:YES completion:nil];
});
}
...
- (UIViewController*)reactRoot {
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
UIViewController *maybeModal = root.presentedViewController;
UIViewController *modalRoot = root;
if (maybeModal != nil) {
modalRoot = maybeModal;
}
return modalRoot;
}
...
#end

Hard work to deal with state restoration in iOS

How to deal with "Warning: Unable to create restoration in progress marker file."
I have assigned the Restoration Ids to my view controllers in main story board.
I have also assigned Restoration Id to the view controllers created by code.
For example:
#interface BEMenuController ()<UIViewControllerRestoration>
.....
+ (UIViewController *) viewControllerWithRestorationIdentifierPath: (NSArray *)identifierComponents coder:(NSCoder *)coder
{
UIViewController *retViewController = [[BEMenuController alloc] init];
return retViewController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.restorationIdentifier = #"BEMenuController";
self.restorationClass = [self class];
..........
}
I did as in following link asked
state restoration in iOS
Now the problem is when I run the application I am getting the following warning also:
Unable to create restoration in progress marker file.
Not sure what else need to be done.

App Shows Old XIB Instead of Storyboard

I'm updating an old iOS project that uses XIB files but want to convert to Storyboard. I created my Storyboard, connected it with the appropriate ViewControllers, removed any references that I can find to the XIB files, and set the Main Interface of the project to use my Storyboard.
However, when I run my app it still shows the XIB files instead of using the Storyboard; as if there's something still referencing them. The only one that shows the Storyboard is the initial scene/ViewController (and it's most likely because it's a newly created ViewController).
I've looked at other solutions online but to no avail. I tried:
Cleaning the project
Deleting DerivedData folder
Restarting Xcode and my computer
Updated MyApplication-info.plist
Removed references to ViewControllers from XIB files
Tried removing XIB files from project (it'll show a black screen because file is missing)
The original project target iOS 5.1, the new 6.0, and I'm using Xcode 5. Is there anything that I might be missing?
Edit: I'm not programmatically segueing to the other controllers; I have the Storyboard take care of that. The only thing I do in the first ViewController is send a string text to the next one:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NextViewController * nextController;
if ([[segue identifier] isEqualToString:#"SegueToNext"])
{
nextController = [segue destinationViewController];
[nextController initWithTitle:#"New"];
}
}
Edit: This is the code for the ViewController that is showing the XIB file instead of the Storyboard. As you can see, there are no calls related to loading/pushing new controllers or anything that should relate to the XIB.
#import "NextViewController.h"
#interface NextViewController ()
#end
#implementation NextViewController
-(id) initWithTitle:(NSString *) title
{
self = [super init];
if (self)
{
self.title = title;
// Load the dictionary
self.dictionary = [NSDictionary dictionaryWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"NewDictionary"
ofType:#"plist"]];
}
return self;
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Render new slide
[self renderOpening];
}
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
-(void) renderOpening
{
// 1st slide
NSLog(#"renderOpening");
// Initial settings for opening slide; all are UIOutlets (UIButton, UIImageView)
back.hidden = YES;
diagram.hidden = YES;
next.hidden = NO;
text.hidden = NO;
avatar.hidden = NO;
avatar.image = [[UIImage imageNamed:#"avatar"]
rescaleImageToSize:CGSizeMake(150, 150)];
text.text = #"Hello World";
}
#end
Within your appDelegate class file, remove any code that directs the app to launch to your old initial xib. (From within the didFinishLaunching' method) You don't need to replace it with any code to start the storyboard as this selects this from the 'Development Info' section - see below.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Then click on your project and under the tab of 'General' within the 'Development Info' section, make sure your storyboard is selected as the main interface - see picture below.
Also, if you select the view controller from the storyboard that you want the app to initially start with and select it. Then go to the RHS utilities area and make sure the 'Initial Scene [ ] Is Initial View Controller bock is selected, please see picture.
I hope this helps,
Cheers
Jim
In the ViewController that I was segueing to, I called a custom function which returned a reference to a newly created object of the ViewController class itself:
-(id) initWithTitle:(NSString *) title
{
self = [super init];
if (self)
{
self.title = title;
// Load the dictionary
self.dictionary = [NSDictionary dictionaryWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"NewDictionary"
ofType:#"plist"]];
}
return self;
}
It was left there from the old code, and I completely overlooked it. It was no longer needed now that I was using Storyboard. So instead of seeing the ViewController that I was segueing into, it was showing the old XIB file. Deleting the XIB file from the project showed an empty, black view, and removing the code [super init] showed the correct Storyboard ViewController.
Although I got it working, I'm still not certain as to why it was showing the old XIB file, even though I removed references to it.

ios7 blank screen from framework

I have a framework that creates some views, the app that uses the framework calls a method from it and pass in the current view controller, the framework then calls presentModalViewController to display a view.
It was working just fine with iOS 6.1 SDK but when I updated to Xcode 5 and iOS 7 SDK I don't see the modal view anymore, instead all I get is a blank screen.
EDIT
Heres some code:
The Framework is called "testityi"
testityi.m
#import "TestViewController.h"
#implementation testitiy
- (NSString*) sayHi : (NSString*) name {
return [NSString stringWithFormat:#"Hello %#", name];
}
- (void) displayView:(UIViewController *)parentController {
TestViewController* controller = [[TestViewController alloc] init];
[parentController presentViewController:controller animated:YES completion:nil];
}
TestViewController is simply a view with a label that says "View from framework"
The framework itself works fine, calling sayHi method works just fine.
The third party app has a view with a label and a button which calls sayHi method and then displayView method, heres the view controller code:
MainViewController.m
- (IBAction)buttonPressed:(id)sender {
testitiy* framework = [[testitiy alloc] init];
NSString* msg = [NSString stringWithFormat:#"Calling sayHi method on framework...\n result: %#", [framework sayHi:#"John"]];
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"sayHi method call" message:msg delegate:self cancelButtonTitle:#"Ok, show me the view" otherButtonTitles:nil, nil];
[alert show];
}
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == [alertView cancelButtonIndex]) {
testitiy* framework = [[testitiy alloc] init];
[framework displayView:self];
}
}
The alert button action is also working correctly, I added a NSLog before and its working.
After clicking the alert button a view is presented but instead of containing the label "View from framework" I get a blank screen.
You can see the code on Github
EDIT 2
I got it... I wasn't calling initWithBundle on the ViewController from the framework, I added the a custom init method that calls:
framework: TestViewController.m
+ (NSBundle *)frameworkBundle {
static NSBundle* frameworkBundle = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
NSString* mainBundlePath = [[NSBundle mainBundle] resourcePath];
NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:#"testity.bundle"];
frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
});
return frameworkBundle;
}
- (id) initWithFramework {
NSBundle* bundle = [[self class] frameworkBundle];
self = [super initWithNibName:#"TestViewController" bundle: bundle];
return self;
}
And changed testitiy.m
- (void) displayView:(UIViewController *)parentController {
TestViewController* controller = [[TestViewController alloc] initWithFramework];
[parentController presentViewController:controller animated:YES completion:nil];
//[parentController.navigationController pushViewController:controller animated:YES];
}
And now its working...
I hope this helps someone else but I'm guessing it was a stupid mistake of mine.
Sorry for all the trouble and thanks for your time!
So after a while I finally understand the issue:
When using a custom framework, all resources like images and NIB files have to be manually included in the third-party app so that it has access to those files.
My problem was that I was including the resources (stored in a bundle) into the third-party app but the framework was trying to display the View based on its own resources, which the app couldn't access, for that reason I was getting a blank screen.
I just needed to tell the framework to use the included bundle into the third-party app to display that View (using the initWithNibName: Bundle method).
See EDIT 2 in the question to see the code that solved my problem.
Hope this helps someone. :-)

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