2010-09-01 20 views
6

Im tratando de crear un control giratorio, básicamente un conjunto de 6 dígitos que dan vueltas y vueltas para dar el efecto de un medidor giratorio (similar a su potencia/agua metros, o tal vez una máquina de póquer, de hecho muy similar al control UIPickerView existente, pero con un aspecto completamente diferente).Dificultades principales de la animación con el control de cuadrante giratorio (muy detallado)

Hasta ahora, casi lo tengo funcionando, pero estoy en una etapa en la que la animación principal me está causando dolor.

Es bastante complejo, por lo que los fragmentos de código serían difíciles de dar una buena instantánea de lo que está sucediendo, por lo que creo que el pseudocódigo será suficiente.

En primer lugar, en términos de la configuración de vista, tengo 6 UIView s separados (llamados NumberView1, NumberView2, etc ...), cada uno para cada número en el control.

Dentro de cada NumberViewX tengo otro UIView, que es una vista recipiente, llamado ContainerView1, 2, etc ...

I entonces tener 10 UIImageView s apiladas una encima de la otra en diferentes offsets y. Estas imágenes son todas 30x30 lo que lo hace agradable. 9 es primero, luego 8 en y-offset 30, luego 7 en y-offset 60, etc. ... todo el camino hasta 0 en y-offset 270.

NOTA IMPORTANTE: Mis números solo se desplazan hacia arriba

Los números representan un número de 5 puntos decimales (es decir, 2.34677) que se desplaza hacia arriba (a, por ejemplo, 2.61722).

También tengo algunos diccionarios que mantienen el valor numérico actual para cada número y los desplazamientos para cada número:

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]]; 

ya por la lógica, comienzo a cabo el ajuste de las propiedades de capa del ContainerView ser ciertas compensaciones de Y que moverá los números actuales en sus lugares correctos, así:

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

que mostrará los números, 2,83385, que es un número arbitrario por el bien de pruebas.

Entonces entro en otro valor en un cuadro de texto y pulsa el botón Ir, que da inicio a la lógica de la animación, que es donde consigo las dificultades en las materias básicas de animación (como sugiere el título)

que llamo:

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

donde dollarSlot1Animation es un Ivar CABasicAnimation

se define el método siguiente:

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

Como puede ver (o tal vez no :)) estoy tratando cada número básicamente independiente, y diciéndole que se traduzca a una nueva posición Y, pero puede haber notado un método llamado setupNumberForAnimation que es otro método grande que agrega dinámicamente más UIImageView s al contenedor UIView anterior los 10 mosaicos iniciales de la imagen.

Por ejemplo, hay 10 fichas para comenzar, si quiero desplazar el dial UP 21 puntos (pasando de 3 a 24, por ejemplo) necesitaría agregar 15 números nuevos por encima de 9, luego animar el traducción de la vista del contenedor hasta la parte superior del mosaico de la imagen.

Después de que el desplazamiento esté listo, elimino los UIImageView s dinámicamente agregados y vuelva a colocar la vista y del desplazamiento de la vista del contenedor a un valor entre 0 y -270 (esencialmente terminando en el mismo número, pero quitando todas las -imágenes de imagen necesarias).

Esto da la animación suave que im después. Y funciona. Confía en mí :)

Mi problema es doble, en primer lugar cuando la animación se detiene Núcleo de animación revierte los cambios de animaciones a la capa porque he configurado animation.fillMode = kCAFillModeRemoved; No pude entender cómo, una vez completadas las animaciones, establecer la capa del contenedor y offset, sé que suena extraño, pero si tengo la propiedad fillMode establecida en kCAFillModeForwards, nada de lo que intento parece tener un efecto en la capa.

En segundo lugar, cuando la animación se detiene y el cambio se revierte, hay un pequeño parpadeo entre cuando la animación revierte la traducción y cuando lo configuro de nuevo, no puedo encontrar la manera de evitarlo.

Sé que hay un montón de detalles y especificidad aquí, pero cualquier ayuda o idea sobre cómo lograr esto sería muy apreciada.

Muchas gracias

+0

Es mejor que ser vago (como este comentario). – BoltClock

+0

Una tesis a juzgar por un profesor;) –

+0

Gracias por la "ayuda" ... Sé que es una publicación detallada, pero estoy abierto a otras ideas también, estoy feliz de eliminar mi implementación por completo – Mark

Respuesta

1

Recuerdo que había algo sobre el problema de la animación volviendo a su estado anterior en los videos de CoreAnimation de WWDC10 que puedes descargar gratis de apple.

Tengo que buscarlo más tarde, pero está por supuesto el método de delegado - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag que se llama cuando la animación está hecha. Este sería un buen lugar para hacer algunas tareas domésticas. Tal vez podría establecer el desplazamiento y de la capa mirando la propiedad toValue de la animación.

Es una posibilidad remota, pero tal vez esté apuntando en la dirección correcta.

+0

Sí, gracias, la forma en que supere este problema al final fue para cambiar la propiedad fillMode a kCAFillModeForwards, establecer removedOnCompletion = NO y luego una vez completada la animación (en el método animationDidStop: llamar a removeAllAnimations en la capa que estaba siendo animada, luego configurarlo inmediatamente después funcionaba como un amuleto sin saltos ... – Mark

+0

Gracias por el comentario. Al menos supuse que era correcto. – GorillaPatch

+0

marcando esto como correcto, ya que mi comentario y su publicación resuelven los problemas que estaba teniendo en la pregunta – Mark

0

¿Entiendo correctamente que el efecto visual que desea es tener una rueda de números (más o menos un poco como una diana) que giran en torno al igual que el medidor de gas/agua de estilo antiguo en el lado de nuestra casa cuando estábamos creciendo?

Si es así, ¿por qué no tiene una sola imagen con los dígitos dibujados en un círculo, y luego rota esa imagen la cantidad deseada? Algo así como (código no probado escrito en el navegador):

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; 

Para sus preguntas directas (de nuevo, códigos introducido en el navegador):

Para establecer de una capa contenedora offset:

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

Para ayudar protegerse contra el parpadeo, hay muchas técnicas, dependiendo de la naturaleza exacta del problema y el resultado deseado, pero una común es usar 2 vistas que son casi idénticas, hacer cambios en la vista1 y luego, en el último momento, ocultar view1 y un-hide view2.

Según sus preguntas, parece que tiene una comprensión decente de estas cosas, así que lamento si respondí debajo de su nivel, pero es difícil saber exactamente qué está haciendo y qué está fallando.

Asesoramiento: Puede que valga la pena intentar reducir la pregunta solo a los bits importantes y, del mismo modo, escribir una aplicación de muestra simplificada que SOLAMENTE esta animación y nada más, y luego experimentar con el caso de prueba mínimo. Modular todo, registrar todo, estudiar la salida. Repita, enjuague, desvanezca.

+0

Su idea sobre girar una circular La imagen es interesante, ¿podrías elaborar o esto un poco más? Si es lo que creo que es, (un círculo de números) ¿no se vería como si los números estuvieran entrando en un ángulo en lugar de hacia abajo? ¿Tiene sentido? ¡Gracias por la respuesta! – Mark

+0

En otra nota, ¿qué tan difícil sería implementar un spinner 3D? ¿Podrías hacer esto simplemente usando Core Animation? Eso sería rock! – Mark

+0

Es por eso que dije que no está claro qué efecto visual desea. ¿Estás buscando algo así como una máquina tragamonedas? ¿O algo más parecido a los medidores de gas de la vieja escuela? O algo más, completamente. Si pudieras proporcionar un enlace al aspecto general que deseas, eso nos ayudaría a responder. – Olie

Cuestiones relacionadas