Joshua Frank
Joshua Frank

Reputation: 13848

UIImage animations don't work in a view pushed without animation

I've got a view controller whose view contains a UIImageView that does animation:

//AnimationViewController::ViewDidLoad event:
var ctlAnimations = new UIImageView();

ctlAnimations.AnimationImages = list.ToArray();  //<--list contains the UIImages
ctlAnimations.AnimationDuration = 1.0 * list.Count;
ctlAnimations.StartAnimating();

this.Add(ctlAnimations);

This works perfectly: when I push AnimationViewController onto the navigation stack, it displays and animates the UIImage.

But now I need to show AnimationViewController with a custom animated transition:

var transition = CATransition.CreateAnimation ();
transition.Duration = 0.3f;
transition.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut);
transition.Type = CATransition.TransitionFade;
this.View.Layer.AddAnimation (transition, "fade");
//viewController is being pushed with animated=false, because we have a custom animation
base.PushViewController (viewController, false);  
this.View.Layer.RemoveAnimation("fade");

This also works perfectly, in that the new View transitions into place using the specified custom animation.

But when I push AnimationViewController onto the stack using an animated transition, it displays but the animation doesn't run. Instead, it shows the first frame of the animation (the first image in the list), and doesn't run it.

So, something about the transition is breaking the ability to animate a UIImage in the new view controller, but I simply can't figure out what to do about it.

Update: I've noticed that if I tap the NavigationController's back button, but then move off of the back button and let go (so I don't actually go back), the animation starts playing!

Upvotes: 10

Views: 1276

Answers (7)

Joshua Frank
Joshua Frank

Reputation: 13848

Well, I never figured out what the problem was here, but it also turned out that there was some sort of memory leak in UIImage such that on some phones my code was crashing.

So I rewrote it to do the animation manually. Now I set a timer and keep a frame index variable, and every time the timer rings I change the image myself. This amounts to the same thing, and it turns out it fixes this problem.

Upvotes: 0

ASHISHT
ASHISHT

Reputation: 305

There is the concept for the Main thread. So UIAnimation are actually works on Main Thread and at the same time may be this was happen that another task is performing on the same Main Thread.So that was a case like IOS will give preference according to processes id of each process at a time and for the solution you need to make background thread for the same operations.

Upvotes: 0

RamaKrishna Chunduri
RamaKrishna Chunduri

Reputation: 1130

Let me tell the something about UI in IOS. In IOS access to the UI Elements is limited to a single thread.

The single thread would always be the mainThread except in the case when you are running an animation.

Hence when u are performing number of animation at the same instance you have to use

  1. beginAnimation.

  2. setFrame (or) some methods that changes state of UI element.

  3. Repeat step2 for all those objects u are scheduling to animate.

  4. comitAnimations to perform all animations at once. (using comit animations ensure all the animations are performed on same thread)

So, I guess here is what happening in ur case.

  1. Viewcontroller started an animation to push the view controller into stack.

  2. Image view started another animation before finishing first animation.

Look at the links to get a clear idea link1 and link2.

Well Let's get into the solution

Add an ivar and retained property named ctlAnimations to your class

In ViewDidLoad (or) ViewDidAppear

self.ctlAnimations = new UIImageView();
ctlAnimations.image=(UIImage*)[list.toArray() objectAtIndex:0];
this.Add(ctlAnimations);
[self performSelector:@selector(startAnimatingImage) afterDelay:0.1];

Create a private method named startAnimatingImage with below code

self.ctlAnimations.AnimationImages = list.ToArray();
ctlAnimations.AnimationDuration = 1.0 * list.Count;
ctlAnimations.StartAnimating();

On a brief we just shown first image for a moment when the firstanimation is taken place and then we delayed animation start for 1 second so that it performs after first animation and then starts animating the image.

Go ahead and give it a try

Upvotes: 0

G. Shearer
G. Shearer

Reputation: 2185

Suspicion #1

I am betting that your code is not being called because it is in ViewDidLoad. I believe you are creating a customized view stack, that means you need to be using the ChildViewController methods from Cocoa.

I am unfamiliar with MonoTouch (I only write pure CocoaTouch), so this might not be 100% correct

I would be consoling out your viewDidLoad and viewDidAppear methods and absolutely make sure they are being called. It is my suspicion that viewDidLoad IS NOT. And this is causing viewDidLoad to not be called on the UIImageView.

In your code you probably need the equivalent of (from objective-c):

[self addChildViewController:viewController];
// OR?
[base addChildViewController:viewController];

This tells the 'parent' viewController that the Child has been made visible, so call the viewDidLoad/Appear and Unload/Disappear methods when appropriate. This might not exist in MonoTouch, or the Push methods might not be fully implemented, so you might need to do some hacky (bad) stuff like manually calling the viewDidLoad method manually.

Suspicion #2

It could also be that your 'list' variable (the one holding the images) is nil. If that happened the animation would not run. OR maybe it has something to do with the duration of your animation, try to set it to whatever would make it repeat forever. Be sure it isn't running REAL FAST somehow and you are just missing it.

begin philosophical musing
Either that or start learning actual Cocoa development :) Not meant as a flame, but definitely meant seriously, you are going to run into problems trying to develop applications through translation layers (custom language-bridges meant to get around writing the base language of a framework/application/platform).

Titanium/MonoTouch/PhoneGap will never produce as robust or high-quality applications as real Objective-C. And besides that, once you learn Cocoa it will change how you write everything else, and I doubt you will want to go back. As the wonderful website of the same name says, 'Cocoa is my girlfriend'

Upvotes: 0

Ed Chin
Ed Chin

Reputation: 1214

I'm very curious what the culprit is here as well. Why is the animation not displaying correctly in some cases?

My theory is that you have placed animation code in viewWillAppear rather than viewDidAppear. Animation code does not run properly when placed in WILL or SHOULD methods.

Can you please post back what caused the issue?

Upvotes: 0

itscoderslife
itscoderslife

Reputation: 356

PushViewController works like this: Over the current view controller the next view controller is placed you can say pushed onto the stack. From Apple docs its clear that either you need to push view controllers either with animation or without.

Work around:

  1. Set the frame of the next view controller's view's x position beyond the screen's right
  2. Suppose width of the screen is 320, then set the x position of next view as 320.
  3. Add the next view as subview to the existing one.
  4. Now do your custom animation.

Another work around:(a bit more overhead though)

  • Take a snapshot programmatically of current view.
  • Add the snapshot image as the initial view of next view controller.
  • Now push view controller without animation. (User will still see the old view)
  • In viewDidAppear of new view controller start your custom animation.

[I have to warn you that this method of taking snapshot might give you a small delay in older devices. Newer devices are pretty fast enough you wont see any lag]

Let me know if any issues in case you are implementing any of these solutions.

Upvotes: 1

WolfLink
WolfLink

Reputation: 3317

Try putting the animating bit in ViewDidAppear rather than ViewDidLoad. Also, try using breakpoints and NSLogs to follow what happens after the animation, starting with the ViewDidLoad and ViewDidAppear. Try having the animation repeat forever so you can see if it has ever been animating or not.

Upvotes: 0

Related Questions