ios 如何使用 CAShapeLayer 制作我的 UIBezierPath 动画?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15318918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make my UIBezierPath animated with CAShapeLayer?
提问by marina
I'm trying to animate a UIBezierPath and I've installed a CAShapeLayer to try to do it. Unfortunately the animation isn't working and I'm not sure any of the layers are having any affect (as the code is doing the same thing it was doing before I had the layers).
我正在尝试为 UIBezierPath 设置动画,并且我已经安装了 CAShapeLayer 来尝试执行此操作。不幸的是动画不起作用,我不确定任何图层是否有任何影响(因为代码正在做与在我拥有图层之前所做的相同的事情)。
Here is the actual code - would love any help. Draw2D is an implementation of UIView that is embedded in a UIViewController. All the drawing is happening inside the Draw2D class. The call to [_helper createDrawing... ] simply populates the _uipath variable with points.
这是实际的代码 - 希望得到任何帮助。Draw2D 是嵌入在 UIViewController 中的 UIView 的实现。所有绘图都发生在 Draw2D 类中。对 [_helper createDrawing... ] 的调用只是用点填充 _uipath 变量。
Draw2D.h defines the following properties:
Draw2D.h 定义了以下属性:
#define defaultPointCount ((int) 25)
@property Draw2DHelper *helper;
@property drawingTypes drawingType;
@property int graphPoints;
@property UIBezierPath *uipath;
@property CALayer *animationLayer;
@property CAShapeLayer *pathLayer;
- (void)refreshRect:(CGRect)rect;
below is the actual implementation :
下面是实际的实现:
//
// Draw2D.m
// Draw2D
//
// Created by Marina on 2/19/13.
// Copyright (c) 2013 Marina. All rights reserved.
//
#import "Draw2D.h"
#import"Draw2DHelper.h"
#include <stdlib.h>
#import "Foundation/Foundation.h"
#import <QuartzCore/QuartzCore.h>
int MAX_WIDTH;
int MAX_HEIGHT;
@implementation Draw2D
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
if (self.pathLayer != nil) {
[self.pathLayer removeFromSuperlayer];
self.pathLayer = nil;
}
self.animationLayer = [CALayer layer];
self.animationLayer.frame = self.bounds;
[self.layer addSublayer:self.animationLayer];
CAShapeLayer *l_pathLayer = [CAShapeLayer layer];
l_pathLayer.frame = self.frame;
l_pathLayer.bounds = self.bounds;
l_pathLayer.geometryFlipped = YES;
l_pathLayer.path = _uipath.CGPath;
l_pathLayer.strokeColor = [[UIColor grayColor] CGColor];
l_pathLayer.fillColor = nil;
l_pathLayer.lineWidth = 1.5f;
l_pathLayer.lineJoin = kCALineJoinBevel;
[self.animationLayer addSublayer:l_pathLayer];
self.pathLayer = l_pathLayer;
[self.layer addSublayer:l_pathLayer];
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect :(int) points :(drawingTypes) type //:(Boolean) initial
{
//CGRect bounds = [[UIScreen mainScreen] bounds];
CGRect appframe= [[UIScreen mainScreen] applicationFrame];
CGContextRef context = UIGraphicsGetCurrentContext();
_helper = [[Draw2DHelper alloc ] initWithBounds :appframe.size.width :appframe.size.height :type];
CGPoint startPoint = [_helper generatePoint] ;
[_uipath moveToPoint:startPoint];
[_uipath setLineWidth: 1.5];
CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGPoint center = CGPointMake(self.center.y, self.center.x) ;
[_helper createDrawing :type :_uipath :( (points>0) ? points : defaultPointCount) :center];
self.pathLayer.path = (__bridge CGPathRef)(_uipath);
[_uipath stroke];
[self startAnimation];
}
- (void) startAnimation {
[self.pathLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
}
- (void)drawRect:(CGRect)rect {
if (_uipath == NULL)
_uipath = [[UIBezierPath alloc] init];
else
[_uipath removeAllPoints];
[self drawRect:rect :self.graphPoints :self.drawingType ];
}
- (void)refreshRect:(CGRect)rect {
[self setNeedsDisplay];
}
@end
I know there's probably an obvious reason for why the path isn't animating as it's being drawing (as opposed to being shown immediately which is what happens now) but I've been staring at the thing for so long that I just don't see it.
我知道为什么路径在绘制时没有动画可能有一个明显的原因(而不是立即显示,这就是现在发生的情况)但我一直盯着这个东西看太久了,我只是不看见。
Also, if anyone can recommend a basic primer on CAShapeLayers and animation in general I would appreciate it. Haven't come up across any that are good enough.
另外,如果有人可以推荐有关 CAShapeLayers 和动画的基本入门,我将不胜感激。还没有遇到任何足够好的。
thanks in advance.
提前致谢。
回答by Rob
It looks like you're trying to animate within drawRect
(indirectly, at least). That doesn't quite make sense. You don't animate within drawRect
. The drawRect
is used for drawing a single frame. Some animation is done with timers or CADisplayLink
that repeatedly calls setNeedsDisplay
(which will cause iOS to call your drawRect
) during which you might draw the single frame that shows the progress of the animation at that point. But you simply don't have drawRect
initiating any animation on its own.
看起来您正在尝试在其中设置动画drawRect
(至少是间接的)。这不太合理。你不在drawRect
. 该drawRect
被用于绘制单个帧。一些动画是用计时器完成的,或者CADisplayLink
重复调用setNeedsDisplay
(这将导致 iOS 调用您的drawRect
),在此期间您可能会绘制显示该点动画进度的单帧。但是您根本不需要自行drawRect
启动任何动画。
But, since you're using Core Animation's CAShapeLayer
and CABasicAnimation
, you don't need a custom drawRect
at all. Quartz's Core Animation just takes care of everything for you. For example, here is my code for animating the drawing of a UIBezierPath
:
但是,由于您使用的是 Core Animation 的CAShapeLayer
和CABasicAnimation
,您根本不需要自定义drawRect
。Quartz 的核心动画会为您处理一切。例如,这是我用于绘制 a 动画的代码UIBezierPath
:
#import <QuartzCore/QuartzCore.h>
@interface View ()
@property (nonatomic, weak) CAShapeLayer *pathLayer;
@end
@implementation View
/*
// I'm not doing anything here, so I can comment this out
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
*/
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
// It doesn't matter what my path is. I could make it anything I wanted.
- (UIBezierPath *)samplePath
{
UIBezierPath *path = [UIBezierPath bezierPath];
// build the path here
return path;
}
- (void)startAnimation
{
if (self.pathLayer == nil)
{
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [[self samplePath] CGPath];
shapeLayer.strokeColor = [[UIColor grayColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 1.5f;
shapeLayer.lineJoin = kCALineJoinBevel;
[self.layer addSublayer:shapeLayer];
self.pathLayer = shapeLayer;
}
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = @(0.0f);
pathAnimation.toValue = @(1.0f);
[self.pathLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
}
@end
Then, when I want to start drawing the animation, I just call my startAnimation
method. I probably don't even need a UIView
subclass at all for something as simple as this, since I'm not actually changing any UIView
behavior. There are definitely times that you subclass UIView
with a custom drawRect
implementation, but it's not needed here.
然后,当我想开始绘制动画时,我只需调用我的startAnimation
方法。UIView
对于像这样简单的事情,我可能根本不需要子类,因为我实际上并没有改变任何UIView
行为。肯定有时您会UIView
使用自定义drawRect
实现进行子类化,但这里不需要。
You asked for some references:
您要求提供一些参考资料:
- I would probably start with a review of Apple's Core Animation Programming Guide, if you haven't seen that.
- For me, it all fell into place when I went through Mike Nachbaur's Core Animation Tutorial Part 4, actually reproducing his demo from scratch. Clearly, you can check out parts 1 through 3, too.
- 如果您还没有看过,我可能会先回顾一下 Apple 的Core Animation Programming Guide。
- 对我来说,当我阅读Mike Nachbaur 的核心动画教程第 4 部分,实际上是从头开始复制他的演示时,这一切都已就位。显然,您也可以查看第 1 部分到第 3 部分。