Attempting to begin modal transition while transition is in progress - ios

I'm trying to check if a user is logged into Facebook. If they are, I want to transfer them to another view.
The issue I'm having is that loginViewFetchedUserInfo and loginViewShowingLoggedInUser are both called before the view is actually done loading.
Because of this, when [self showWelcome:self] is called, I get a "attempting to begin modal transition while transition is in progress" error.
I can't seem to figure out a way to wait until the view is done loading before sending them off to the new view.
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
// Set flag
isFirstLoginDone = YES;
NSLog(#"User is logged in");
}
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
// Check first login
if(isFirstLoginDone) {
[self showWelcome:self];
}
// clear the flag
isFirstLoginDone = NO;
}
- (IBAction)showWelcome:(id)sender
{
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"WelcomeStoryboard" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"WelcomeController"];
[self presentViewController:vc animated:YES completion:nil];
}

Yes , it happens when you present some another viewController while a viewController is being present. For that create a dummy method as below code and fire it with small time intervals.
- (IBAction)showWelcome:(id)sender{
[self someMethod];
}
-(void)someMethod{
if(self.isBeingPresented){
[self performSelector:#selector(someMethod) withObject:nil afterDelay:.1];
}
else{
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"WelcomeStoryboard" bundle:nil];
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"WelcomeController"];
[self presentViewController:vc animated:YES completion:nil];
}
}

Related

Why is the incorrect ViewController Showing? and how do I display the correct one?

I am trying to send the user to a registration page if they have not already registered a username etc.. I can't get the registration viewController to load, it only loads the MainViewController.
I've tried every method I can find but nothing works.. My background is Java/Android so this is all very new. A point in the right direction would be much appreciated..
Code
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
BOOL hasRegistered = YES;
if (!hasRegistered)
{
UIViewController *viewController = [[UIStoryboard storyboardWithName:#"Main" bundle:NULL] instantiateViewControllerWithIdentifier:#"registrationView"];
[self presentViewController:viewController animated:YES completion:nil];
}
}
Try this
registrationViewController *viewController = (registrationViewController*)[[UIStoryboard storyboardWithName:#"Main" bundle:NULL] instantiateViewControllerWithIdentifier:#"registrationView"];
instead of
UIViewController *viewController = [[UIStoryboard storyboardWithName:#"Main" bundle:NULL] instantiateViewControllerWithIdentifier:#"registrationView"];
If you show the view controller modally the use this to open
[self presentViewController:viewController animated:YES completion:nil];
Or if you use navigationController then
[self.navigationController pushViewController:viewController animated:YES];

Hide LoginViewController or dismiss to parentView

This is a normal LoginView calling on a specific action in my app
sourceViewVontroller
if ([password length] == 0) {
loginViewController *seatView = [mainStory instantiateViewControllerWithIdentifier:#"loggingView"];
[self presentViewController:login animated:YES completion:nil];
}
it checks if user not signed in yet when he's calling this action, so it redirects him to the loginViewController, then
loginViewController
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
DestinationViewController *Dest = [mainStory instantiateViewControllerWithIdentifier:#"destView"];
[self presentViewController:Dest animated:YES completion:nil];
it goes to the destinationView that needs a login, now my problem is how to go back (dismiss not presentmodalView) to the sourceViewController, or simply, how to remove the loginViewController from the queue on success and dismiss directly to the source?
destinationViewController
// Tried to use presentView .. but i dont need to reload sourceView, just dismiss to it !
//[self presentViewController:srcView animated:YES completion:NULL];
//This is what am doing ..
[self dismissViewControllerAnimated:NO completion:nil];
Or just let me know if there is other professional way to do this login flow
I suggest for you another way:
First in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
You will check user login or not. If login you will set rootView to destinationViewController. If not set rootView to loginViewController
SampleCode:
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
if (logged) {
DestinationViewController *Dest = [mainStory instantiateViewControllerWithIdentifier:#"destView"];
self.window.rootViewController = Dest;
} else {
LoginViewController *seatView = [mainStory instantiateViewControllerWithIdentifier:#"loggingView"];
self.window.rootViewController = seatView;
}
When logout you just call notification or delegate set rootViewController of window to loginViewController.
Do not use presentViewController for bringing login controller.
Complete Process :
In Singleton class. set a global property.
#property (assign)BOOL isUserLoggedIn;
When user logs in or logs out, set this variable to true or false.
Set an enum in LoginViewController.
typedef enum {
DestViewControllerOne =1,
DestViewControllerTwo
} SignInType;
In this enum, put all your view controller where you wanna put that login check.
Set a property in login view controller to hold source controller value -
#property (nonatomic, assign) NSInteger signInType;
Set up a delegate in login controller to redirect after successfull login -
#protocol SignInProtocolDelegate <NSObject>
#optional
-(void) signInSuccess:(NSInteger) signInType;
#end
Create a property with that delegate in login controller -
#property (nonatomic, assign) NSObject<SignInProtocolDelegate>* delegate;
Now before proceeding to required view controller, perform below check -
if(![[TESingleton shareData] isUserLoggedIn]){
[self funcNavigateToSignInWithAlert:YES withSignInType:proceedToDestViewControllerOne];
}
else
{
[self proceedToDestViewControllerOne];
}
-(void)funcNavigateToSignInWithAlert:(BOOL)showAlert withSignInType:(NSInteger) signIntype
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARDNAME bundle:nil];
LoginViewController *viewController = (LoginViewController *)[storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
[viewController setSignInType:signIntype];
[viewController setDelegate:self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[[SlideNavigationController sharedInstance] presentViewController:navController animated:YES completion:nil];
}
By this, it will bring login page.
Next -
After successfull login, Do this in login controller :
[self dismissViewControllerAnimated:YES completion:nil];
[[self delegate] signInSuccess:self.signInType];
Import login delegate method in source controller that we have written above -
-(void) signInSuccess:(NSInteger) signInType
{
switch (signInType)
{
case DestViewControllerOne:
[self performSelector:#selector(proceedToDestViewControllerOne) withObject:nil afterDelay:0.5];
break;
case DestViewControllerTwo:
[self performSelector:#selector(proceedToDestViewControllerTwo) withObject:nil afterDelay:0.5];
break;
default:
break;
}
}
Implement these methods in source view controller -
-(void) proceedToDestViewControllerOne
{
//Restricting navigation to signin
//Addded by vikas Jul 8,2015
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:STORYBOARDNAME bundle:nil];
DestViewcontrollerOne *objDestViewcontrollerOne = (DestViewcontrollerOne *)[storyboard instantiateViewControllerWithIdentifier:#"DestViewcontrollerOne"];
[self.navigationController objDestViewcontrollerOne animated:YES];
}
This is complete process.
Use Unwind Segues
Add a method in SourceViewController
For Swift
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
}
For Objective-C
- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue {
}
You can come back to the SourceViewController in two ways.
Using Storyboard e.g when an UIButton action fires.
Using performSegueWithIdentifier in code.
Storyboard:
Control drag form an UIButton in DestinationViewController to Exit segue inSourceViewController
In Code:
Control drag form an UIButton in DestinationViewController to Exit segue inSourceViewController
Call performSegueWithIdentifier:#"ExitToSourceViewController" when need to come back to SourceViewController

Dismissing ViewController while presenting another one

I have a timer in my app. If the timer reaches 0, the user is brought to a failure page. When that happens, the previous ViewController was not dismissed, leading to more memory being taken up.
I want the previous ViewController to be dismissed if the Failure Page is presented.
if (self.countdownTimer == 0) {
FailurePage *failurePage = [[FailurePage alloc] init];
[self presentViewController:failurePage animated:YES completion:NULL];
//I want to dismiss the current ViewController when the Failure Page is presented
}
You can do something like this:
if (self.countdownTimer == 0) {
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
FailurePage *failurePage = (FailurePage*)[mainStoryboard instantiateViewControllerWithIdentifier: #"<Controller ID>"];
[self presentViewController:failurePage animated:YES completion:^{
[self.parentViewController dismissViewControllerAnimated:NO completion:nil]
}];
}

Switch to a scene in storyboard from xib instead of main scene

I am trying to implement an app that includes a main Login page(implemented in Storyboard) which directs the user to a UIWebView (implemented in xib). When the login is successful, I expect the app to return from the xib to a particular scene (connected by Segue) but instead it returns to the main scene.
I had help from the following links however, I still have the same problem:
How to load a scene in a storyboard from a view controller and
Add subview from a xib or another scene with storyboard
My main View Controller looks like this:
- (IBAction)loginButton:(id)sender
{
oAuthLoginView = [[OAuthLoginView alloc] initWithNibName:nil bundle:nil];
[oAuthLoginView retain];
// register to be told when the login is finished
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loginViewDidFinish:)
name:#"loginViewDidFinish"
object:oAuthLoginView];
[self presentModalViewController:oAuthLoginView animated:YES];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
UINavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"check2"];
// SecondViewController *viewController = navigationController.viewControllers[0];
//
// // setup "inner" view controller
// // viewController. = bar;
//
// [self presentViewController:navigationController animated:YES completion:nil];
}
-(BOOL) loginViewDidFinish:(NSNotification*)notification
{
[self performSegueWithIdentifier:#"afterLoginScreen" sender:self];
return YES;
// [self performSegueWithIdentifier:#"mainApp" sender:self];
// UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// ProfileTabView * yourView = (ProfileTabView *)[storyboard instantiateViewControllerWithIdentifier:#"segueID"];
// [self.presentedViewController:yourView animated:YES completion:^{}];
//[self switchToStoryBoard];
}
I have tried a lot of different things(commented in the code here) but it either leads to crashing the application or returns back to the main login screen.
What am I missing?
Any help appreciated :)
Check2 *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"check2"];
[self.navigationController pushViewController:viewController animated:YES];
'Check2' should be your view controller.
Thats what I'm doing in my app.

attempt to present a view whose view is not in the window both with segues and programmatically

I want to open a login screen and I have tried both programmatically and both with a segue.
I have seen similar questions but it didn't fix it for me.
Here are my two versions of code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
if (![[API sharedInstance] isAuthorized]) {
NSLog(#"I should Open login screen");
[self performSegueWithIdentifier:#"ShowLogin" sender:nil];
}
}
or
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
if (![[API sharedInstance] isAuthorized]) {
NSLog(#"I should Open login screen");
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
LoginScreen *vc = [sb instantiateViewControllerWithIdentifier:#"LoginScreen"];
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:vc animated:YES completion:NULL];
}
}
The segue is modal style.
In both cases the NSLog is printed and then I see a warning:
Warning: Attempt to present <LoginScreen: 0x1e5bd010> on <PhotoScreen: 0x1e5b82e0> whose view is not in the window hierarchy!
and the new view does not open.
Any hint on that?
Don't do this in viewDidLoad, your view isn't on screen yet, so that's why you get that error. You should do it in viewDidAppear (with no animation if you don't want to see the view of the controller that this code is in).

Resources