iOS UIViewAnimation not working - ios

I have a master detail application that when the detail view appears and a string is empty, I want it to present a new view through a UIViewAnimationFlip. The animation is working, but it keeps flipping to itself, not the view controller I initiated. Any help would be great!
- (void)viewDidAppear:(BOOL)animated {
if (masterView.parserURL == nil) {
LoginViewController *login = [[LoginViewController alloc] init];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.8];
[UIView setAnimationTransition:UIModalTransitionStyleFlipHorizontal
forView:self.view
cache:YES];
[self.navigationController presentViewController:login
animated:YES
completion:nil];
[UIView commitAnimations];
}
}

I agree that you should be doing this modally, instead of just adding a subview. In your example code you are animating twice, because the presentviewcontroller method is animating itself already. Try removing the other animation code as follows:
LoginViewController *login = [[LoginViewController alloc] init];
login.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:login
animated:YES
completion:nil];

I'm suggesting simply:
- (void)viewDidAppear:(BOOL)animated {
if (masterView.parserURL == nil) {
LoginViewController *login = [[LoginViewController alloc] init];
login.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:login
animated:YES
completion:nil];
}
}
Note, there's an interesting question of how the login screen is supposed to update that parserURL field in masterView. You might add a property to your login controller that is a pointer to masterView, so that it has a mechanism to update the parserURL. Thus it might be like:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (masterView.parserURL == nil)
{
LoginViewController *login = [[LoginViewController alloc] init];
login.masterView = masterView;
login.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:login
animated:YES
completion:nil];
}
}
Then your login controller can now update the parserURL via:
self.masterView.parserURL = ... // set it as appropriate

Related

Fix Warning: Attempt to present ViewController on NavigationController whose view is not in the window hierarchy

Currently, I have set up a log-in view for users of my app. Below is the code that presents this log-in view to the user:
// Handle how we present the view
if (self.notificationToProcess != nil) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
SWNotificationsViewController *viewController = [[NotificationsViewController alloc] init];
viewController.initialDataID = self.notificationToProcess[#"Data"];
self.notificationToProcess = nil;
[self.navigationController pushViewController:viewController animated:YES];
}];
} else if (self.detailURL != nil) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
WebViewController *wvc = [[WebViewController alloc] init];
wvc.URL = self.detailURL;
wvc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentViewController:wvc animated:YES completion:nil];
self.detailURL = nil;
}];
} else {
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
What I'm attempting to do now is display a web-view first if the user has just updated the app. Below is what this new code looks like:
// Handle how we present the view.
if (self.notificationToProcess != nil) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
SWNotificationsViewController *viewController = [[SWNotificationsViewController alloc] init];
viewController.initialDataID = self.notificationToProcess[#"Data"];
self.notificationToProcess = nil;
[self.navigationController pushViewController:viewController animated:YES];
}];
} else if (self.detailURL != nil) {
[self.navigationController dismissViewControllerAnimated:YES completion:^{
SWWebViewController *wvc = [[SWWebViewController alloc] init];
wvc.URL = self.detailURL;
wvc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentViewController:wvc animated:YES completion:nil];
self.detailURL = nil;
}];
else if (![versionOfLastRun isEqual:currentVersion])
{
SWWebViewController *webViewController = [[SWWebViewController alloc] init];
NSString *url=#"http://someurl";
webViewController.URL = url;
webViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentViewController:webViewController animated:YES completion:nil];
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:#"VersionOfLastRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
else if (versionOfLastRun == nil){
// First start after installing the app
}
else {
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
However, when I run the app, the web view is not displayed, I get a warning of:
Warning: Attempt to present ViewController on NavigationController whose view is not in the window hierarchy!
Can anyone help me diagnose the problem? Thank you!
Seems like your viewController is not in Navigation controller hierarchy. This means that your controller is not connected with navigation controller stack which should handle presented controllers.
The other thing that should help is setting your root view controller as follows:
self.window.rootViewController = self.ViewController;
EDIT: since you mention that you don't use storyboards, setting rootViewController should do the trick.
Try to set the window's rootViewController to the navigation controller after you initialize the navigation controller:
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = navController;
This is how I'm doing it without storyboards.
The problem was that I didn't call self.navigationController dismissViewControllerAnimated:YES completion:^
After correcting this (see listing below), my WebView was shown as expected.
else if (![versionOfLastRun isEqual:currentVersion])
{
[self.navigationController dismissViewControllerAnimated:YES completion:^{
SWWebViewController *webViewController = [[SWWebViewController alloc] init];
NSLog(#"%#", self.window.rootViewController);
NSString *url=#"http://devstatwatch.drbsystems.com/releases_mobile.php";
webViewController.URL = url;
webViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.navigationController presentViewController:webViewController animated:YES completion:nil];
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:#"VersionOfLastRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
}];
}

Swap views in a container view

I'd like to swap two custom UIViewControllers inside a ContainerView. To do this I created a ViewController that handles the logic of the swapping. This swap should occur when pressing a button.
What I want to do is similar to this. I tried the current accepted answer but the problem is that the layout doesn't show up. I don't get any compile errors or warnings.
I have this code in the ContainerViewController, executed when the button is pressed.
if([view isEqualToString:#"view1"]){
NSString *userId = [self getUserId];
ViewController1 *vc = [[ViewController1 alloc]init];
[vc someFunction:userId];
UIViewController *fromViewController = [self.childViewControllers firstObject];
[self swapViewController: fromViewController toViewController:vc];
}else{
NSString *userId = [self getUserId];
ViewController2 *vc = [[ViewController2 alloc]init];
[vc otherFunction:userId];
UIViewController *fromViewController = [self.childViewControllers firstObject];
[self swapViewController: fromViewController toViewController:vc];
}
The swapViewController function looks like this:
- (void)swapViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
toViewController.view.frame = fromViewController.view.frame;
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
}
completion:^(BOOL finished) {
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:self];
}];
}
This is what I have in the storyboard:
If I just simply load the segue it works fine, but I need to show some data received over the internet, so I think it's not an option.
Could anyone please, give me directions on how to achieve that?

How to return to view controller in main.storyboard from a xib without using navigation controller or navigation bar?

I am making an app with different view controllers. My home screen is in main.storyboard. Rest of the view controllers have their own xib, .h & .m files. I am trying this for navigation from home screen.
-(IBAction)btnSignUpTapped:(id)sender
{
SignUpWithEmailViewController * login = [[SignUpWithEmailViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:login];
[self presentViewController:nav animated:YES completion:NULL];
}
This code works fine for navigating to different viewcontroller (in this case SignUpwithEmailViewController). On SignUpWithEmailViewController I have a back button which is supposed to bring me back to home screen. This is what I got so far:
-(IBAction)btnBackTapped:(id)sender
{
ViewController * homeScreen = [[ViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:homeScreen];
[self presentViewController:nav animated:YES completion:NULL];
}
But as result of this code, screen turns black and nothing happens. How do I solve this problem? For hiding the nav bar I am using
-(void) viewWillAppear:(BOOL)animated
{
[[self navigationController] setNavigationBarHidden:YES animated:NO];
}
Yes, you have to insert you called viewController to NavigationController,
but if you wont to use modal flow - to close presentedViewController just call:
[self dismissViewControllerAnimated:YES completion:^{
}];
EDITED:
may be it will be more helpful, it just example, put it to you main view controller:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
UIViewController *c = [[UIViewController alloc] init];
c.view.backgroundColor = [UIColor redColor];
[self presentViewController:c animated:YES completion:^{
[self dismissViewControllerAnimated:YES completion:^{
}];
}];
}

How to present View Controller modally on app launch?

I've created a screen that ONLY appears the first time that my app is launched; it works great. However, I'd like to present this screen modally on top of my root view controller (EditorsNoteViewController being the screen I want to present modally on first launch). Does anyone know how I would do this? Here is what I have so far:
Appdelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
NSLog(#"not first launch");
self.viewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"articlesNav"];
self.window.rootViewController = self.viewController;
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.viewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"articlesNav"];
self.window.rootViewController = self.viewController;
EditorsNoteViewController *vc = [[EditorsNoteViewController alloc]init];
[self.viewController.navigationController presentViewController:vc animated:YES completion:nil];
}
[self.window makeKeyAndVisible];
return YES;
}
I figured out that you'll have to call
[self.window makeKeyAndVisible];
before presenting the view controller
I have read about and encountered this issue many times. A way to completely get around this issue (without any flickering or warnings) is to consider a custom container view controller that will contain your root view controller and your modally presented view controller, as child view controllers. The container view controller can then transition between the two with custom animations, with a very similar effect as present/dismiss.
Let's consider the following container view controller :
#implementation ContainerViewController {
NSMutableArray* stack;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
stack = [NSMutableArray new];
}
return self;
}
- (void) pushViewController:(UIViewController*) viewController {
UIViewController* currentViewController = [stack lastObject];
[stack addObject:viewController];
if (self.isViewLoaded) {
[self addChildViewController:viewController];
[currentViewController willMoveToParentViewController:nil];
viewController.view.frame = CGRectOffset(self.view.bounds, 0, self.view.bounds.size.height);
[self transitionFromViewController:currentViewController toViewController:viewController duration:0.3 options:kNilOptions animations:^{
viewController.view.frame = self.view.bounds;
} completion:^(BOOL finished) {
[viewController didMoveToParentViewController:self];
[currentViewController removeFromParentViewController];
}];
}
}
- (void) popViewController {
UIViewController* currentViewController = [stack lastObject];
[stack removeLastObject];
if (self.isViewLoaded) {
UIViewController* viewController = [stack lastObject];
[self addChildViewController:viewController];
[currentViewController willMoveToParentViewController:nil];
[self transitionFromViewController:currentViewController toViewController:viewController duration:0.3 options:kNilOptions animations:^{
[self.view sendSubviewToBack:viewController.view];
currentViewController.view.frame = CGRectOffset(self.view.bounds, 0, self.view.bounds.size.height);
} completion:^(BOOL finished) {
[viewController didMoveToParentViewController:self];
[currentViewController removeFromParentViewController];
}];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController* viewController = [stack lastObject];
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
}
#end
In your AppDelegate, you can then have something along those lines :
ContainerViewController* viewController = [ContainerViewController new];
[viewController pushViewController: [RootViewController new];
if ([self shouldPresentModalViewController]) {
[viewController pushViewController: [ModalViewController new];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
Somewhere in the modal view controller, you can use the following code to dismiss it:
[((ContainerViewController*) self.parentViewController) popViewController];
Assuming that you have created your EditorsNoteViewController in a storyboard, you should initialise it like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
EditorsNoteViewController *vc = (EditorsNoteViewController*)[storyboard instantiateViewControllerWithIdentifier:#"EditorsNoteOrWhateverIdentifierYouChose"];
Try to check your NSUserDefaults in your RootViewController -viewDidLoad, and if it is the first time, present the new UIViewController there (So that you don't need to mess around with your AppDelegate).
If you want to present it animated, you can do it in -viewDidAppear instead of -viewDidLoad.

Shift to Parent View Controller from child view controller - iOS

I have a main Actions View Controller, and on that there is button "Review". Review Button's functionality is :
- (IBAction)btnReview:(id)sender
{
ReviewViewController *vc = [[ReviewViewController alloc]initWithNibName:#"ReviewViewController" bundle:nil];
[self addChildViewController:vc];
[vc didMoveToParentViewController:self];
[self.view addSubview:vc.view];
}
Now on Review Page, I have a button. And on that button's action, I want to switch back to Parent View Controller. Its view should be displayed. I have tried following code, it either pauses or crashes application.
[self didMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
I even tried:
[self dismissModalViewControllerAnimated:YES]
I have read other posts related to this question as well but could not find a satisfactory answer. Please Help!
I'm assuming that this is a custom viewcontroller container you're trying to build, "self" is the Main Actions viewController you're talking about.
Try this:
- (IBAction)btnReview:(id)sender
{
ReviewViewController *vc = [[ReviewViewController alloc]initWithNibName:#"ReviewViewController" bundle:nil];
[self addChildViewController:vc];
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
vc = nil;
}
For the "Back" button, I'm assuming this is the Button to return to Main Actions viewController from the Review viewController, try this:
- (IBAction)btnReviewHide:(id)sender
{
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
}
Hope this helps.
You should use delegation. When you click the button in your ReviewViewController, you call a method like: [self.delegate hideReviewController:self];
And this method would look something like:
- (void)hideReviewController:(ReviewViewController *)controller {
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
controller.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeSubview:controller.view];
// If you want the Review Controller to be deallocated:
[self removeChildViewController:controller];
}];
}
EDIT:
In your ReviewDelegate.h file add:
#class ReviewViewController;
#protocol ReviewDelegate <NSObject>
- (void)hideReviewController:(ReviewViewController *)controller;
#end
Then add this property:
#property (nonatomic, weak) id <ReviewDelegate> delegate;
In the parent class:
- (IBAction)btnReview:(id)sender
{
ReviewViewController *vc = [[ReviewViewController alloc]initWithNibName:#"ReviewViewController" bundle:nil];
vc.delegate = self;
[self addChildViewController:vc];
[vc didMoveToParentViewController:self];
[self.view addSubview:vc.view];
}
- (void)hideReviewController:(ReviewViewController *)controller {
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
controller.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeSubview:controller.view];
// If you want the Review Controller to be deallocated:
[self removeChildViewController:controller];
}];
}
I also suggest that you read about delegation
Try this
self.view=nil;
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
You don't need to make it complex... Just use UINavigationController.
To navigate from ParentVC to ChildVC :
ChildViewController *ChildVC = [[ChildViewController alloc]initWithNibName:#"ChildViewController" bundle:nil];
[self.navigationController pushViewController:ChildVC animated:YES];
And for navigating back from ChildVC to ParentVC :
ParentViewController *ParentVC = [[ParentViewController alloc]initWithNibName:#"ParentViewController" bundle:nil];
[self.navigationController popViewControllerAnimated:YES];
Since you are adding the Child View as a Subview on to Parent you need to manually it from its superview (in your case parentview). Rather then adding it as a subview you can make use of presentViewController method as follow:
- (IBAction)btnReview:(id)sender
{
ReviewViewController *vc = [[ReviewViewController alloc] initWithNibName:#"ReviewViewController" bundle:nil];
[self presentViewController:vc
animated:YES
completion:nil];
}
and in the child class code for back button will be :
-(IBAction)backButtonClicked:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Sounds like what you're looking for could be taken care of with a UINavigationController. Just use pushViewController: to push the new view onto the screen. Then, a back button will appear in the navigation bar atop the view, which can be used to return to the "parent" view controller.
See the documentation.
writing only
- (IBAction)backbutton:(id)sender {
[self.navigationController popToRootViewControllerAnimated:YES];
}
worked in my case

Resources