我正在移植一些看起来有点像这样的动画代码:
- (void)drawRect:(CGRect)rect
{
self.angle += 0.1;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);
CGContextSetLineWidth(context, 2);
CGContextSetLineCap(context, kCGLineCapButt);
CGContextAddArc(context,
self.frame.size.height/2, self.frame.size.height/2, //center
self.frame.size.height/2 - 2, //radius
0.0 + self.angle, M_PI_4 + self.angle, //arc start/finish
NO);
CGContextStrokePath(context);
}
问题是drawRect
只在第一次绘制视图时被调用一次,因此弧的位置永远不会更新。
如何实现我想要的效果(弧线缓慢且连续地围绕中心点移动)?我能找到的大多数动画示例都是执行一次性动画(例如淡入淡出),但不是连续动画。
我也尝试了以下方面:
[arcView animateWithDuration:10.0f
delay:1.0f
options: UIViewAnimationOptionRepeat | UIViewAnimationOptionBeginFromCurrentState
animations: ^(void){
_arcView.transform = CGAffineTransformMakeRotation(self.angle++);
}
completion:NULL];
显示视图时,这似乎也没有做任何事情。
更多关于我的目标:我有一个我想要能够设置某些状态的视图,例如arcView.state = STATE_READY
,并为此改变它的动画方式。这是从一个Android项目移植而来的,就像在视图上为draw
方法添加逻辑一样简单,并且首选类似的东西。
答案 0 :(得分:2)
有几点意见:
首先,drawRect
可能不应该增加角度。 drawRect
的目的是绘制一个帧,它不应该改变视图的状态。
如果您希望UIView
子类重复更新angle
并重绘自己,您可以设置一个定时器,或者更好的是CADisplayLink
,它将被调用定期更新angle
,然后调用[self setNeedsDisplay]
更新视图:
#import "MyView.h"
@interface MyView ()
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic) CFTimeInterval startTime;
@property (nonatomic) CGFloat revolutionsPerSecond;
@property (nonatomic) CGFloat angle;
@end
@implementation MyView
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.angle = 0.0;
self.revolutionsPerSecond = 0.25; // i.e. go around once per every 4 seconds
[self startDisplayLink];
}
return self;
}
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
self.startTime = CACurrentMediaTime();
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
CFTimeInterval elapsed = CACurrentMediaTime() - self.startTime;
double revolutions;
double percent = modf(elapsed * self.revolutionsPerSecond, &revolutions);
self.angle = M_PI * 2.0 * percent;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);
CGContextSetLineWidth(context, 2);
CGContextSetLineCap(context, kCGLineCapButt);
CGContextAddArc(context,
self.frame.size.width/2, self.frame.size.height/2, //center
MIN(self.frame.size.width, self.frame.size.height) / 2.0 - 2.0, //radius
0.0 + self.angle, M_PI_4 + self.angle, //arc start/finish
NO);
CGContextStrokePath(context);
}
@end
更简单的方法是更新视图的transform
属性,以便旋转它。在iOS 7及更高版本中,您使用弧添加视图,然后使用以下内容旋转它:
[UIView animateKeyframesWithDuration:4.0 delay:0.0 options:UIViewKeyframeAnimationOptionRepeat | UIViewAnimationOptionCurveLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0.00 relativeDuration:0.25 animations:^{
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
}];
[UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.25 animations:^{
self.view.transform = CGAffineTransformMakeRotation(M_PI);
}];
[UIView addKeyframeWithRelativeStartTime:0.50 relativeDuration:0.25 animations:^{
self.view.transform = CGAffineTransformMakeRotation(M_PI_2 * 3.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
self.view.transform = CGAffineTransformMakeRotation(M_PI * 2.0);
}];
} completion:nil];
注意,我已将动画分为四个步骤,因为如果从0旋转到M_PI * 2.0,它将不会设置动画,因为它知道结束点与起点相同。因此,通过将其分解为四个步骤,它将连续执行四个动画中的每一个。如果在iOS的早期版本中进行操作,则执行与animateWithDuration
类似的操作,但要重复此操作,则需要完成块来调用另一个旋转。类似于:https://stackoverflow.com/a/19408039/1271826
最后,如果您想要动画,例如,仅仅为弧的末端(即为弧的绘制设置动画),您可以将CABasicAnimation
与CAShapeLayer
一起使用:
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0) radius:MIN(self.view.bounds.size.width, self.view.bounds.size.height) / 2.0 - 2.0 startAngle:0 endAngle:M_PI_4 / 2.0 clockwise:YES];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.frame = self.view.bounds;
layer.path = path.CGPath;
layer.lineWidth = 2.0;
layer.strokeColor = [UIColor redColor].CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:layer];
CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animateStrokeEnd.duration = 10.0;
animateStrokeEnd.fromValue = @0.0;
animateStrokeEnd.toValue = @1.0;
animateStrokeEnd.repeatCount = HUGE_VALF;
[layer addAnimation:animateStrokeEnd forKey:@"strokeEndAnimation"];
我知道这可能不是你想要的,但我提到它只是为了你可以把它添加到你的动画“工具带”。
答案 1 :(得分:1)
我能找到的大多数动画示例都是执行一次性的 动画(如淡入),但不是连续的。
如果您使用UIView的+animateWithDuration:delay:options:animations:completion:
之类的方法来制作动画,则可以在UIViewAnimationOptionRepeat
参数中指定options
以使动画无限重复。
从根本上说,对大多数动画使用-drawRect:
是错误的方法。正如您所发现的那样,-drawRect:
仅在图形系统真正需要重绘视图时调用,这是一个相对昂贵的过程。尽可能地,你应该使用Core Animation来制作你的动画,特别是对于这样的东西,其中视图本身不需要重绘,但它的像素需要以某种方式进行转换。
请注意,+animateWithDuration:delay:options:animations:completion:
是一种类方法,因此您应将其发送到UIView
本身(或UIView
的某个子类),而不是UIView
的实例。有一个例子here。此外,该特定方法可能或可能不是您正在做的最佳选择 - 我只是将其调出来,因为它是一个使用带有视图的动画的简单示例,它允许您指定重复选项。
我不确定你的动画有什么问题(除了上面描述的错误的接收器之外),但是我会避免使用++
运算符来修改角度。角度以弧度指定,因此增加1可能不是您想要的。相反,尝试将π/ 2添加到当前旋转,如下所示:
_arcView.transform = CAAffineTransformRotate(_arcView.transform, M_PI_2);
答案 2 :(得分:-1)
所以这就是我最终的结果。我花了很长时间来完成我需要"translation.rotation"
而不只是"rotation"
...
@implementation arcView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
self.state = 0;
if (self) {
self.layer.contents = (__bridge id)([[self generateArc:[UIColor redColor].CGColor]CGImage]);
}
return self;
}
-(UIImage*)generateArc:(CGColorRef)color{
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, color);
CGContextSetLineWidth(context, 2);
CGContextSetLineCap(context, kCGLineCapButt);
CGContextAddArc(context,
self.frame.size.height/2, self.frame.size.height/2,
self.frame.size.height/2 - 2,0.0,M_PI_4,NO);
CGContextStrokePath(context);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
-(void)spin{
[self.layer removeAnimationForKey:@"rotation"];
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
NSNumber *current =[[self.layer presentationLayer] valueForKeyPath:@"transform.rotation"];
rotate.fromValue = current;
rotate.toValue = [NSNumber numberWithFloat:current.floatValue + M_PI * (self.state*2 - 1)];
rotate.duration = 3.0;
rotate.repeatCount = INT_MAX;
rotate.fillMode = kCAFillModeForwards;
rotate.removedOnCompletion = NO;
[self.layer addAnimation:rotate forKey:@"rotation"];
}