- (void)viewWillAppear:(BOOL)animated detect custom animation - objective-c

I have a button which when pressed pushes a view controller however i'm using a custom animation so pushViewController: childController animated: is set to NO. What i want to do though is detect this custom animation in my - (void)viewWillAppear:(BOOL)animatedmethod and write an if statement like this;
- (void)viewWillAppear:(BOOL)animated {
if (customAnimation occured) {//Do this}
else {//Do this}
}
This is the method for my button which pushes the view controller.
- (void)nextPressed:(id)sender {
childController = [[CategoryOneDetailController alloc] initWithNibName:xibDownName bundle:nil];
[UIView beginAnimations: #"Showinfo"context: nil];
[UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[self.navigationController pushViewController: childController animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.navigationController.view cache:NO];
[UIView commitAnimations];
[childController release];
}
Any help would be much appreciated, thanks, Sami.

If you don't use standard animations, I think your best bet is to add a property to your pushed view controller that is set to YES in case of a custom animation (and NO by default to not break any existing behavior). Then you can check that property in viewDidAppear:.
If you need your custom logic to be executed after the animation has run, you might want to set up an animation completion handler or block.

Related

Why is my UIViewController being loaded twice? iOS7

I am presenting my custom UIViewController (called "temp") with a custom animation. The UIVC gets called with:
[temp setModalPresentationStyle:UIModalPresentationCustom];
temp.transitioningDelegate = self;
[temp.view setHidden:YES];
[self presentViewController:temp animated:YES completion:nil];
My custom animation is presenting a view modally from right to top-left position of the screen. It is being presented hidden so the user doesn't see the animation. After it reaches the SCREEN_HEIGHT (768) position it is being set to visible and animated (moved) from top to bottom being presented in the middle. The goal was to present a view from top to bottom and dismiss it from top to bottom (like a movie scene). This code is the NOT working one:
- (void)animateTransition:(id)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
NSLog(#" fromViewController %# ",fromViewController);
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
NSLog(#" toViewController %# ",toViewController);
UIView *containerView = [transitionContext containerView];
if (self.presenting)
{
// set starting rect for animation toViewController.view.frame = [self rectForDismissedState:transitionContext];
[containerView addSubview:toViewController.view];
[UIView animateWithDuration:0.5
animations:^{
toViewController.view.frame = CGRectMake(-self.customSize.width, self.yValue, self.customSize.width, self.customSize.height);
}
completion:^(BOOL finished)
{
//HERE IS THE PROBLEM!!!
[toViewController.view setHidden:NO];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
CGRect variable = [self rectForPresentedState:transitionContext];
CGRect fitToCurrentScreenResolution = CGRectMake(0, 0, variable.size.width, variable.size.height);
toViewController.view.frame = fitToCurrentScreenResolution;
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
}];
}];
}
else
{
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromViewController.view.frame = [self rectForDismissedState:transitionContext];
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
[fromViewController.view removeFromSuperview];
}
];
}
}
And here is the solution:
- (void)animateTransition:(id)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
NSLog(#" fromViewController %# ",fromViewController);
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
NSLog(#" toViewController %# ",toViewController);
UIView *containerView = [transitionContext containerView];
if (self.presenting)
{
// set starting rect for animation toViewController.view.frame = [self rectForDismissedState:transitionContext];
[containerView addSubview:toViewController.view];
[UIView animateWithDuration:0.5
animations:^{
toViewController.view.frame = CGRectMake(-self.customSize.width, self.yValue, self.customSize.width, self.customSize.height);
}
];
[toViewController.view setHidden:NO];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
CGRect variable = [self rectForPresentedState:transitionContext];
CGRect fitToCurrentScreenResolution = CGRectMake(0, 0, variable.size.width, variable.size.height);
toViewController.view.frame = fitToCurrentScreenResolution;
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
}];
}
else
{
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromViewController.view.frame = [self rectForDismissedState:transitionContext];
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
[fromViewController.view removeFromSuperview];
}
];
}
}
My question is simple. Why is my UIVC being presented twice?
I have tried making my custom UIVC a property which is lazy loaded but my app crashes saying that a UIVC = nil can not be presented modally.
I have tried this solution, but it didn't apply to my problem :viewWillAppear being called twice in iOS5
I also did this with no help: Calling presentModalViewController twice?
I could have used a hack but I wouldn't find out why it is happening. So far it seems that when the animation enters the completion BLOCK it calls the view again.
The apple docs say:
A block object to be executed when the animation sequence ends. This
block has no return value and takes a single Boolean argument that
indicates whether or not the animations actually finished before the
completion handler was called. If the duration of the animation is 0,
this block is performed at the beginning of the next run loop cycle.
This parameter may be NULL.
Is the view being drawn again since the next run loop cycle is being started?
NOTE: Even thought the view is being presented twice, the viewDidLoad method is being called only once.
I would like to know why this is happening. There are some stackoverflow questions with the same code but with different usage scenarios having the same problem without a working solution or explanation.
Thank you for any advice/comment.
iOS 8.0, Xcode 6.0.1, ARC enabled
Yeah you are definetely onto it with "chained animation" (see comment from O.P.).
I witnessed a similar problem trying to hide and show the UIStatusBar for various UIViewControllers in my application, e.g. I have a dummy after load screen UIViewController that shows the same image as the load screen, but it has some added animations.
I am using a custom transition, which features a UIViewController that handles the transition from the "from" UIViewController and the "to" UIViewController by adding or removing their views from itself and assigning the "to" UIViewController "control" to itself. So on and so forth.
In the app. delegate,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
I had to instantiate the "initial view controller" and then initialize the transition UIViewController with it. Since there are no "to" UIViewControllers the transition UIViewController must hold an initial UIView of the UIViewController it was initialized with until a transition is triggered.
This was done utilizing,
self.window.rootViewController = self.transitionViewController;
[self.window makeKeyAndVisible];
After the very first transition, there always two UIViews overlaid onto each other. And two UIViewControllers one existing as the current control for the transition UIViewController that was assigned during the transition and the previous UIViewController that remains until the transition completes.
This was the code I was trying to use to show/hide the UIStatusBar, one must have the "View controller-based status bar appearance" set to "YES" in the *-Info.plist file.
- (void)viewDidLoad
{
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
}
- (BOOL)prefersStatusBarHidden
{
return false;
}
Whenever the "return" value was changes from default "false" to "true" regardless of when
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
was triggered, delay, no delay, conditional, etc.; both UIViewControllers, the "to" and "from" were reloaded. At first this was not noticeable, however after implementing an NSURLSession in one of the UIViewControllers that was triggered in the - (void)ViewDidLoad; the problem was clear. The session was executed twice and the graphical content involved was also updated.
I successfully solved the issue in two ways, however I kept the 2nd.
I put everything in -(void)ViewDidLoad; in an if statement and forced it to only be executed once, using an instance variable boolean. The -(void)ViewDidLoad; still loaded twice, however, things that I did not want to execute twice did not.
I transitioned to the UIViewController at which the UIStatusBar hidden state needed to change without using my transitional UIViewController. After the UIStatusBar was shown or hidden, I would reset the "rootViewController" for the app. delegate, once again assigning the transitional UIViewController as always "shown".
I describe how to do this in the following post: https://stackoverflow.com/a/26403108/4018041
Thanks. Hope this helps someone. Please comment on how this could be handled in the future for either the OP or myself.

navigationController setup pop transition style

Is it possible to change popToViewController transition style for UINavigationController? I need the same style as for pushViewController method?(I mean from right to left style(pushViewController), not left to right(popToViewController by default)).
For modally presented view controllers, you can change the animation with the modalTransitionStyle property. AFAIK, there is no way to change a navigation controller's push animation (except rebuilding UINavigationController from scratch).
https://github.com/devindoty/iOS-Transition-Pack
OR
[UIView beginAnimations:#"animation" context:nil];
[self.navigationController pushViewController: yourviewcontroller animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView commitAnimations];

Update interface orientation manually in iOS

My iOS application supports all orientations except PortraitUpsideDown.
But in the application I have an view with preferences which I want it to only be shown in Portrait orientation. So whenever this view is shown, it is rotated if needed, to be in portrait mode. That means that user will rotate device in portrait mode also, to setup preferences, and then after closing this view interface should now have portrait orientation.
The problem is, that after preferences view is hidden interface stays in landscape orientation, since I block autorotation after this view is shown.
So after the view is hidden I want to manually update the interface to current device orientation. How can I do it?
self.view.hidden=NO;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.view.alpha=1.0;
[UIView commitAnimations];
This code is called from the OptionsViewController after a LongPressGesture on its superview.
I created a UIViewController extension to force update of orientation of a controller, based on the solution presented by Marek R. Since the new versions of iOS, his solution does not work anymore. I post here my solution to force orientation update (in order to take into account supported orientation methods of controller) without having side visual effects. Hope it helps.
UIViewController *vc = [[UIViewController alloc]init];
[[vc view] setBackgroundColor:[UIColor clearColor]];
[vc setModalPresentationStyle:(UIModalPresentationOverFullScreen)];
[self presentViewController:vc animated:NO completion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[vc dismissViewControllerAnimated:YES completion:completion];
});
}];
All you have to do is add the following to the view controller you're using for your preferences.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
Calling this workaround works for me:
-(void)forceOrientationUpdate {
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
[c release];
}

Objective C: Triggering IBAction showInfo in viewDidAppear

I created my app using Utility Application template in Xcode 4.
I want my app to display the flip side on the first launch so that the user can enter a URL.
In my mainViewController I have:
- (void)viewDidAppear:(BOOL)animated
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if(![defaults stringForKey:#"url"]){
// Flip to the back page
[self showInfo: self];
NSLog(#"No url, flip");
}else{
NSLog(#"dont flip");
}
}
- (IBAction)showInfo:(id)sender
{
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
"No url, flip" is logged, and the showInfo method is called, but it does not flip.
What am I doing wrong?
just edit one line in your code.
- (void)viewDidAppear:(BOOL)animated
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if(![defaults stringForKey:#"url"]){
// Flip to the back page
[self performSelector:#selector(showInfo:) withObject:self afterDelay:0.1];
NSLog(#"No url, flip");
}else{
NSLog(#"dont flip");
}
}
Your showInfo: method is on the right track, sort of, but isn't going to work, I think. Here is an example of some code I have in an app that does what you're after, I believe:
PersonDetailViewController *pvc = [[PersonDetailViewController alloc]
initWithNibName:#"PersonDetailView" bundle:nil];
UIView *container = self.parentViewController.tabBarController.view;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
forView:container cache:YES];
[container addSubview:pvc.view];
[pvc viewWillAppear:YES];
[self viewWillDisappear:YES];
[UIView commitAnimations];
The point of all this is that you have to give more info about the transition you are hoping to achieve. There are a number of UIView class methods (as shown here) that deal with setting up view transitions and other view-based animations.
So what's this doing? The code is adding the new view controller's view (pvc.view) as a subview of "container". In this app, the "container" view is the view that gets flipped out of the way as the new view (pvc.view) gets flipped in.
It is also important to call both the new view's viewWillAppear: and the old view's viewWillDisapear: so all the right stuff that you expect to happen when views come and go, happens. [This recipe may be considered "old school" by some. In the iPhone OS 2.x days (it wasn't called iOS back then), the Utility template had code similar to this, calling viewWillAppear and viewWillDisappear, etc. It still works today, fwiw. And the code shown here is in a shipping application on the app store, so its still kosher, so to speak. :-) ]
It is worth noting that in PersonDetailViewController (in this example) there is similar code that flips everything back, for consistency.

viewWillAppear-related problem

Without giving you all my code examples, I'll made this quick.
Has this ever happened to one of you to get viewWillAppear called only the first time it shows up?
I have this problem with all my view.
For example: When my app starts, I get to the StartView which is a main menu. (viewWillAppear gets called) then I press on one button that'll show a navigation controller (viewWillAppear gets called). Then I get back to the main menu (it does not get called) and then I press on the same navigation controller again and it does not get called.
It would be awesome if someone could points me somewhere, I've been searching for this since two days now...
Also if you need more code samples, I can give you some.
For further reading:
That's how I call my navigation controller:
PremierSoinsAppDelegate *AppDelegate = (PremierSoinsAppDelegate *)[[UIApplication sharedApplication] delegate];
UIView *newView = [AppDelegate.navigationController view];
[newView setFrame:CGRectMake(320.0f, 0.0f, 320.0f, 480.0f)];
[UIView beginAnimations:#"RootViewController" context:nil];
[UIView setAnimationDuration:0.3];
newView setFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
UIView commitAnimations];
[AppDelegate.window addSubview:newView];
[AppDelegate.window makeKeyAndVisible];
And that's how I show my menu back:
PremierSoinsAppDelegate *AppDelegate = (PremierSoinsAppDelegate *)[[UIApplication sharedApplication] delegate];
UIView *newView = [AppDelegate.startViewController view];
newView setFrame:CGRectMake(-320.0f, 0.0f, 320.0f, 480.0f)];
UIView beginAnimations:#"StartViewController" context:nil];
UIView setAnimationDuration:0.3];
newView setFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
[UIView commitAnimations];
[AppDelegate.window addSubview:newView];
[AppDelegate.window makeKeyAndVisible];
Thanks A LOT.
You can implement the UINavigationControllerDelegate in your Nav Controller to propagate the viewWillAppear: messages down. You can implement the message like this:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController respondsToSelector:#selector(viewDidAppear:)]) {
[viewController viewDidAppear:animated];
}
}
Note that this is the viewDidAppear and not ViewWillAppear version, but they're basically the same.
However, you should note that the fact that you need to do this may be a sign that something else is wrong in your controller/view code and you might want to reask the question giving us more context to answer it. In particular, I'm assuming that somewhere outside of the code you're giving us, you're pushing and popping view controllers as per usual for a Nav Controller.
viewWill/DidAppear: will only be called when using a UINavigationController or UITabBarController (or really any system-provided-viewControlller managing class) to manipulate views. If you're manually doing this (as you seem to do in your second code snippet, these messages won't get sent.