65591

Preferred way to run custom animations

Question:

I need to run a complex custom animation in an iOS app. For this, I wrote a function that needs to be called repeatedly and uses the current time-stamp to calculate positions, colors, shadows, etc. of UIView elements on the screen.

There seem to be a whole bunch of different approaches I could use to have this function called:

<ul><li><a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html" rel="nofollow">Multi-Threading</a></li> <li><a href="https://developer.apple.com/Library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/index.html" rel="nofollow">Timers</a></li> <li><a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html" rel="nofollow">Dispatch Queues</a></li> <li>God knows what else... :)</li> </ul>

I tried calling my animation-function from a separate thread first, but while the thread does run, I don't see any screen updates until I trigger a refresh manually with a device rotation, so I must be missing some step where I call the update functions from inside the GUI Thread instead of my own or invalidating the View or something... But I don't even know if this is the best approach...

What is the preferred way to keep calling a function (for an animation, for example) as quickly as possible (or with a small delay of 10ms or so) without blocking the GUI and in such a way that if this function, for example, changes the background color or position of a view, the screen gets updated?

If possible, I would like to use a method that is as backward-compatible as possible, so preferably something that doesn't use any features introduced in iOS 8.1 (exaggeration)... :)

<em>Aside:</em>

<em>Sorry for not posting a code example. I'm using RoboVM and don't want to "scare off" any answers from true XCode developers. Also, this is more of a general conceptual question rather than a specific bug-fix.</em>

Answer1:

I've found the best performance from CADisplayLink.

displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkTick)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - (void)displayLinkTick { // Update your animation. }

Don't forget to teardown when you're destroying this view or else you'll have your displayLinkTick called until your application exits:

[displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

Alternatively, if you're using (or convert to) CALayer, your subclass would return YES from needsDisplayForKey: on your animating key. Then, in your CALayer subclass' display method, you'd apply the changes that your self.presentationLayer has for your animation.

@property (assign) CGFloat myAnimatingProperty; @implementation MyAnimatingLayer : CALayer + (BOOL)needsDisplayForKey:(NSString *)key { if ([key isEqualToString:@"myAnimatingProperty"]) { return YES; } return [super needsDisplayForKey:key]; } - (void)display { if ([self.animationKeys containsObject:@"myAnimatingProperty"]) { CGFloat currentValue = self.presentationLayer.myAnimatingProperty; // Update. } } @end

This second way will allow you to link in with the built-in easing functions really easily.

Answer2:

In case someone else is looking for a solution for RoboVM, here you go:

import org.robovm.apple.coreanimation.CADisplayLink; import org.robovm.apple.foundation.NSObject; import org.robovm.apple.foundation.NSRunLoop; import org.robovm.apple.foundation.NSString; import org.robovm.objc.Selector; import org.robovm.objc.annotation.BindSelector; import org.robovm.rt.bro.annotation.Callback; // Requires iOS 3.1 public abstract class DisplayRefreshTimer extends NSObject implements Runnable { private static final Selector REFRESH = Selector.register("displayRefresh:"); private static final NSString RUNMODE = new NSString("kCFRunLoopDefaultMode"); public DisplayRefreshTimer() { CADisplayLink displayLink = CADisplayLink.create(this, REFRESH); displayLink.addStrongRef(this); // Don't garbage collect "this" displayLink.addToRunLoop(NSRunLoop.getCurrent(), RUNMODE); // Start calling } @Callback @BindSelector("displayRefresh:") private static void displayRefresh(DisplayRefreshTimer __self__) { if (__self__!=null) __self__.run(); } }

Just sub-class this, override run() and instantiate:

new DisplayRefreshTimer() { @Override public void run() { // Do your magic here... } };

Done...

<em>Note: The constant String "kCFRunLoopDefaultMode" might change and should instead be read from the NSDefaultRunLoopMode constant that should be provided by NSRunLoop. For some reason RoboVM removed access to this constant (<a href="https://stackoverflow.com/questions/26721462/nsrunloopcommonmodes-constant-in-robovm" rel="nofollow">some details here</a>). While I would think it's unlikely, Apple might decide to change this constant in the future, in which case apps based in this code will break.</em>

Recommend

  • NSRunLoopCommonModes constant in RoboVM
  • Robovm bindings import issue
  • Error while running npm “prefix -g is not recognized as an internal or external command”
  • Calculate cash for hours, cash till 8 hours is 3$, after 8 it goes +5. Without “if”
  • Does php scandir() exclude hidden files under Windows?
  • HTML select, correct option selected in DOM, but wrong item shown in firefox
  • C++ #include Loop
  • Merge arrays by common column values in julia
  • How to stop a goroutine that is listening for RethinkDB changefeeds?
  • Why does .addView throw this parent/child exception?
  • SQL Server: +(unary) operator on non-numeric Strings
  • git cherry-pick: how consider only lines modified by the commit (i.e., not the surrounding context)?
  • TextBox AutoCompleteStringCollection Suggest
  • How do i disable a text box within an iframe
  • Get the last date of each month in a list of dates in Python
  • Adding independent aspx/asmx pages into DotNetNuke
  • Single django queryset to get n adjacent items
  • Better Indy for Dephi 2007
  • Compare struct to a constant in C
  • Activation Function choice for Neural network
  • Outputting SharePoint Hyperlink Column as URL
  • Does Apportable support to build library binary (.a/.so)?
  • In matplotlib, how do you change the fontsize of a single figure?
  • how to set variables in a php include file?
  • Does Mobilefirst provide a provision to access web services directly?
  • Sort List of Strings By Version
  • How to define and use opencv mat of user type
  • Extracting HTML between tags
  • swift auto completion not working in Xcode6-Beta
  • vba code to select only visible cells in specific column except heading
  • Acquiring multiple attributes from .xml file in c#
  • -fvisibility=hidden not passed by compiler for Debug builds
  • How to CLICK on IE download dialog box i.e.(Open, Save, Save As…)
  • How can I remove ASP.NET Designer.cs files?
  • sending mail using smtp is too slow
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass
  • Busy indicator not showing up in wpf window [duplicate]
  • Why is Django giving me: 'first_name' is an invalid keyword argument for this function?
  • How can I use `wmic` in a Windows PE script?
  • java string with new operator and a literal