Cómo crear un círculo con un relleno por porcentaje en Objective-C

Hice esto usando CAShapeLayer, CABasicAnimation y un UIBezierPath:

1.) Aquí está el controlador de vista:

Este es el archivo de encabezado de stock, nada que hacer aquí.

// ViewController.h
// CircleTest
//
// Creado por J Mundie el 29/04/15.
// Copyright (c) 2015 jpm165. Todos los derechos reservados.
//

#import

@interface ViewController: UIViewController

@fin

En la implementación, estamos utilizando un método llamado drawCircle para configurar el UIBezierPath y dibujarlo en una vista subclasificada de UIView.

//
// ViewController.m
// CircleTest
//
// Creado por J Mundie el 29/04/15.
// Copyright (c) 2015 jpm165. Todos los derechos reservados.
//

#import “ViewController.h”
#import “CircleView.h”
#import

#definir grados ToRadians (grados) ((grados) / 180.0 * M_PI)

@interface ViewController ()

{
CGRect drawbox;
}

@property (no atómico, fuerte) IBOutlet CircleView * cv;
@property (no atómico, fuerte) UIBezierPath * circlePath;

@fin

@implementation ViewController

– (nulo) viewDidLoad {
[super viewDidLoad];
// Realice cualquier configuración adicional después de cargar la vista, generalmente desde una punta.
[self drawCircle];
[self.cv drawLineWithPath: self.circlePath];
}

– (nulo) drawCircle {
CGFloat start = degreesToRadians (270);
CGFloat end = degreesToRadians (630);
Origen de CGPoint = CGPointMake (self.cv.frame.origin.x + 4, self.cv.frame.origin.y + 4);
drawbox = CGRectMake (origin.x, origin.y, self.cv.frame.size.width-2, self.cv.frame.size.height-2);
CGPoint midPoint = CGPointMake (CGRectGetWidth (drawbox) / 2, CGRectGetWidth (drawbox) / 2);
CGFloat lineWidth = (CGRectGetWidth (drawbox) / 2) * 0.25;
CGFloat radio = (CGRectGetWidth (drawbox) / 2) -lineWidth / 2-2;
self.circlePath = [UIBezierPath bezierPathWithArcCenter: radio del punto medio: radio startAngle: start endAngle: end clockwise: true];
self.circlePath.lineWidth = lineWidth;
}

@fin

2. En mi Vista personalizada, estoy dibujando la ruta y actualizando y mostrando el progreso. Aquí se puede hacer más al anotar el progreso en el tiempo que lleva animar. El crédito donde corresponde se anota en el archivo.

Primero, aquí está el encabezado. Nada sofisticado…

//
// CircleView.h
// CircleTest
//
// Creado por J Mundie el 29/04/15.
// Copyright (c) 2015 jpm165. Todos los derechos reservados.
//

#import

@interface CircleView: UIView

– (vacío) drawLineWithPath: (UIBezierPath *) ruta;

@fin

A continuación, aquí está la implementación. Desglosaré esto por método.

Primero definimos un método auxiliar para convertir grados a radianes.

//
// CircleView.m
// CircleTest
//
// Creado por J Mundie el 29/04/15.
// Copyright (c) 2015 jpm165. Todos los derechos reservados.
//
// basado en tecnología móvil y de redes sociales

#import “CircleView.h”
#import “LayerWithProgress.h”

#definir grados ToRadians (grados) ((grados) / 180.0 * M_PI)

Luego, configure los bordes para el indicador de progreso y una etiqueta para el porcentaje.

@interface CircleView ()

@property (no atómico, fuerte) UIBezierPath * externalPath;
@property (no atómico, fuerte) UIBezierPath * innerPath;
@property (no atómico, fuerte) UILabel * lblProgress;

@fin

@implementation CircleView

El siguiente es un método que agrega la etiqueta, dimensionada correctamente.

– (nulo) addProgressLabelForPath: (UIBezierPath *) ruta {
NSString * lblProgressTxt = @ “100%”;
UIFont * myFont = [UIFont fontWithName: @ tamaño “HelveticaNeue-Thin”: 60.0];
Tamaño de CGSize = [lblProgressTxt sizeWithAttributes: @ {NSFontAttributeName: myFont}];
CGRect oldRect = path.bounds;
self.lblProgress = [[UILabel alloc] initWithFrame: CGRectMake (CGRectGetMidX (oldRect) -size.width / 2, CGRectGetMidY (oldRect) -size.height / 2, size.width, size.height)];
[self addSubview: self.lblProgress];
self.lblProgress.text = lblProgressTxt;
self.lblProgress.font = myFont;
self.lblProgress.backgroundColor = [UIColor clearColor];
self.lblProgress.textAlignment = NSTextAlignmentCenter;
}

Ahora, dibuja las rutas de los bordes exterior e interior. Luego dibuje la línea correspondiente a la ruta que configuramos en el controlador de vista. Finalmente, anime el dibujo del círculo.

– (nulo) drawLineWithPath: (UIBezierPath *) ruta {

[self drawOuterPathForPath: ruta];
[self drawInnerPathForPath: ruta];
[self addProgressLabelForPath: ruta];

CAShapeLayer * pathLayer = [capa CAShapeLayer];
pathLayer.frame = CGRectMake (path.bounds.origin.x-path.lineWidth / 2-1, path.bounds.origin.y-path.lineWidth / 2-1, path.bounds.size.width, path.bounds. tamaño.Altura);
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [UIColor greenColor] .CGColor;
pathLayer.fillColor = nil;
pathLayer.lineWidth = path.lineWidth;
pathLayer.lineJoin = kCALineJoinBevel;

[self.layer addSublayer: pathLayer];

CABasicAnimation * pathAnimation = [animación de CABasicAnimationWithKeyPath: @ “strokeEnd”];
pathAnimation.duration = 10.0;
pathAnimation.fromValue = @ 0;
pathAnimation.toValue = @ 1;
[pathLayer addAnimation: pathAnimation forKey: @ “strokeEnd”];

Continuando con este método, tenemos que utilizar una subclase personalizada para obtener el progreso a medida que se dibuja la línea. Hacemos esto subclasificando CAShapeLayer y agregando un método de devolución de llamada. Luego solo agregue la animación con los mismos atributos que el círculo dibujando uno.

LayerWithProgress * progressLayer = [capa LayerWithProgress];
progressLayer.frame = CGRectMake (0, -1, 1, 1);
progressLayer.progressdelegate = self;
[self.layer addSublayer: progressLayer];

CABasicAnimation * progressAnimation = [CABasicAnimation animationWithKeyPath: @ “progress”];
progressAnimation.duration = 10.0;
progressAnimation.beginTime = 0;
progressAnimation.fromValue = @ 0;
progressAnimation.toValue = @ 1;
progressAnimation.fillMode = kCAFillModeForwards;
progressAnimation.removedOnCompletion = NO;

[progressLayer addAnimation: progressAnimation forKey: @ “progress”];

self.lblProgress.text = @ “0%”;

}

El siguiente bit solo actualiza la etiqueta, manteniendo todo centrado.

– (nulo) progressUpdatedTo: (CGFloat) progress {
NSLog (@ “Progreso:% f”, progreso);
// [self bringSubviewToFront: self.lblProgress];
NSString * padStr = @ “”;
NSInteger num = progreso * 100 + 1;
si (num <10) {
padStr = @ “”;
}
if (num == 100) {
padStr = @ “”;
}

self.lblProgress.text = [NSString stringWithFormat: @ “% @% ld %%”, padStr, (long) num];
}

Finalmente, métodos para dibujar los caminos internos y externos.

– (nulo) drawOuterPathForPath: (UIBezierPath *) ruta {
CGRect oldRect = path.bounds;
Radio CGFloat = CGRectGetWidth (oldRect) /2+path.lineWidth/2+1;
CGPoint midPoint = CGPointMake (CGRectGetWidth (self.frame) / 2, CGRectGetHeight (self.frame) / 2);
self.outerPath = [UIBezierPath bezierPathWithArcCenter: radio del punto medio: radio startAngle: degreesToRadians (270) endAngle: degreesToRadians (630) en sentido horario: SÍ];
self.outerPath.lineWidth = 2.0f;
}

– (nulo) drawInnerPathForPath: (UIBezierPath *) ruta {
CGRect oldRect = path.bounds;
Radio CGFloat = CGRectGetWidth (oldRect) /2-path.lineWidth/2-1;
CGPoint midPoint = CGPointMake (CGRectGetWidth (self.frame) / 2, CGRectGetHeight (self.frame) / 2);
self.innerPath = [UIBezierPath bezierPathWithArcCenter: radio del punto medio: radio startAngle: degreesToRadians (270) endAngle: degreesToRadians (630) en sentido horario: SÍ];
self.innerPath.lineWidth = 2.0f;
}

– (nulo) drawRect: (CGRect) rect {
[[UIColor blackColor] setStroke];
[self.outerPath stroke];
[self.innerPath stroke];
}

@fin

3. Y aquí está la clase CAShapeLayer personalizada para obtener el progreso. Encontré esto aquí: devolución de llamada de progreso de animación central

Primero el archivo de encabezado con protocolo. Tuve que cambiar la variable delegado para no interferir con el delegado CAShapeLayer.

//
// LayerWithProgress.h
// CircleTest
//
// Creado por J Mundie el 29/04/15.
// Copyright (c) 2015 jpm165. Todos los derechos reservados.
//

#import

@protocol LayerWithProgressProtocol

– (nulo) progressUpdatedTo: (CGFloat) progreso;

@fin

@interface LayerWithProgress: CAShapeLayer

@property CGFloat progress;
@property (débil) id progressdelegate;

@fin

Y aquí está la implementación.

//
// LayerWithProgress.m
// CircleTest
//
// Creado por J Mundie el 29/04/15.
// Copyright (c) 2015 jpm165. Todos los derechos reservados.
//
// basado en la devolución de llamada de progreso de animación de Core

#import “LayerWithProgress.h”

@implementation LayerWithProgress

– (id) initWithLayer: capa (id)
{
self = [super initWithLayer: capa];
si (auto) {
LayerWithProgress * otherLayer = (LayerWithProgress *) capa;
self.progress = otherLayer.progress;
self.progressdelegate = otherLayer.progressdelegate;
}
volver a sí mismo;
}

+ (BOOL) needsDisplayForKey: tecla (NSString *) {
if ([key isEqualToString: @ “progress”]) {
devuelva SÍ;
} más {
return [super needsDisplayForKey: clave];
}
}

– (nulo) drawInContext: (CGContextRef) ctx
{
if (self.progressdelegate)
{
[self.progressdelegate progressUpdatedTo: self.progress];
}
}

@fin

En resumen, la imagen de arriba es de 2 círculos y 1 campo de texto.

Entonces, si dibuja un círculo más grande en el fondo, es fácil dibujar una “rebanada de pastel” normal en ese círculo. El círculo frontal siempre está lleno y luego tienes un campo de texto sobre todo.

Este es un efecto bastante bueno, pero muy simple.

Ve y obtén PaintCode. Agregue un círculo de fondo al lienzo, elimine el trazo y configure el relleno en gris oscuro. Agrega otro círculo, justo encima de la parte superior del primero, elimina el trazo y hazlo verde. Para ese segundo círculo verde, establezca la propiedad ‘ángulo de inicio’ en 270. El ‘ángulo final’ cambia según dónde desee que se detenga el relleno verde (tenga en cuenta que 0 grados es la posición de las tres en punto, por lo que debe haz un poco de matemática para traducir tu porcentaje a ángulo: Ray Wenderlicht ha escrito algunos tutoriales que cubren esto). Finalmente, agregue un tercer círculo más pequeño y hágalo gris claro. Centrarlo sobre la parte superior de los otros dos círculos. Use la tecla Mayús para mantener los círculos perfectamente circulares cuando los arrastre.

PaintCode genera código Objective-C que puede agregar al método drawRect de su vista personalizada. Aquí hay un fragmento muy rápido de cómo ese código busca el círculo en su pregunta: https://gist.github.com/adrianoc

Recomiendo encarecidamente PaintCode para este tipo de trabajo: no es necesario calcular las coordenadas de los trazados / formas a mano.

Si necesita ayuda para calcular los ángulos o usar drawRect, consulte los tutoriales en el sitio de Ray Wenderlicht. Si estás luchando con algo específico, StackOverflow tendrá la respuesta.

No lo he intentado pero puedes usar los métodos de UIView para dibujar un círculo como ese.
2 círculos exteriores.
un círculo exterior primero con un ángulo central, luego rellena el color deseado.
Segundo círculo exterior con un ángulo de 360 ​​xy rellene ese color oscuro.
Luego dibuje un círculo interior con un radio menor y rellene de color blanco.

Puede que no sea el más optimizado, pero supongo que sería suficiente su requisito 🙂

dibuje un círculo usando la trama central o usando sus propias habilidades matemáticas extendiendo una clase uiview con el tamaño que desee. Cree una etiqueta con un radio de esquina del 50% y establezca el centro de la etiqueta como centro del círculo. Eso es.

Para esto puedes seguir este enlace: –

Barras de progreso circular en iOS

Espero que te ayude …