cómo hacer una vista de progreso como la que se encuentra en la aplicación de KAYAK

Me pregunto cómo hacer una vista de progreso que se parece a la aplicación KAYAK (es una aplicación de viaje para search vuelos y hoteles), captura de pantalla:

introduzca la descripción de la imagen aquí

Recurro a los resources de la aplicación KAYAK en un iPhone jailbroken y encontré las siguientes 3 imágenes que construyen esta vista de progreso:

progressbar-background@2x.png

introduzca la descripción de la imagen aquí

progressbar-gradient@2x.png

introduzca la descripción de la imagen aquí

progressbar-overlay@2x.png

introduzca la descripción de la imagen aquí

dando a la vista de progreso una image de superposition mobile que se mueve repetidamente junto con la image de gradiente.

cualquier idea o código de muestra sería muy apreciada.

He creado un package de trabajo completo que imitará esta vista de progreso que haya publicado. Sin embargo, para hacerlo más personalizable, no he usado ninguna image, pero he usado CoreGraphics para dibujarlo. El package se puede encontrar en lightdesign / LDProgressView . Probablemente también lo convierta en un CocoaPod si sabes qué es eso.

Cómo dibujar una vista de progreso tipo KAYAK

Todos los trabajos internos de la vista de progreso y, por lo tanto, cómo imitar la vista de progreso de KAYAK se pueden encontrar en este file . Agregué algunos comentarios aquí en los bloques de código para facilitar la comprensión. Aquí está el método drawRect :

 - (void)drawRect:(CGRect)rect { [self setAnimateIfNotSet]; CGContextRef context = UIGraphicsGetCurrentContext(); [self drawProgressBackground:context inRect:rect]; if (self.progress > 0) { [self drawProgress:context withFrame:rect]; } } 

Esto es bastante fácil de explicar. Configuro la propiedad animada si ya no está configurada y dibujo el background. Entonces, si el progreso es mayor 0, dibujaré el progreso dentro del marco total. Pasemos al drawProgressBackground:inRect: method:

 - (void)drawProgressBackground:(CGContextRef)context inRect:(CGRect)rect { CGContextSaveGState(context); // Draw the background with a gray color within a rounded rectangle UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:10]; CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.51f green:0.51f blue:0.51f alpha:1.00f].CGColor); [roundedRect fill]; // Create the inner shadow path UIBezierPath *roundedRectangleNegativePath = [UIBezierPath bezierPathWithRect:CGRectMake(-10, -10, rect.size.width+10, rect.size.height+10)]; [roundedRectangleNegativePath appendPath:roundedRect]; roundedRectangleNegativePath.usesEvenOddFillRule = YES; CGSize shadowOffset = CGSizeMake(0.5, 1); CGContextSaveGState(context); CGFloat xOffset = shadowOffset.width + round(rect.size.width); CGFloat yOffset = shadowOffset.height; CGContextSetShadowWithColor(context, CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)), 5, [[UIColor blackColor] colorWithAlphaComponent:0.7].CGColor); // Draw the inner shadow [roundedRect addClip]; CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(rect.size.width), 0); [roundedRectangleNegativePath applyTransform:transform]; [[UIColor grayColor] setFill]; [roundedRectangleNegativePath fill]; CGContextRestoreGState(context); } 

Aquí, creo un rectángulo networkingondeado dentro de la vista con un radio de 10 (que luego puedo permitir que sea personalizable) y lo complete. Entonces, el rest del código está dibujando la sombra interna, de la que realmente no necesito entrar en detalles. Ahora, aquí está el código para dibujar el progreso en el método drawProgress:withFrame: ::

 - (void)drawProgress:(CGContextRef)context withFrame:(CGRect)frame { CGRect rectToDrawIn = CGRectMake(0, 0, frame.size.width * self.progress, frame.size.height); CGRect insetRect = CGRectInset(rectToDrawIn, 0.5, 0.5); UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:10]; if ([self.flat boolValue]) { CGContextSetFillColorWithColor(context, self.color.CGColor); [roundedRect fill]; } else { CGContextSaveGState(context); [roundedRect addClip]; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGFloat locations[] = {0.0, 1.0}; NSArray *colors = @[(__bridge id)[self.color lighterColor].CGColor, (__bridge id)[self.color darkerColor].CGColor]; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations); CGContextDrawLinearGradient(context, gradient, CGPointMake(insetRect.size.width / 2, 0), CGPointMake(insetRect.size.width / 2, insetRect.size.height), 0); CGContextRestoreGState(context); CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); } CGContextSetStrokeColorWithColor(context, [[self.color darkerColor] darkerColor].CGColor); [self drawStripes:context inRect:insetRect]; [roundedRect stroke]; [self drawRightAlignedLabelInRect:insetRect]; } 

Hay 4 partes principales de este método. Primero, hago el cálculo del marco que tomará el progreso basado en la propiedad self.progress . En segundo lugar, dibujo un color sólido si la propiedad flat está configurada, o dibujo un gradiente calculado (los methods más lighterColor y más darkerColor están en una categoría UIColor ). Tercero, dibujo rayas y finalmente dibujo la label porcentual. Cubrimos esos 2 methods rápidamente. Aquí está el drawStripes:inRect: método:

 - (void)drawStripes:(CGContextRef)context inRect:(CGRect)rect { CGContextSaveGState(context); [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:10] addClip]; CGContextSetFillColorWithColor(context, [[UIColor whiteColor] colorWithAlphaComponent:0.2].CGColor); CGFloat xStart = self.offset, height = rect.size.height, width = STRIPE_WIDTH; while (xStart < rect.size.width) { CGContextSaveGState(context); CGContextMoveToPoint(context, xStart, height); CGContextAddLineToPoint(context, xStart + width * 0.25, 0); CGContextAddLineToPoint(context, xStart + width * 0.75, 0); CGContextAddLineToPoint(context, xStart + width * 0.50, height); CGContextClosePath(context); CGContextFillPath(context); CGContextRestoreGState(context); xStart += width; } CGContextRestoreGState(context); } 

Aquí es donde ocurre la "magia" de la animation. Básicamente, dibujo estas rayas basadas en un self.offset que está en algún lugar entre -STRIPE_WIDTH y 0 como incrementado por un timer. Luego, creo un bucle simple para que solo cree suficientes rayas para completar por completo la parte de progreso de la vista. También dejo el 25% del STRIPE_WIDTH blanco para que las rayas no estén agrupadas una contra la otra. Aquí está el método de dibujo final drawRightAlignedLabelInRect: ::

 - (void)drawRightAlignedLabelInRect:(CGRect)rect { UILabel *label = [[UILabel alloc] initWithFrame:rect]; label.backgroundColor = [UIColor clearColor]; label.textAlignment = NSTextAlignmentRight; label.text = [NSString stringWithFormat:@"%.0f%%", self.progress*100]; label.font = [UIFont boldSystemFontOfSize:17]; UIColor *baseLabelColor = [self.color isLighterColor] ? [UIColor blackColor] : [UIColor whiteColor]; label.textColor = [baseLabelColor colorWithAlphaComponent:0.6]; [label drawTextInRect:CGRectOffset(rect, -6, 0)]; } 

En este método, creo una label con text que se convierte de un flotante (entre 0.0 y 1.0 ) a un porcentaje (de 0% a 100% ). Luego, establezco el color para que sea oscuro o ligero dependiendo de la oscuridad del color de progreso elegido y dibuje la label en CGContext .

Personalización

Hay tres properties que se pueden establecer directamente en una instancia de LDProgressView o de antemano en un método de UIAppearance .

  • Color

El color, obviamente, establecerá el aspecto general del selector. Los gradientes, rayas y / o colors de contorno se determinan de esta manera. El método de UIAppearance sería algo como esto:

  [[LDProgressView appearance] setColor:[UIColor colorWithRed:0.87f green:0.55f blue:0.09f alpha:1.00f]]; 
  • Plano

Esto determinará si el background de la vista de progreso será un degradado o solo la propiedad de color . Este método UIAppearance se vería así:

 [[LDProgressView appearance] setFlat:@NO]; 
  • Animar

Finalmente, esto determinará si las rayas se animarán. Este método UIAppearance también se puede configurar genéricamente para todas las instancias de LDProgressView y se ve así:

 [[LDProgressView appearance] setAnimate:@YES]; 

Conclusión

¡Uf! Esa fue una larga respuesta. Espero no haberlos aburrido demasiado. Si simplemente saltó a aquí, aquí está la esencia para dibujar en código en lugar de con imágenes. Creo que CoreGraphics es una forma superior de dibujar en iOS si tienes time / experiencia, ya que permite más personalización y creo que tiende a ser más rápido.

Aquí hay una image del producto final, que funciona:

LDProgressView

Puede usar NSTimer para actualizar el "desplazamiento" en el que comienza a renderizar la superposition. Puedes encontrar un ejemplo aquí . Es el control OS X y usa dibujo personalizado, no imágenes, pero el principio es el mismo.

¡Ajusta el color y el grosor y ya estás listo! https://www.cocoacontrols.com/controls/ylprogressbar

Este también parece bueno … pero aún no lo he usado. https://github.com/JonasGessner/JGProgressView

¡Buena suerte!

Aquí hay algunos ejemplos en los que puede modificar algunos de los códigos y get la vista de progreso como desee: marque Ejemplo1 y Ejemplo2

La barra de progreso relacionada más cercana en su caso es ADVProgressBar .

Aquí, necesitas hacer un poco de modificación. Al igual que, debe cambiar la image de background y puede agregar esas esquinas networkingondeadas. Después de esos cambios, la barra de progreso se verá exactamente como lo que mencionaste en tu pregunta.


Captura de pantalla :

introduzca la descripción de la imagen aquí

Puede usar este control personalizado de cocoa.

Control ADV

Prueba los controles Cocoa para el ProgressView … como para las imágenes superpuestas, necesitas usar un NSTimer que retransmita en sincronía absoluta con la vista de progreso

Cuando configura su UIProgressView ( self.progress ) nosotros configuramos lo siguiente para get el mismo aspecto:

  UIImage *mynd =[[UIImage imageNamed:@"KgcoJ.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(11, 13, 11, 13)]; UIImage *bakMynd =[[UIImage imageNamed:@"UoS0A.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(11, 13, 11, 13)]; [self.progress setProgressImage:mynd]; [self.progress setTrackImage:bakMynd]; 

Tenga en count que es posible que deba cambiar los nombres de las imágenes y los numbers en UIEdgeInsetsMake según los tamaños de image. Eso te dará lo siguiente: Barra de progreso con imágenes