Рисование анимации

Я создаю простое приложение, в котором, когда пользователь нажимает кнопку, на экране будет отображаться серия линий, и пользователь сможет видеть эти линии в режиме реального времени (почти как анимация).

Мой код выглядит примерно так (был упрощен):

UIGraphicsBeginImageContext(CGSizeMake(300,300));
CGContextRef context = UIGraphicsGetCurrentContext();

for (int i = 0; i < 100; i++ ) {
    CGContextMoveToPoint(context, i, i);
    CGContextAddLineToPoint(context, i+20, i+20);
    CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
    CGContextStrokePath(context);
}

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

Моя проблема в том, что:

1) Как только пользователь нажимает кнопку, UIThread блокируется до завершения рисования.

2) Я не могу заставить линии рисоваться на экране по одной - я пытался установить UIImage непосредственно внутри цикла, а также попытался установить содержимое слоя внутри цикла.

Как обойти эти проблемы?


person HHHH    schedule 09.10.2012    source источник
comment
Это помогает? stackoverflow.com/ вопросы/9245954/   -  person S.P.    schedule 10.10.2012
comment
какую отсрочку вы хотите?   -  person nielsbot    schedule 10.10.2012
comment
Настраиваемая задержка была бы хороша — я думаю, у Роба было то, что я искал... спасибо за вашу помощь!   -  person HHHH    schedule 11.10.2012


Ответы (1)


Вы говорите «прямо как анимация». Почему бы не сделать настоящую анимацию CABasicAnimation в стиле Core Graphics? Вам действительно нужно показать это в виде серии строк или подойдет правильная анимация?

Если вы хотите анимировать фактическое рисование линии, вы можете сделать что-то вроде:

#import <QuartzCore/QuartzCore.h>

- (void)drawBezierAnimate:(BOOL)animate
{
    UIBezierPath *bezierPath = [self bezierPath];

    CAShapeLayer *bezier = [[CAShapeLayer alloc] init];

    bezier.path          = bezierPath.CGPath;
    bezier.strokeColor   = [UIColor blueColor].CGColor;
    bezier.fillColor     = [UIColor clearColor].CGColor;
    bezier.lineWidth     = 5.0;
    bezier.strokeStart   = 0.0;
    bezier.strokeEnd     = 1.0;
    [self.view.layer addSublayer:bezier];

    if (animate)
    {
        CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        animateStrokeEnd.duration  = 10.0;
        animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f];
        animateStrokeEnd.toValue   = [NSNumber numberWithFloat:1.0f];
        [bezier addAnimation:animateStrokeEnd forKey:@"strokeEndAnimation"];
    }
}

Затем все, что вам нужно сделать, это создать UIBezierPath для вашей строки, например:

- (UIBezierPath *)bezierPath
{
    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointMake(0.0, 0.0)];

    [path addLineToPoint:CGPointMake(200.0, 200.0)];

    return path;
}

Если вы хотите, вы можете объединить несколько строк в один путь, например. вот ряд линий в форме примерно синусоидальной кривой:

- (UIBezierPath *)bezierPath
{
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGPoint point = self.view.center;

    [path moveToPoint:CGPointMake(0, self.view.frame.size.height / 2.0)];

    for (CGFloat f = 0.0; f < M_PI * 2; f += 0.75)
    {
        point = CGPointMake(f / (M_PI * 2) * self.view.frame.size.width, sinf(f) * 200.0 + self.view.frame.size.height / 2.0);
        [path addLineToPoint:point];
    }

    return path;
}

И они не блокируют основной поток.

Кстати, вам, очевидно, придется добавить CoreGraphics.framework к Build Settings вашей цели под Link Binary With Libraries.

person Rob    schedule 10.10.2012
comment
Это отлично - спасибо! Еще один вопрос - я вызываю [path addLineToPoint] много раз (может быть, 1000+). После того, как я применил ваш метод, я обнаружил, что мое приложение и устройство, на котором оно работает, становятся чрезвычайно медленными/рывками. Я профилировал приложение и, похоже, не использует слишком много ресурсов ЦП. Знаете, почему это так? - person HHHH; 11.10.2012
comment
@HHHH Я легко мог представить, что это облагает налогом устройство. Как только вы начнете превышать, скажем, 30 сегментов линии в секунду, приложению будет сложнее идти в ногу с анимацией. Я не знаю, где находится отсечка (это 30/секунду? 60? 100? я не знаю), но в какой-то момент количество времени, которое требуется, чтобы нарисовать пути для данного кадра анимации, тем дольше будет длиться каждый кадр. И как только анимация замедлится до намного меньше 30 кадров в секунду, она будет более дерганной. Вы должны каким-то образом сократить количество отрезков, которые нужно отрисовывать в секунду. Вздох. - person Rob; 12.10.2012
comment
@HHHH Возможно, вы можете установить продолжительность анимации как x / y, где x — количество сегментов линии, а y — количество сегментов в секунду, которые вы будете поддерживать (значение, с которым вы, вероятно, захотите поэкспериментировать, чтобы выяснить, что вам сойдет с рук без каких-либо рывков, может быть, 30, или 60, или 100, или ...). - person Rob; 12.10.2012
comment
Это имеет смысл - производительность определенно улучшается, когда я сокращаю количество строк. Спасибо большое за вашу помощь! - person HHHH; 12.10.2012