transitionFromView:toView:duration:options:completion: is not animating the transition - objective-c

I have a view controller that displays the views of 2 sub view controllers in a given area of its view. The 2 sub view controllers are FlopVC and FipVC.
I want to animate the transition from one sub view to the other. The code I'm using is:
-(IBAction)flip:(id)sender{
UIViewController *newVC = nil;
if (self.isFlip) {
newVC = [[FlopVC alloc] initWithNibName:nil bundle:nil];
}else{
newVC = [[FipVC alloc] initWithNibName:nil bundle:nil];
}
newVC.view.frame = CGRectMake(120, 20, 240, 260);
[self.view addSubview:newVC.view];
[UIView transitionFromView:self.currentVC.view
toView:newVC.view
duration:0.9
options:UIViewAnimationTransitionFlipFromLeft
completion:^(BOOL finished) {
self.currentVC = newVC;
self.isFlip = ! self.isFlip;
}];
}
The sub views are swapped, but without any animation. What am I doing wrong?
PS the full project is here.

UIViewAnimationTransitionFlipFromLeft != UIViewAnimationOptionTransitionFlipFromLeft

if you are using the new iOS5 view container paradigm you need to do something along the lines of the following:
-(IBAction)flip:(id)sender{
UIViewController *newVC = nil;
if (self.isFlip) {
newVC = [[FlopVC alloc] initWithNibName:nil bundle:nil];
}else{
newVC = [[FipVC alloc] initWithNibName:nil bundle:nil];
}
newVC.view.frame = CGRectMake(120, 20, 240, 260);
// required for the new viewController container
self.currentVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
[self transitionFromViewController:self.currentVC
toViewViewController:newVC.view
duration:0.9
options: UIViewAnimationOptionTransitionFlipFromLeft
animations:nil
completion:^(BOOL finished) {
// required for the new viewController container
[self.currentVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
self.currentVC=newVC;
}];
}
reference the section Implementing a Container View Controller and the 2011 WWDC videos on UIViewController containers for more info.

Here is working code that (by sheer coincidence) does exactly what you're describing. My two child vc's are stored in self->swappers. The integer cur keeps track of which one is currently showing. The spot in my interface where the subview is to go is marked by a view outlet, panel.
UIViewController* fromvc = [self->swappers objectAtIndex:cur];
cur = (cur == 0) ? 1 : 0;
UIViewController* tovc = [self->swappers objectAtIndex:cur];
tovc.view.frame = self.panel.bounds;
// must have both as children before we can transition between them
[self addChildViewController:tovc]; // "will" called for us
// note: when we call remove, we must call "will" (with nil) beforehand
[fromvc willMoveToParentViewController:nil];
[self transitionFromViewController:fromvc
toViewController:tovc
duration:0.4
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:nil
completion:^(BOOL done){
// note: when we call add, we must call "did" afterwards
[tovc didMoveToParentViewController:self];
[fromvc removeFromParentViewController]; // "did" called for us
}];

Related

Flip view controllers inside bigger view controller will flip the main view controller

I want to flip a view controller which has smaller size than the screen. I am using transitionFromViewController but this would flip also the whole screen. And I want just the smaller view controller to be flipped.
First the login should be visible, after flipped the register view should be available!
My code is
- (void)showLogin {
vcLogin = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
[self.view addSubview:vcLogin.view];
[self addChildViewController:vcLogin];
if (IS_IPAD()) {
vcLogin.view.center = self.view.center;
}
}
- (void)showRegister {
vcRegister = [[RegisterViewController alloc] initWithNibName:#"RegisterViewController" bundle:nil];
[self.view addSubview:vcRegister.view];
[self addChildViewController:vcRegister];
if (IS_IPAD()) {
vcRegister.view.center = self.view.center;
}
[vcLogin willMoveToParentViewController:nil];
[self transitionFromViewController:vcLogin toViewController:vcRegister duration:0.5f options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{} completion:^(BOOL finished) {
[vcLogin removeFromParentViewController];
[vcRegister didMoveToParentViewController:self];
}];
}
I also tried with [UIView transitionFromView:...] but the result is the same!
Any suggestions? Thanks!
Have you tried core animation? You might use something like the code below and have one view controller with subviews.
[UIView transitionFromView:viewOne
toView:viewTwo
duration:1.0f
options:UIViewAnimationOptionTransitionFlipFromRight | UIViewAnimationOptionShowHideTransitionViews
completion:(^(BOOL finished){
})];

View not receiving touches after custom spinner is invoked

I followed the guide here to make my own custom view pop up to indicate I am searching for the users results:
http://joris.kluivers.nl/blog/2012/03/02/custom-popups-revisited/
When the search results are found, I dismiss the custom view, an a Flurry Ad pops up immediately. I click the x button on the ad and my next view appears. This view is not receiving any touch events anymore, neither is the tab bar at the bottom working. I suspect it has to do something with the custom view still animating away(via a transform to 0.0f) when the Flurry ad is coming up, but I am not sure. I am including the code for how the spinner is created, as well as the dismissal.
Note: I have confirmed the problem is related to Flurry Ads showing up modally while my customSpinner is going away. I don't know how to fix it though. When I don't display the Ad, the touch events are registering fine.
PCFFinder.c
- (void)queryServer:(NSString *)queryString {
__block PCFCustomSpinner *spinner = [[PCFCustomSpinner alloc] initWithFrame:CGRectMake(0, 0, 200, 200):#"Searching.."];
[spinner show];
NSString *url = #"https://selfservice.mypurdue.purdue.edu/prod/bwckschd.p_get_crse_unsec";
NSString *referer = #"https://selfservice.mypurdue.purdue.edu/prod/bwckgens.p_proc_term_date";
dispatch_queue_t task = dispatch_queue_create("Task 3", nil);
dispatch_async(task, ^{
NSString *webData = [PCFWebModel queryServer:url connectionType:#"POST" referer:referer arguements:queryString];
if (webData) {
classesOffered = [PCFWebModel parseData:webData type:2];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner dismiss];
spinner = NULL;
if ([classesOffered count] > 0) {
if (self.view.window)
searching = NO;
//the ad pops up in the next view controllers viewWillAppear method and after exiting the ad, the views no longer work
[self performSegueWithIdentifier:#"ChooseClass" sender:self];
}else {
[spinner dismiss];
PCFCustomAlertView *alert = [[PCFCustomAlertView alloc] initAlertView:CGRectMake(0, 0, 300, 200) :#"Search Results" :#"No results were found. Please try broadening the search or double check your input." :#"OK"];
[alert show];
//the views still work here though..
searching = NO;
}
});
//rest omitted because it is irrelevant.
}
CustomPopup.c
-(void)dismiss
{
__block UIWindow *animatedWindow = self.window;
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationCurveEaseOut animations:^{
animatedWindow.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
}completion:^(BOOL finished) {
animatedWindow.hidden = YES;
animatedWindow = nil;
}];
}
-(void)show
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.windowLevel = UIWindowLevelAlert;
self.window.backgroundColor = [UIColor clearColor];
self.center = CGPointMake(CGRectGetMidX(self.window.bounds), CGRectGetMidY(self.window.bounds));
[self.window addSubview:self];
[self.window makeKeyAndVisible];
//animated
self.window.transform = CGAffineTransformMakeScale(1.5f, 1.5f);
self.window.alpha = 0.0f;
__block UIWindow *animationWindow = self.window;
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationCurveEaseIn animations:^{
animationWindow.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
animationWindow.alpha = 1.0f;
}completion:nil];
}
I have tried setting the view as first responder in the viewWillAppear method, but that doesn't work either.
In your dismiss method you are setting the single window to have a zero scale and hidden. This is surely your problem. Animate the pop ups view then remove it from the super view instead.

CDVViewController still executing JS after parent has been removed

I have attempted to insert a Cordova CleaverView (CDVViewController) into a UIViewController(SenchaViewController) instantiated from a storyboard in order to render a javascript Sencha based view.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (![ self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
CDVViewController *cdvc = [CDVViewController new];
cdvc.wwwFolderName = #"www";
cdvc.startPage = #"sencha.html";
cdvc.useSplashScreen = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
cdvc.view.frame = CGRectMake(0, 0, 768, 1004);
} else {
cdvc.view.frame = CGRectMake(0, 0, 320, 460);
}
[self.view addSubview:cdvc.view];
[self.view bringSubviewToFront:self.menuButton];
[self.view bringSubviewToFront:self.favButton];
cdvc = nil;
NSString *jsSetIdString = [NSString stringWithFormat:#"fetchGuid = function(){return '%#';}", [player valueForKey:#"playerID"]];
[[cdvc webView] stringByEvaluatingJavaScriptFromString:jsSetIdString];
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
}
My problem is after I remove the parent ViewController(SenchaViewController) which owns the CDVViewController's view instance, the CDVViewController does not get released and is still running in the background as I can still see javascript logging in the console. The causes for me to have several CDVViewController all running at once.
This is how the app adds the Parent View Controller:
- (void)renderPlayer:(NSNotification*)notification {
SenchaViewController* newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SenchaView"];
NSDictionary *player = notification.object;
// Set active player
if( player != nil && [player valueForKey:#"playerID"] != #"" ){
// Attach Player Dictionary to Sencha View
newTopViewController.player = player;
// Send it to the top
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = newTopViewController;
self.slidingViewController.topViewController.view.frame = frame;
}
This is how the Parent View controller which owns the CDVController is being removed
_topViewController.view removeFromSuperview];
[_topViewController willMoveToParentViewController:nil];
[_topViewController removeFromParentViewController];
Am I missing something? I was assuming ARC would dealloc this for me
Cordova has some dirty aspects, and this is one of them. Removing a CDVViewController is not enough to get it released. You have to also call the [controller dispose] method.

View Controller Animation in Xcode

I need to animate a view when user click on the button. i am using the following code for animating
App Delegate
ViewController2 *vc2_controller = [[ViewController2 alloc]init];
UINavigationController *baseViewController = [[UINavigationController alloc]initWithRootViewController:vc2_controller];
[self.window addSubview:baseViewController.view];
self.window.rootViewController = vc2_controller;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
[self.window addSubview:navigationController.view];
self.viewController = [[vc1controller alloc]initWithNibName:nil bundle:nil];
//ViewController1.m -- This is Working
[self.navigationController pushSlideViewController:self.navigationController];
I created function definition inside the category
//Category
- (void)pushSlideViewController:(UIViewController *)viewController
{
// NSLog(#"In Navigation Controller");
//[UIView transitionWithView:viewController.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft animations:nil completion:nil];
CGRect screensize = [[UIScreen mainScreen]bounds];
[UIView animateWithDuration:0.5 animations:^{
NSLog(#"In View ANimatin");
CGRect current_rect = viewController.view.frame;
viewController.view.frame = current_rect;
current_rect.origin.x = screensize.size.width-50;
viewController.view.frame = current_rect;
}];
}
Now i am calling ViewController1.m from ViewController2.m -- Animation is Not Working
ViewController1 *view_control = [[ViewController1 alloc]init];
[self.navigationController popSlideViewController:view_control];
//Category
-(void)popSlideViewController:(UIViewController *)viewController
{
CGRect screensize = [[UIScreen mainScreen]bounds];
//NSLog(#"In Pop");
[UIView animateWithDuration:0.5 animations:^{
//View is not coming to original position.
CGRect current_rect = viewController.view.frame;
NSLog(#"In View ANimatin %f",current_rect.origin.x);
viewController.view.frame = current_rect;
current_rect.origin.x = 0.0;
viewController.view.frame = current_rect;
}];
}
Note: i am not using ios5 :-)
Thanks,
You're loading VC2 from VC1, which, as you say, works fine. Then you're trying to load VC1 from VC2, which is where the problem lies - VC1 is still loaded, behind VC2, so you just need to dismiss VC2 in a similar way to how you brought it up in the first place, just animate it back off the screen, remove it from the view, then release it (unless you released it when you showed it). Alternatively you could use the built-in methods,
presentViewController:animated:completion:
and
dismissViewControllerAnimated:completion:
which have their own animation.

How to flip an individual UIView (without flipping the parent view)

This is an iPad project where I have a UIView with several subViews, and I am trying to animate one of this UIViews using [UIView transitionFromView:toView:duration:options:completion], but when I run this method the whole parent view gets flipped!
This is the code I'm using:
UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 300, 300)];
[newView setBackgroundColor:[UIColor redColor]];
[UIView transitionFromView:self.firstView.view toView:secondView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
Any idea on how can I flip between this views without animating the whole parent view?
Thanks!
this code may helps you:
put the two views you want to flip inside an unnamed view with the same size
and link the IBOutlet UIView *newView,*oldView; to the views and put the new view on top
bool a = NO;
#implementation ViewController
- (IBAction)flip:(id)sender
{
if (a == NO) {
[UIView transitionFromView:oldView toView:newView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
a = YES; // a = !a;
}
else {
[UIView transitionFromView:newView toView:oldView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
a = NO; // a = !a;
}
}
I had problems getting this to work also. I used the code written by Floris, but although it worked for the first "flip" the next flip started to go weird where the buttons and labels on the frontside UI View lost there positions.
I put the code below into place and it is working fine.
Couple of things to note:
panelView is a UIView control on the ViewController that has two other UIViews nested inside it (subviews). The first one is called frontView, second one is called backView. (see picture below)
I have a bool property called displayingFront on the class
(in my .h)
#property BOOL displayingFront;
in my .m
#synthesize displayingFront;
in my viewDidLoad method
self.displayingFront = YES;
This is the code in the .m that i have rigged up to the two buttons if front and back UIViews...
- (IBAction)flip:(id)sender
{
[UIView transitionWithView:self.panelView
duration:1.0
options:(displayingFront ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
animations: ^{
if(displayingFront)
{
self.frontView.hidden = true;
self.backView.hidden = false;
}
else
{
self.frontView.hidden = false;
self.backView.hidden = true;
}
}
completion:^(BOOL finished) {
if (finished) {
displayingFront = !displayingFront;
}
}];
}
Use a container view to hold your subviews, and make the container view the same size as your subview that you're trying to animate.
So, you can setup your views like this.
self.view-> "a container view" -> subviews
**//flipping view continuously**
bool a = NO;
-(void)viewDidLoad
{
// THIS is the canvass holding the two views this view is flipping
UIView *v=[[UIView alloc]initWithFrame:CGRectMake(40, 40, 150, 150)];
v.backgroundColor=[UIColor clearColor];
[self.view addSubview:v];
[v addSubview:yourfirstview];
[v addSubview:yoursecondview];
// calling the method
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(flip)
userInfo:nil
repeats:YES];
}
//flipping view randomly
-(void)flip
{
if (a == NO)
{
[UIView transitionFromView:yourfirstview toView:yoursecondview duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
a = YES;
}
else if(a==YES)
{
[UIView transitionFromView:yoursecondview toView:yourfirstview duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
a = NO;
}
}
I fixed this problem by using old-school animations:
[UIView beginAnimations:#"Flip" context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:firstView cache:YES];
[self addSubview:secondView];
[UIView commitAnimations];
Additionally you can remove the firstView:
[firstView removeFromSuperview];
I'd like to make an update of Steve Parker's answer in SWIFT 4:-
var displayBlankView:Bool = true
UIView.transition(with:flipView, duration:1.0, options:displayBlankView ? .transitionFlipFromLeft:.transitionFlipFromRight, animations:{
if(self.displayBlankView){
self.view1.isHidden=true
self.view2.isHidden=false
}else{
self.view1.isHidden=false
self.view2.isHidden=true
}
}, completion:{
(finished: Bool) in
self.displayBlankView = !self.displayBlankView;
})