iOS: change view from viewDidAppear - ios

I'm playing around with view life cycles & am having trouble changing a view from the load of a different view.
In my ViewController.h i have:
-(void)viewDidAppear:(BOOL)animated{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController2 *viewController = (ViewController2 *)[storyboard instantiateViewControllerWithIdentifier:#"ViewController2"];
[self presentViewController:viewController animated:YES completion:nil];
}
However this only causes the view to be between ViewController, and ViewController2 appearing with animation (in a loop).
I used the code in viewDidLoad however neither of the view's loaded (from reading you cannot change view until the viewWillAppear)
Update: When using the code in viewWillAppear, whose view is not in the window hierarchy error is thrown.
How does one change the view from view setup stage?
Update
Using the above code, inside & out of GCD, in viewDidLoad, viewWillAppear & viewDidAppear either results in an infinite loop of animated showing of the ViewController2, or crash on 2nd attempt of segue (result from the loop).

EDITED:
I'm not sure exactly what you're trying to do, but assuming you are wanting the first viewcontroller to appear and then the second viewcontroller to immediately animate on top of the first one, you should be able to accomplish using several options:
First you could just wrap your calls in a dispatch_async call:
dispatch_async(dispatch_get_main_queue(), ^{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController2 *viewController = (ViewController2 *)[storyboard
instantiateViewControllerWithIdentifier:#"ViewController2"];
[self presentViewController:viewController animated:YES completion:nil];
});
Or you could use a show modally segue:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"myModalSegue" sender:self];
});
}
Or you could use a navigation controller and use a standard show segue (formally push). This one doesn't require the dispatch_async:
- (void)viewDidLoad {
[super viewDidLoad];
[self performSegueWithIdentifier:#"myshowsegue" sender:self];
}
I've posted working examples of all three on: github

It is better to exchange views in loadView method.
- (void)loadView {
CGRect rect = [[UIScreen mainScreen] applicationFrame];
MyView *view = [[MyView alloc] initWithFrame:rect];
[view setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
self.view = view;
}

Related

iOS: loop on viewDidLoad unbalanced calls

I have two View Controllers. ViewController & Next.
ViewContoller.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
bool static check = FALSE;
if(!check){
check = true;
[self changeView];
}
}
-(void)changeView{
dispatch_async(dispatch_get_main_queue(), ^{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
Next *viewController = (Next *)[storyboard instantiateViewControllerWithIdentifier:#"next"];
[self presentViewController:viewController animated:YES completion:nil];
});
}
Next.m is nothing more than the xcode boilerplate code of a view.
When i execute this as above, i receive this output:
2014-11-27 13:20:21.005 changeView[1804:72014] Unbalanced calls to begin/end appearance transitions for <ViewController: 0x7fdd80f41e20>.
You'll notice that i have a static boolean in the viewDidLoad. Without this, the viewDidLoad is called infinitely causing an animated loop of Next's view appearing ontop of ViewController's view.
I'm almost 100% certain that i should not have to implement the boolean to prevent the loop but this is the only way i can. What do?
Update!
When i embed a navigation controller, and use [self.navigationController presentViewController:viewController animated:NO completion:nil]; i do not experience the loop.
While this resolves the situation, the issue of changing views (without a navigation controller) still stands

iOS switch back to background UIViewController

I'm looking for a way to present a modal view over my current UIViewController to basically show a UIActivityIndicator and force users to wait while data is being loaded.
in BaseViewController.m (base class of all my UIViewControllers):
// show loading view
-(void) showLoading
{
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
LoadingViewController *loading = [storyBoard instantiateViewControllerWithIdentifier:#"loadingView"];
loading.view.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:0.7];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:loading animated:NO completion:nil];
}
This works great, but how can I go back to the background view after the loading view should be done?
Need a stopLoading method to go back to the original view:
// stop loading
-(void) stopLoading
{
// code here
}
If I try to present a new view after I present the loading view like so:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
UIViewController *view = [storyBoard instantiateViewControllerWithIdentifier:#"loadingView"];
[self presentViewController:view animated:YES completion:nil];
The debugger gives Warning:
Attempt to present PropertyPickerViewController: 0x8af6010 on ViewController: 0x8ab23c0 which is already presenting LoadingViewController: 0x8acf530.
Try:
[self dismissViewControllerAnimated:YES completion:nil];
In fact, I'm not sure that it'a great idea to present new controller with animated gif.
The best option is (imo) show UIActivityIndicator + place a view on top on all other views to prevent user from clicking anything.
You must [self dismissViewControllerAnimated:YES completion:nil] first.
Check the Apple Documentation.

How to present a TabBarController modal

From one view(LoginTesteInicial) I can go to two tabBarControllers, but when I run the code, it crashes with this error:
Attempt to present <UITabBarController: 0x8a4a870> on <LoginTesteInicial: 0x8a46970> whose view is not in the window hierarchy!
here is my code from LoginTesteInicial.m:
UITabBarController *vc;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
vc = [[UIStoryboard storyboardWithName:#"Main_iPad" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
} else {
vc = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
}
[vc setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:vc animated:YES completion:nil];
The answer to your question is that when -viewDidLoad is called, the view controller's view is not in the view hierarchy. You need to wait until the view is placed into the view hierarchy. This can been done either in -viewWillAppear: or -viewDidAppear:.
The "Unbalanced calls to begin/end appearance transitions" warning you get is because a view controller has not be fully loaded before being replaced with another view controller. To avoid that warning, you can use -performSelector:withObject:afterDelay: to schedule the present view controller in the next run loop.
- (void)viewDidAppear:(BOOL)animated
{
…
[self performSelector:#selector(showTabBarController) withObject:nil afterDelay:0.0];
}
- (void)showTabBarController
{
UITabBarController *vc;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
vc = [[UIStoryboard storyboardWithName:#"Main_iPad" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
} else {
vc = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"TabBarSemLogin"];
}
[vc setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:vc animated:YES completion:nil];
}
Move [self presentViewController:vc animated:YES completion:nil] and related code it either of these
1 viewWillAppear
2 viewWillLayoutSubviews
3 viewDidLayoutSubviews
You will need to keep one flag which will take care that even if one of these method triggered multiple times your view will get presented only once.
Or you can avoid presenting view and add as subview, do this instead of presenting if you didn't like keeping flag etc.
[self.view addSubview:vc.view];
[self addChildViewController:vc];
[vc didMoveToParentViewController:self]

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.

Modal Popup out of place

I have a modal popup which is out of place as soon as the keyboard pops up.
How can I avoid this behavior?
The popup is called like this:
[self performSegueWithIdentifier:#"loginSegue" sender:self];
And in the popup's view controller, I say:
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
self.view.superview.bounds = CGRectMake(0, 0, 403, 340);
}
Thanks in advance!
Try to present viewController like this,
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
LoginViewController *loginVC = (LoginViewController *)[storyboard instantiateViewControllerWithIdentifier:#"loginSegue"];
loginVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
loginVC.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:loginVC animated:YES];
Edit
You can use your original code [self performSegueWithIdentifier:#"loginSegue" sender:self]; for presenting view if above is also giving you same result.
I checked with all possible solution and found that becomeFirstResponder might cause issue. So just place code becomeFirstResponder in viewDidLoad or in viewWillAppear method. Make sure it should not be in viewDidAppear method.

Resources