Анимация при рисовании с помощью CGContext

Мой вопрос заключается в том, как анимировать процесс рисования при рисовании с помощью CGContextRef. Является ли это возможным? Если предположить, то как?

У меня есть два фрагмента кода, которые я хотел бы оживить. Первый рисует индикатор выполнения, а второй рисует простую линейную диаграмму. Рисунок выполняется внутри подкласса UIView.

Прогресс-бар удобен и удобен. Но я хочу, чтобы он как бы вытягивался слева. Я почти уверен, что для этого потребуется использовать что-то другое, кроме UIRectFill, но я не знаю, как это сделать.

- (void)drawProgressLine
{
    [bgColor set];
    UIRectFill(self.bounds);
    [graphColor set];
    UIRectFill(CGRectMake(0, 0, self.frame.size.width / 100 * [[items objectAtIndex:0] floatValue], self.frame.size.height));
}

Линейный график немного сложнее. Мне бы очень хотелось, чтобы он начал рисовать себя слева, строка за строкой, медленно завершая себя вправо, но если это слишком много, как я могу просто постепенно исчезать? Код:

- (void)drawLineChart
{
    [bgColor set];
    UIRectFill(self.bounds);
    [graphColor set];

    if (items.count < 2) return;

    CGRect bounds = CGRectMake(0, 50, self.bounds.size.width, self.bounds.size.height - 100);

    float max = -1;
    for (GraphItem *item in items)
        if (item.value > max)
            max = item.value;

    float xStep = (self.frame.size.width) / (items.count - 1);

    for (int i = 0; i < items.count; i++)
    {
        if (i == items.count - 1) break;

        float itemHeight = bounds.origin.y + bounds.size.height - ((GraphItem*)[items objectAtIndex:i]).value / max * bounds.size.height;
        float nextItemHeight = bounds.origin.y + bounds.size.height - ((GraphItem*)[items objectAtIndex:i + 1]).value / max * bounds.size.height;
        CGPoint start = CGPointMake(xStep * i, itemHeight);
        CGPoint stop = CGPointMake(xStep * (i + 1), nextItemHeight);
        [self drawLineFromPoint:start toPoint:stop lineWidth:1 color:graphColor shadow:YES];
    }
}

Довольно просто, я думаю. Если важно, drawLineFromPoint..... реализовано так:

- (void)drawLineFromPoint:(CGPoint)startPoint toPoint:(CGPoint)endPoint lineWidth:(CGFloat)width color:(UIColor *)color shadow:(BOOL)shadow
{
    if (shadow)
    {
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGFloat components[4] = {0.0, 0.0, 0.0, 1.0};
        CGColorRef shadowColor = CGColorCreate(colorSpace, components);
        CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(1,1), 2.0, shadowColor);
    }

    CGContextBeginPath(context);
    CGContextSetLineWidth(context, width);
    CGContextMoveToPoint(context, startPoint.x, startPoint.y);
    CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
    CGContextClosePath(context);
    [color setStroke];
    CGContextStrokePath(context);
    CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
}

Надеюсь, я ясно выразился, потому что в моей стране сейчас час ночи, и этот пост — последнее, что стоит между мной и моей кроватью. Привет, Ян.


person Majster    schedule 19.08.2013    source источник


Ответы (2)


Похоже, вы не понимаете Цикл отрисовки представления UIKit. Вы понимаете, что каждый раз, когда вы хотите изменить внешний вид вашего пользовательского вида, вам нужно отправить его setNeedsDisplay? И тогда вам нужно полностью перерисовать его в вашем методе drawRect:? Ваш рисунок не появится на экране до тех пор, пока не вернется drawRect:, и после этого вы не сможете больше рисовать в этом виде, пока он не получит другое сообщение drawRect:. Если вы хотите, чтобы содержимое представления было анимировано, вам нужно будет периодически отправлять setNeedsDisplay в представление (скажем, каждую 1/30-ю или 1/60-ю секунды, используя либо NSTimer, либо CADisplayLink).

person rob mayoff    schedule 20.08.2013
comment
Я понимаю, как работает setNeedsDisplay, я делаю рисунок, как описано выше, через drawRect, но он рисует весь график за долю секунды в представлении. Я хочу сделать это красиво и легко, как описано выше. Однако я наткнулся на интересное, но не полное решение, я использую view.frame = CGRectZero; [UIView animateWithDuration:0.5f animations:^(void){view.frame = fullFrame;}];, который делает то, что я хотел. Проблема в том, что представление находится внутри табличного представления и анимируется каждый раз, когда ячейка появляется на экране, я хочу, чтобы оно анимировалось только в первый раз. Любые идеи? - person Majster; 20.08.2013

Похоже, вы разобрались с индикатором выполнения, так что вот что я предлагаю для рисования графика. Просто создайте и отладьте свой код один раз, чтобы нарисовать весь график. Затем используйте прямоугольник клипа, ширину которого вы анимируете, чтобы прямоугольник клипа начинался тонким, а затем расширялся по ширине, пока весь график не стал видимым (слева направо). Это даст пользователю представление о том, что любые линии, которые у вас есть, «рисуются» слева направо, но фактический код очень прост, поскольку шаги анимации просто изменяют прямоугольник клипа, чтобы сделать его шире для каждого «шага». См. этот вопрос для получения дополнительной информации о вызовах CoreGraphics: Как установить вверх по прямоугольнику или области отсечения

person MoDJ    schedule 20.08.2013
comment
Что вы имеете в виду под обработанным? Он не оживляется, он просто появляется. Я вижу, что в этой штуковине с обрезкой может быть некоторый полезный потенциал :) Не могли бы вы расширить, написав пример анимации индикатора выполнения, который растет слева направо? - person Majster; 20.08.2013