Основные трудности анимации с управлением вращающимся циферблатом (очень подробно) - PullRequest
6 голосов
/ 01 сентября 2010

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

Пока у меня почти все работает, но я нахожусь на этапе, когда основная анимация доставляет мне горе.

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

Во-первых, с точки зрения настройки представления, у меня есть 6 отдельных UIView s (называемый NumberView1, NumberView2 и т. Д.), Каждый для каждого числа в элементе управления.

Внутри каждого NumberViewX у меня есть еще один UIView, представляющий собой контейнер, называемый ContainerView1, 2 и т. Д. *

У меня по 10 UIImageView s, сложенных поверх каждогодругие с разными смещениями Y.Все эти изображения 30х30, что делает его красивым.Сначала 9, затем 8 со смещением y 30, затем 7 со смещением y 60 и т. Д. ... вплоть до 0 со смещением y 270.

ВАЖНОЕ ПРИМЕЧАНИЕ: Мои числатолько прокрутка вверх

Числа представляют собой 5-значное десятичное число (т. е. 2,33477), которое прокручивается вверх (например, до 2,61722).

У меня также есть несколько словарей, которыеудерживать текущее числовое значение для каждого числа и смещения для каждого числа:

NSArray *offsets = [[NSArray alloc] initWithObjects: 
                    [NSNumber numberWithInt:0],    //9
                    [NSNumber numberWithInt:-30],  //8
                    [NSNumber numberWithInt:-60],  //7
                    [NSNumber numberWithInt:-90],  //6
                    [NSNumber numberWithInt:-120], //5
                    [NSNumber numberWithInt:-150], //4
                    [NSNumber numberWithInt:-180], //3
                    [NSNumber numberWithInt:-210], //2
                    [NSNumber numberWithInt:-240], //1
                    [NSNumber numberWithInt:-270], //0
                    nil];

NSArray *keys = [[NSArray alloc] initWithObjects: 
                 [NSNumber numberWithInt:9],
                 [NSNumber numberWithInt:8],
                 [NSNumber numberWithInt:7],
                 [NSNumber numberWithInt:6],
                 [NSNumber numberWithInt:5],
                 [NSNumber numberWithInt:4],
                 [NSNumber numberWithInt:3],
                 [NSNumber numberWithInt:2],
                 [NSNumber numberWithInt:1],
                 [NSNumber numberWithInt:0], nil];

offsetDict = [[NSDictionary alloc] initWithObjects:offsets forKeys:keys];

[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:2] forKey: [NSValue valueWithNonretainedObject: self.containerViewDollarSlot1]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:8] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace1]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:3] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace2]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:3] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace3]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:8] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace4]];
[currentValuesForDecimalPlace setObject:[NSNumber numberWithInt:5] forKey: [NSValue valueWithNonretainedObject: self.containerViewDecimalPlace5]];

Теперь для логики я начну с установки свойств слоя ContainerView для определенных смещений Y, которые будут перемещать текущие числа вих правильные места, например, так:

self.containerViewDollarSlot1.layer.transform =   CATransform3DTranslate(self.containerViewDollarSlot1.layer.transform, 0, -210, 0);
self.containerViewDecimalPlace1.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace1.layer.transform, 0, -30, 0);
self.containerViewDecimalPlace2.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace2.layer.transform, 0, -180, 0);
self.containerViewDecimalPlace3.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace3.layer.transform, 0, -180, 0);
self.containerViewDecimalPlace4.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace4.layer.transform, 0, -30, 0);
self.containerViewDecimalPlace5.layer.transform = CATransform3DTranslate(self.containerViewDecimalPlace5.layer.transform, 0, -120, 0);

, который покажет числа, 2.83385, который является произвольным числом для тестирования ради.

Затем я ввожу другое значение в текстовое поле и нажимаюкнопка «go», которая запускает логику анимации, где я получаю трудности в Core Animation (как следует из названия)

Я звоню:

[self animateDecimalPlace: 0
            withAnimation: self.dollarSlot1Animation
         andContainerView: self.containerViewDollarSlot1];
[self animateDecimalPlace: 1 
            withAnimation: self.decimalPlace1Animation 
         andContainerView: self.containerViewDecimalPlace1];
//etc... for all 6 numbers

, где dollarSlot1Animation - это CABasicAnimation ивар

Метод определен ниже:

- (void) animateDecimalPlace: (int) decimalIndex withAnimation: (CABasicAnimation*)animation andContainerView: (UIView*) containerView{

NSRange decimalRange = {decimalIndex == 0 ? 0 : 2,  
                        decimalIndex == 0 ? 1 : decimalIndex};

double diff = 0;
if (decimalIndex == 0)
{
    int decimalPartTarget = [[[NSString stringWithFormat:@"%f", targetNumber] substringWithRange: decimalRange] intValue];
    int decimalPartCurrent = [[[NSString stringWithFormat:@"%f", currentValue] substringWithRange: decimalRange] intValue]; 
    diff = decimalPartTarget - decimalPartCurrent;
}
else {
    double fullDiff = targetNumber - currentValue;
    diff = [[[NSString stringWithFormat:@"%f", fullDiff] substringWithRange: decimalRange] doubleValue];
}

animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeRemoved;
animation.duration = 5.0;

NSNumber *n = [currentValuesForDecimalPlace objectForKey:[NSValue valueWithNonretainedObject: containerView]];
NSNumber *offset = (NSNumber*)[offsetDict objectForKey: n];
int rotations = [self setupNumberForAnimation: diff decimalIndex: decimalIndex forContainer: containerView];
int finalOffset = (rotations*30)+([offset intValue]);
CATransform3D translation = CATransform3DMakeTranslation(0, finalOffset, 0);

animation.fromValue = [NSValue valueWithCATransform3D:containerView.layer.transform];
animation.toValue = [NSValue valueWithCATransform3D:translation];
animation.delegate = self;
[containerView.layer addAnimation: animation forKey: [NSString stringWithFormat:@"%i", decimalIndex] ];
}

Как вы можете видеть (или, возможно, нет :)), я рассматриваю каждое число в основном независимо, и говорю ему перевести в новую позицию Y,но вы, возможно, заметили там метод с именем setupNumberForAnimation, который является еще одним крупным методом, который динамически добавляет больше UIImageView s к контейнеру UIView над начальными 10 фрагментами изображения.

Например, есть 10 плиток, с которых нужно начать, если я хочу прокрутить шкалу ВВЕРХ на 21 точку (например, от 3 до 24), мне нужно добавить 15 новых чисел выше 9, затеманимируйте перевод представления контейнера до самой верхней части изображения.

После того, как прокрутка сделана, я удаляю динамически добавленные UIImageView s и переставляю смещение по оси y представления контейнера обратно к значениюв пределах от 0 до -270 (по сути, заканчивая тем же номером, но удаляя все ненужные изображения).

Это дает плавную анимацию, которая будет после.И это работает.Поверьте мне:)

Моя проблема двоякая, во-первых, когда анимация останавливается, Core Animation отменяет изменения анимации в слое, потому что я установил animation.fillMode = kCAFillModeRemoved; Я не мог понять, как после завершения анимации, чтобыустановите смещение y слоя контейнера, я знаю, это звучит странно, но если у меня установлено свойство fillMode, равное kCAFillModeForwards, то все, что я пробовал, казалось, не оказало влияния на слой.

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

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

Большое спасибо

Ответы [ 2 ]

1 голос
/ 02 сентября 2010

Я помню, что в видео CoreAnimation WWDC10, которое можно бесплатно скачать с apple, было что-то в проблеме с анимацией, возвращающейся в прежнее состояние.

Я должен посмотреть это позже, но, конечно, есть метод делегата - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag, который вызывается, когда анимация завершена. Это было бы хорошим местом для уборки. Возможно, вы могли бы установить y-смещение слоя, посмотрев на свойство анимации toValue.

Это просто длинный выстрел, но, возможно, он указывает в правильном направлении.

0 голосов
/ 01 сентября 2010

Правильно ли я понимаю, что визуальный эффект, который вы хотите получить, - это иметь колесо чисел (что-то вроде дартс), которое вращается, как старый газовый счетчик на стороне нашего дома, когда мы быливзросление?

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

   const double degreesPerDigit = 360.0 / 10.0;
   const double radPerDigit = degreesPerDigit * M_PI / 180.0;
   CGAffineTransform xfm = CGAffineTransformMakeRotation(radPerDigit * digitUp);
   // animated or otherwise:
   myView.transform = xfm;

На ваши прямые вопросы (опять же, код, набранный в браузере):

Чтобы установить смещение слоя контейнера:

CGRect rect = myLayer.frame;
rect.origin.y = newYValue;
myLayer.frame = rect;

Для защиты от мерцания существует множество методов, в зависимости от точного характера проблемы и желаемого результата, но распространенным является использование двух почти одинаковых представлений, затем внесите изменения в представление1, затем нав последний момент скрывайте view1 и снимайте скрытие view2.

Из ваших вопросов звучит, как будто вы достаточно хорошо понимаете этот материал, поэтому извините, если я ответил ниже вашего уровня, но это трудно понятьточно, что вы делаете, и что идет не так.

Совет: Возможно, стоит потратить время на то, чтобы попытаться разделить вопрос только на важные части и, аналогичным образом, написать сокращенноеПример приложения, которое выполняет ТОЛЬКО эту анимацию и ничего больше, а затем поэкспериментируйте с минимальным тестовым примером.Все модулируй, все регистрируй, изучай вывод.Повторите, промойте, исчезните.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...