Я сделал весь рабочий пакет, который имитирует опубликованное вами представление прогресса. Однако, чтобы сделать его более настраиваемым, я не использовал изображения, но использовал CoreGraphics для их рисования. Пакет можно найти по адресу lightdesign / LDProgressView. Я также, вероятно, сделаю из него CocoaPod, если вы знаете, что это такое.
Как нарисовать прогресс в стиле KAYAK
Всю внутреннюю работу представления прогресса и, следовательно, то, как имитировать представление прогресса KAYAK, можно найти в этот файл. Я добавил несколько комментариев сюда, в блоки кода, для облегчения понимания. Вот метод drawRect
:
- (void)drawRect:(CGRect)rect {
[self setAnimateIfNotSet];
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawProgressBackground:context inRect:rect];
if (self.progress > 0) {
[self drawProgress:context withFrame:rect];
}
}
Это довольно понятно. Я устанавливаю свойство animate, если оно еще не установлено, и рисую фон. Затем, если прогресс больше 0, я нарисую прогресс в пределах всего кадра. Перейдем к drawProgressBackground:inRect:
методу:
- (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);
}
Здесь я создаю прямоугольник с закругленными углами внутри вида с радиусом 10
(который позже я могу разрешить настраивать) и заполняю его. Затем остальная часть кода рисует внутреннюю тень, о которой мне действительно не нужно вдаваться в подробности. Теперь вот код для рисования прогресса в методе 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];
}
Этот метод состоит из 4 основных частей. Сначала я вычисляю кадр, который займет прогресс, на основе свойства self.progress
. Во-вторых, я рисую либо сплошным цветом, если установлено свойство flat
, либо рисую рассчитанный градиент (методы lighterColor
и darkerColor
относятся к категории UIColor
). В-третьих, я рисую полосы и, наконец, рисую метку процента. Давайте быстро рассмотрим эти 2 метода. Вот метод drawStripes:inRect:
:
- (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);
}
Вот где происходит «волшебство» анимации. По сути, я рисую эти полосы на основе self.offset
, который находится где-то между -STRIPE_WIDTH
и 0
, увеличиваемый таймером. Затем я создаю простой цикл, так что я создаю достаточно полос, чтобы полностью заполнить часть прогресса представления. Я также оставляю 25% STRIPE_WIDTH
пустыми, чтобы полосы не слипались друг с другом. Вот последний метод рисования 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)];
}
В этом методе я создаю метку с текстом, который преобразуется из числа с плавающей запятой (от 0.0
до 1.0
) в процентное значение (от 0%
до 100%
). Затем я либо устанавливаю темный, либо светлый цвет в зависимости от темноты выбранного цвета прогресса и рисую метку в CGContext
.
Возможность настройки
Есть три свойства, которые можно установить либо непосредственно в экземпляре LDProgressView
, либо заранее в UIAppearance
методе.
Цвет, очевидно, будет определять общий вид сборщика. На этом основании определяются градиенты, полосы и / или цвета контура. Метод UIAppearance
будет примерно таким:
[[LDProgressView appearance] setColor:[UIColor colorWithRed:0.87f green:0.55f blue:0.09f alpha:1.00f]];
Это определит, будет ли фон окна прогресса градиентом или просто свойством color
. Этот UIAppearance
метод будет выглядеть примерно так:
[[LDProgressView appearance] setFlat:@NO];
Наконец, это определит, будут ли полосы анимированы. Этот UIAppearance
метод также может быть в общем установлен для всех экземпляров LDProgressView
и выглядит следующим образом:
[[LDProgressView appearance] setAnimate:@YES];
Вывод
Ух! Это был длинный ответ. Надеюсь, я не слишком утомлял вас, ребята. Если вы просто пропустили сюда, вот суть рисования в коде, а не с изображениями. Я думаю, что CoreGraphics - лучший способ рисования на iOS, если у вас есть время / опыт, поскольку он позволяет больше настраивать и, как мне кажется, имеет тенденцию быть быстрее.
Вот изображение окончательного рабочего продукта:
![LDProgressView](https://dl.dropboxusercontent.com/u/20180054/Github%20Resources/LD-progress-view-improved.png)
person
Christian Di Lorenzo
schedule
28.09.2013