具有Core Graphic的iOS 7中的动态绘图性能

时间:2013-12-08 16:07:38

标签: ios performance dynamic drawing quartz-2d

我需要一些关于在iOS中绘图的建议,更具体地说是关于性能绘制的建议。我在iOS7上阅读了很多关于绘图的文章,但是我没有成功获得正确的结果。

我有点比我需要以正确的顺序链接它们。 感谢我的Dot&弹性类,我成功了这个渲染。我很满意: http://d.pr/v/nYzH

此点代表一张卡片,我需要使用时间轴在每张卡片之间导航。 我使用iCarousel库来实现这个目标。我的工作就像一个UITableView,除了我们管理视图。视图可以重复使用等......

但问题从那里开始。这是结果: http://d.pr/v/y7dq

第一个问题:点的分辨率很低。 第二个真正的问题:我有一些滞后..

你可以按照这里我用来绘制点和点的文件弹性的。

Dot.m文件

@interface Dot () {
    CAShapeLayer *_foreground;
    CAShapeLayer *_background;
    UIBezierPath *_path;
}
@end

@implementation Dot

- (id)initWithPoint:(CGPoint)point andRadius:(CGFloat)radius
{
    self = [super init];
    if (self) {
        self.frame    = CGRectMake(point.x, point.y, (radius + 10) * 2, (radius + 10) * 2);
        self.radius   = radius;
        self.color    = [UIColor colorWithHexString:@"#FFFFFF"];
        [self setPosition:point];
        [self setupPath];

        _background = [CAShapeLayer layer];
        [_background setFrame:CGRectMake(0, 0, self.width, self.height)];
        [_background setPath:_path.CGPath];
        [self.layer addSublayer:_background];
        [self drawStrokedDotToLayer:_background];

        _foreground = [CAShapeLayer layer];
        [_foreground setFrame:CGRectMake(0, 0, self.width, self.height)];
        [_foreground setPath:_path.CGPath];
        [self.layer addSublayer:_foreground];
        [self drawPlainDotToLayer:_foreground];

        [self setBackgroundColor:[UIColor clearColor]];

        self.state    = DotStateHidden;
    }
    return self;
}

- (void)setupPath
{
    _path = [UIBezierPath bezierPath];
    [_path addArcWithCenter:CGPointMake(self.width*.5f, self.height*.5f) radius:self.radius startAngle:0 endAngle:M_PI * 2 clockwise:NO];
}

#pragma mark - Setters

- (void)setPosition:(CGPoint)position
{
    self.x = position.x - self.width*.5f;
    self.y = position.y - self.width*.5f;
}

- (CGPoint)position
{
    return CGPointMake(self.x + self.width*.5f, self.y + self.height*.5f);
}

- (void)setState:(DotState)state
{
    _state = state;

    CAKeyframeAnimation *foregroundAnim = nil;
    CAKeyframeAnimation *backgroundAnim = nil;

    switch (_state) {
        case DotStateFeedback:
        {
            self.color = [UIColor colorWithHexString:@"#FFFFFF"];
            [self drawFeedback:_foreground];
            [self removeGlow:_foreground];

            foregroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:1.f];
            break;
        }

        case DotStateVisible:
        {
            self.color = [UIColor colorWithHexString:@"#FFFFFF"];
            [self drawPlainDotToLayer:_foreground];
            [self drawGlow:_foreground];
            [self drawStrokedDotToLayer:_background];

            foregroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:.2f];
            break;
        }
        case DotStateNext:
        {
            self.color = [UIColor colorWithHexString:@"#FFFFFF"];
            [self drawStrokedDotToLayer:_background];

            foregroundAnim = [self animation:ScaleOut function:ExponentialEaseOut duration:.16f];
            backgroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:.25f];
            [foregroundAnim setBeginTime:CACurrentMediaTime() + .04f];
            break;
        }

        case DotStateHidden:
        default:
        {
            self.color = [UIColor colorWithHexString:@"#333333"];
            [self drawPlainDotToLayer:_foreground];
            [self removeGlow:_foreground];

            foregroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:.5f];
            backgroundAnim = [self animation:ScaleOut function:ExponentialEaseOut duration:.25f];
            break;
        }
    }

    if (foregroundAnim) [_foreground addAnimation:foregroundAnim forKey:nil];
    if (backgroundAnim) [_background addAnimation:backgroundAnim forKey:nil];
}

#pragma mark - Drawing

- (void)drawStrokedDotToLayer:(CAShapeLayer *)layer
{
    [layer setLineWidth:2.f];
    [layer setStrokeColor:self.color.CGColor];
    [layer setFillColor:[UIColor colorWithHexString:@"#1F1F1F"].CGColor];
}

- (void)drawPlainDotToLayer:(CAShapeLayer *)layer
{
    [layer setLineWidth:2.f];
    [layer setStrokeColor:[UIColor clearColor].CGColor];
    [layer setFillColor:self.color.CGColor];
}

- (void)drawFeedback:(CAShapeLayer *)layer
{
    [layer setLineWidth:2.f];
    [layer setStrokeColor:[UIColor clearColor].CGColor];
    [layer setFillColor:[UIColor colorWithHex:0xFFFFFF andAlpha:.025f].CGColor];
}

- (void)drawGlow:(CAShapeLayer *)layer
{
    [layer setShadowRadius:8];
    [layer setShadowOpacity:1.f];
    [layer setShadowOffset:CGSizeMake(0, 0)];
    [layer setShadowColor:[UIColor whiteColor].CGColor];
    [layer didChangeValueForKey:@"path"];
}

- (void)removeGlow:(CAShapeLayer *)layer
{
    [layer setShadowRadius:0];
    [layer setShadowOpacity:0.f];
    [layer setShadowOffset:CGSizeMake(0, 0)];
    [layer setShadowColor:[UIColor clearColor].CGColor];
    [layer didChangeValueForKey:@"path"];
}

- (CAKeyframeAnimation *)animation:(NSInteger)name function:(AHEasingFunction)function duration:(CGFloat)duration
{
    CAKeyframeAnimation *animation;

    switch (name) {
        case ScaleIn:
        {
            animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale" function:function fromValue:0.f toValue:1.f];
            break;
        }

        case ScaleInFeedback:
        {
            animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale" function:function fromValue:0.f toValue:3.f];
            break;
        }

        case ScaleOut:
        {
            animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale" function:function fromValue:1.f toValue:0.f];
            break;
        }

        default:
        {
            animation = [CAKeyframeAnimation animationWithKeyPath:@"opacity" function:function fromValue:0.f toValue:1.f];
            break;
        }
    }

    animation.duration = duration;
    animation.fillMode = kCAFillModeForwards;
    animation.removedOnCompletion = NO;

    return animation;
}

@end

Elastic.m

@interface Elastic () {
    UIBezierPath *_path;
    UIImage *_image;
}

@end

@implementation Elastic

- (id)initWithDotOne:(Dot *)aDotOne DotTwo:(Dot *)aDotTwo
{
    self = [super initWithFrame:[UIScreen mainScreen].bounds];
    if (self) {
        self.dotOne = aDotOne;
        self.dotTwo = aDotTwo;

        self.displayDots = NO;
        self.feedBack    = NO;

        self.curveRadius = 4;

        [self setBackgroundColor:[UIColor clearColor]];

        _path = [UIBezierPath bezierPath];
        [self updatePath];
    }
    return self;
}

- (void)setDisplayDots:(BOOL)displayDots
{
    _displayDots = displayDots;

    if (_displayDots) {
        [self addSubview:self.dotTwo];
    } else {
        [self.dotTwo removeFromSuperview];
    }
}

- (void)drawRect:(CGRect)rect
{
    [_image drawInRect:rect];
}

- (void)updatePath
{
    // Initialize variables
    CGFloat dist  = distance(self.dotOne.position, self.dotTwo.position);
    CGFloat angle = angleBetweenPoints(self.dotOne.position, self.dotTwo.position) + M_PI * 1.5;

    // Points
    CGPoint ptA = CGPointMake(
                              self.dotOne.position.x + cosf(angle) * (self.dotOne.radius - self.curveRadius),
                              self.dotOne.position.y + sinf(angle) * (self.dotOne.radius - self.curveRadius)
                              );
    CGPoint ptB = CGPointMake(
                              self.dotOne.position.x + cosf(angle + M_PI) * (self.dotOne.radius - self.curveRadius),
                              self.dotOne.position.y + sinf(angle + M_PI) * (self.dotOne.radius - self.curveRadius)
                              );

    CGPoint ptC = CGPointMake(
                              self.dotTwo.position.x + cosf(angle) * (self.dotTwo.radius - self.curveRadius),
                              self.dotTwo.position.y + sinf(angle) * (self.dotTwo.radius - self.curveRadius)
                              );
    CGPoint ptD = CGPointMake(
                              self.dotTwo.position.x + cosf(angle + M_PI) * (self.dotTwo.radius - self.curveRadius),
                              self.dotTwo.position.y + sinf(angle + M_PI) * (self.dotTwo.radius - self.curveRadius)
                              );

    // Bezier
    CGFloat mapA = angle + M_PI_2 + map(dist, 150, 350, 0.0001, 0.0005);
    CGFloat mapB = angle + M_PI_2 - map(dist, 150, 350, 0.0001, 0.0005);

    CGPoint bzA = CGPointMake(self.dotOne.position.x + cosf(mapA) * dist*.5f, self.dotOne.position.y + sinf(mapA) * dist*.5f);
    CGPoint bzB = CGPointMake(self.dotOne.position.x + cosf(mapB) * dist*.5f, self.dotOne.position.y + sinf(mapB) * dist*.5f);

    // Start drawing path
    [_path moveToPoint:ptA];
    [_path addQuadCurveToPoint:ptC controlPoint:bzA];
    [_path addLineToPoint:ptD];
    [_path addQuadCurveToPoint:ptB controlPoint:bzB];

    [self drawBitmap];

    [self setNeedsDisplay];
}

- (void)drawBitmap
{
    _image = nil;
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
    [_image drawAtPoint:CGPointZero];
    [[UIColor whiteColor] setFill];
    [_path fill];

    _image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [_path removeAllPoints];
}

@end

1 个答案:

答案 0 :(得分:0)

您的视频看起来好像是在模拟器上录制的,而不是设备上录制的。模拟器测试对于性能,尤其是图形性能毫无意义,因为模拟器无法使用专用GPU。

由于这个原因,模拟器上的GPU操作通常比设备差很多。

所以,我可以提供以下建议:

  1. 在设备上运行性能测试,理想情况下是您定位的最慢的测试。
  2. 使用核心动画工具,它将为您提供帧速率和时间分析,并将突出显示代码的昂贵区域。
  3. 调查设备上仪器提供的各种图形调试选项 - 屏幕外渲染,重绘等等
  4. 如果您喜欢iCarousel,那么该项目的作者Nick Lockwood就iOS的核心动画有一本很好的书,其中包含一章关于性能调优的内容。