2013-12-08 32 views
0

我需要一些关于在iOS中绘制的建议,更具体地说是关于性能绘图。我读了很多关于iOS7绘图的文章,但是我没有成功地获得正确的结果。动态制图在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 

回答

0

您的视频看起来好像他们被记录在模拟器,而不是设备。模拟器测试对性能没有意义,特别是图形性能,因为专用GPU不适用于模拟器。由于这个原因,GPU模拟器上的操作通常比模拟器上的设备要糟糕得多。

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

  1. 运行性能测试的设备上,最好你的目标最慢的一个。
  2. 使用核心动画工具,它会给你帧速率和时间分析,并将突出代码的昂贵部分。
  3. 调查通过设备上的仪器的各种图形调试可用的选项,以及 - 离屏渲染,重绘等

如果你喜欢iCarousel,则此项目,尼克·洛克伍德的作者,有一个很好的书关于iOS的核心动画,其中包含关于性能调整的一章。