运行自定义动画的首选方法

时间:2014-11-03 17:43:17

标签: ios multithreading timer background-process

我需要在iOS应用中运行复杂的自定义动画。为此,我编写了一个需要重复调​​用的函数,并使用当前时间戳来计算屏幕上UIView元素的位置,颜色,阴影等。

我可以使用一大堆不同的方法来调用此函数:

我首先尝试从一个单独的线程调用我的动画函数,但是当线程运行时,我没有看到任何屏幕更新,直到我用设备旋转手动触发刷新,所以我必须错过一些步骤我从GUI线程中调用更新函数而不是我自己的或者使View无效......但是我甚至不知道这是否是最好的方法......

在不阻塞GUI的情况下,尽可能快地(或者以10ms左右的小延迟)保持调用函数(例如动画)的首选方法是什么?如果这个函数,例如,更改视图的背景颜色或位置,屏幕会更新吗?

如果可能的话,我想使用尽可能向后兼容的方法,所以最好不要使用iOS 8.1中引入的任何功能(夸张)......:)

除了:

很抱歉没有发布代码示例。我正在使用RoboVM,并且不想“吓跑”真正的XCode开发人员的任何答案。此外,这更像是一个普遍的概念问题,而不是特定的错误修复。

2 个答案:

答案 0 :(得分:1)

我从CADisplayLink找到了最佳效果。

displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkTick)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

- (void)displayLinkTick {
  // Update your animation.
}

当您破坏此视图时不要忘记拆解,否则在您的应用程序退出之前,您将调用displayLinkTick

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

或者,如果您正在使用(或转换为)CALayer,您的子类将从动态键上的YES返回needsDisplayForKey:。然后,在CALayer子类'display方法中,您应用self.presentationLayer对动画所做的更改。

@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

第二种方法可以让您轻松链接内置的缓动功能。

答案 1 :(得分:0)

如果其他人正在为RoboVM寻找解决方案,请转到:

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();
    }
}

只需对此进行子类化,覆盖run()并实例化:

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

完成...

注意:常量字符串"kCFRunLoopDefaultMode"可能会更改,而应从NSDefaultRunLoopMode应提供的NSRunLoop常量中读取。出于某种原因,RoboVM删除了对此常量(some details here)的访问权限。虽然我认为这不太可能,Apple可能会决定在未来更改此常量,在这种情况下,基于此代码的应用程序将会中断。