2014-11-03 4 views
0

Мне нужно запустить сложную пользовательскую анимацию в приложении iOS. Для этого я написал функцию, которую нужно вызывать повторно, и использует текущую метку времени для вычисления позиций, цветов, теней и т. Д. Элементов UIView на экране.Предпочтительный способ запуска пользовательских анимаций

Там, кажется, целая куча различных подходов я мог бы использовать, чтобы эта функция называется:

Я пробовал называть свою анимационную функцию из отдельного сначала, но пока поток выполняется, я не вижу никаких обновлений экрана, пока я не запускаю обновление вручную с помощью поворота устройства, поэтому я должен упустить некоторый шаг, на котором я вызываю функции обновления изнутри потока графического интерфейса пользователя вместо моего собственный или недействительный вид или что-то в этом роде ... Но я даже не знаю, является ли это наилучшим подходом ...

Как выбрать способ функции (например, для анимации) (или с небольшой задержкой в ​​10 мс или около того) без блокировки графического интерфейса и таким образом, что если эта функция, например, изменяет цвет фона или позицию представления, экран обновляется?

Если возможно, я хотел бы использовать метод, который также обратную совместимость, насколько это возможно, поэтому желательно что-то, что не использует какие-либо функции, представленные в прошивкой 8.1 (преувеличение) ... :)

Кроме того:

Извините, что не отправлял код. Я использую RoboVM и не хочу «отпугивать» любые ответы от настоящих разработчиков XCode. Кроме того, это скорее общий концептуальный вопрос, чем конкретный исправление ошибок.

ответ

1

Я нашел лучшее исполнение от CADisplayLink.

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

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

Не забудьте Teardown когда вы разрушив эту точку зрения, иначе вы будете иметь вашу 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 

Этот второй способ позволит вам легко подключиться к встроенным функциям ослабления.

+0

Это похоже на интересный подход. В документах говорится, что метод displayLinkTick, который вы определили выше, будет вызван «когда экран будет обновлен». Означает ли это, что мне также необходимо сохранить недействительность экрана? Или это происходит автоматически? Кроме того, могу ли я затем просто установить свойства, такие как положение и цвет фона и т. Д. На UIViews из метода displayLinkTick? Или мне нужно вызвать какой-то другой метод, чтобы фактически вызвать перерисовку экрана? –

+0

Ваш экран displayLinkTick будет вызываться, когда ОС обновит экран; он не связан с вашей иерархией представлений или недействительными битами экрана. Вы сможете установить любые свойства, которые вы хотите в рамках этой функции, хотя вы должны быть осторожны, чтобы не выполнять длительные процессы. –

+0

Gotcha. Я попробую! :) –

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... 
    } 
}; 

Done ...

Примечание: постоянная строка "kCFRunLoopDefaultMode" может измениться и должна быть указана из константы NSDefaultRunLoopMode, которая должна быть предоставлена ​​NSRunLoop. По какой-то причине RoboVM удалил доступ к этой константе (some details here). Хотя я думаю, что это маловероятно, Apple может решить изменить эту константу в будущем, и в этом случае приложения, основанные на этом коде, сломаются.

+0

Текущая версия RoboVM (2.3.1) позволяет это: 'displayLink.addToRunLoop (NSRunLoop.getCurrent(), NSRunLoopMode.Default);' – Clyde

Смежные вопросы