我在drawrect方法中有以下代码。我想用动画画画。
-(void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathMoveToPoint(pathRef, NULL, a.x, a.y);
CGPathAddLineToPoint(pathRef, NULL, b.x, b.y);
CGPathAddLineToPoint(pathRef, NULL, c.x, c.y);
CGPathAddLineToPoint(pathRef, NULL, d.x, d.y);
CGPathCloseSubpath(pathRef);
CGContextAddPath(context, pathRef);
CGContextStrokePath(context);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextAddPath(context, pathRef);
CGContextFillPath(context);
//我在这里展示动画,但它不起作用。有没有办法做到这一点
-(void)showAnimation{
[UIView beginAnimations:@"movement" context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationWillStartSelector:@selector(didStart:context:)];
[UIView setAnimationDidStopSelector:@selector(didStop:finished:context:)];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:3.0f];
[UIView setAnimationRepeatAutoreverses:YES];
a=CGPointMake(10, 100);
b=CGPointMake(100, 100);
c=CGPointMake(100, 30);
d=CGPointMake(20, 30);
[UIView commitAnimations];
}
}
答案 0 :(得分:2)
也许自定义图层子类可能是您要搜索的内容。通过这样做,您可以添加自己的自定义动画属性,例如表示绘图进度的进度变量,并在drawInContext
方法中用于确定要绘制的内容。
现在,如果您通过CAAnimation为该属性设置动画,那么所有发生的事情都是CoreAnimation系统制作图层的副本,将属性稍微更改为最终值,调用drawInContext
,再次更改属性一点点,再次呼叫drawInContext
,依此类推。这就是动画的工作原理。
Layer子类应如下所示:
#import <QuartzCore/QuartzCore.h>
@interface AnimatedRect : CALayer
@property (nonatomic) float progress;
@end
@implementation AnimatedRect
//the animated property must be dynamic
@dynamic progress;
//initWithLayer is called when the animation starts, for making the copy
- (id)initWithLayer:(id)layer {
self = [super initWithLayer:layer];
if (self) {
self.progress = layer.progress;
}
return self;
}
//drawInContext is called to perform drawing
-(void)drawInContext:(CGContextRef)ctx
{
//your drawing code for the rect up to the percentage in progress
//some math is required here, to determine which sides are drawn
//and where the endpoint is
}
在drawInContext方法中,您必须计算最后绘制线的结束位置。例如,如果你有一个正方形,12.5%是半个第一行,50%是前两个。下面的矩形是87.5%,从右上角开始
作为补充:如果要隐式动画,则需要实现其他方法-(id<CAAction>)actionForKey:(NSString *)event
,在其中创建CAAnimation并将其返回。
关于该主题的一个很好的消息来源是this tutorial
答案 1 :(得分:0)
这不是解决这个问题的正确方法。实施drawRect:
应该是最后的选择,特别是如果你所做的只是画一个矩形。它不能很好地与动画配合使用,因为你要强制重绘每一帧,这对性能非常不利。
您最好不要执行以下操作之一:
CAShapeLayer
并为其路径设置动画。 答案 2 :(得分:0)
来自@TAKeanice的有用answer我已实施similar超时行为。
XYZTimeoutView
#import <UIKit/UIKit.h>
/**
* A view to draw timeout line.
*/
@interface XYZTimeoutView : UIImageView
/**
* Current progress in percent.
*/
@property(nonatomic) IBInspectable CGFloat progress;
/**
* Padding between outer view edge and timeout line.
*/
@property(nonatomic) IBInspectable CGFloat padding;
/**
* A width of timeout line.
*/
@property(nonatomic) IBInspectable CGFloat strokeWidth;
/**
* A duration of timeout animation in seconds.
*/
@property(nonatomic) IBInspectable CGFloat durationOfTimeoutAnimation;
/**
* A color of timeout line.
*/
@property(nonatomic) IBInspectable UIColor *strokeColor;
@end
#import "XYZTimeoutView.h"
#import "XYZTimeoutLayer.h"
@interface XYZTimeoutView ()
@property(nonatomic) XYZTimeoutLayer *timeoutLayer;
@end
@implementation XYZTimeoutView
#pragma mark - Creation and initialization
- (instancetype)init {
self = [super init];
if (self) {
[self initialization];
[self update];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initialization];
[self update];
}
return self;
}
- (void)initialization {
[self setTimeoutLayer:[XYZTimeoutLayer layer]];
[[self layer] addSublayer:[self timeoutLayer]];
}
- (void)update {
[[self timeoutLayer] setPadding:[self padding]];
[[self timeoutLayer] setProgress:[self progress]];
[[self timeoutLayer] setStrokeWidth:[self strokeWidth]];
[[self timeoutLayer] setStrokeColor:[self strokeColor]];
[[self timeoutLayer] setDurationOfTimeoutAnimation:[self durationOfTimeoutAnimation]];
}
#pragma mark - Setter methods
- (void)setProgress:(CGFloat)progress {
_progress = MAX(0.0f, MIN(progress, 100.0f));
[[self timeoutLayer] setFrame:[self bounds]];
[[self timeoutLayer] setProgress:[self progress]];
}
- (void)setPadding:(CGFloat)padding {
_padding = padding;
[[self timeoutLayer] setPadding:[self padding]];
}
- (void)setStrokeWidth:(CGFloat)strokeWidth {
_strokeWidth = strokeWidth;
[[self timeoutLayer] setStrokeWidth:[self strokeWidth]];
}
- (void)setDurationOfTimeoutAnimation:(CGFloat)durationOfTimeoutAnimation {
_durationOfTimeoutAnimation = durationOfTimeoutAnimation;
[[self timeoutLayer]
setDurationOfTimeoutAnimation:[self durationOfTimeoutAnimation]];
}
- (void)setStrokeColor:(UIColor *)strokeColor {
_strokeColor = strokeColor;
[[self timeoutLayer] setStrokeColor:[self strokeColor]];
}
@end
XYZTimeoutLayer
@import QuartzCore;
@import UIKit;
/**
* A layer used to animate timeout line.
*/
@interface XYZTimeoutLayer : CALayer
/**
* Current progress in percent.
*/
@property(nonatomic) CGFloat progress;
/**
* Padding between outer view edge and timeout line.
*/
@property(nonatomic) CGFloat padding;
/**
* A width of timeout line.
*/
@property(nonatomic) CGFloat strokeWidth;
/**
* A duration of timeout animation in seconds.
*/
@property(nonatomic) CGFloat durationOfTimeoutAnimation;
/**
* A color of timeout line.
*/
@property(nonatomic) UIColor *strokeColor;
@end
#import "XYZTimeoutLayer.h"
#import "XYZTimeoutLocation.h"
@implementation XYZTimeoutLayer
@dynamic progress;
#pragma mark - Layer creation and initialization
- (instancetype)initWithLayer:(id)layer {
self = [super initWithLayer:layer];
if (self && [layer isKindOfClass:[XYZTimeoutLayer class]]) {
[self copyFrom:(XYZTimeoutLayer *)layer];
}
return self;
}
#pragma mark - Property methods
+ (BOOL)needsDisplayForKey:(NSString *)key {
if ([NSStringFromSelector(@selector(progress)) isEqualToString:key]) {
return YES;
}
return [super needsDisplayForKey:key];
}
#pragma mark - Animation methods
- (id<CAAction>)actionForKey:(NSString *)event {
if ([NSStringFromSelector(@selector(progress)) isEqualToString:event]) {
return [self createAnimationForKey:event];
}
return [super actionForKey:event];
}
#pragma mark - Draw
- (void)drawInContext:(CGContextRef)ctx {
// Initialization
CGRect rect = [self drawRect];
CGPoint pointTopLeft = CGPointMake(rect.origin.x, rect.origin.y);
CGPoint pointTopRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y);
CGPoint pointBottomLeft = CGPointMake(rect.origin.x, rect.origin.y + rect.size.height);
CGPoint pointBottomRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
XYZTimeoutLocation *location = [XYZTimeoutLocation endLocationForPercent:[self progress] rect:rect];
// Draw initialization
CGContextSetLineWidth(ctx, [self strokeWidth]);
CGContextSetStrokeColorWithColor(ctx, [[self strokeColor] CGColor]);
// Move to start point
CGContextMoveToPoint(ctx, pointTopRight.x, pointTopRight.y - ([self strokeWidth] / 2));
// Set draw path
if(TOP == [location edge]){
CGContextAddLineToPoint(ctx, pointBottomRight.x, pointBottomRight.y);
CGContextAddLineToPoint(ctx, pointBottomLeft.x, pointBottomLeft.y);
CGContextAddLineToPoint(ctx, pointTopLeft.x, pointTopLeft.y);
CGContextAddLineToPoint(ctx, rect.origin.x + [location scope], pointTopRight.y);
} else if(LEFT == [location edge]) {
CGContextAddLineToPoint(ctx, pointBottomRight.x, pointBottomRight.y);
CGContextAddLineToPoint(ctx, pointBottomLeft.x, pointBottomLeft.y);
CGContextAddLineToPoint(ctx, pointTopLeft.x, rect.origin.y + rect.size.height - [location scope]);
} else if(BOTTOM == [location edge]) {
CGContextAddLineToPoint(ctx, pointBottomRight.x, pointBottomRight.y);
CGContextAddLineToPoint(ctx, rect.origin.x + rect.size.width - [location scope], pointBottomLeft.y);
} else if(RIGHT == [location edge]) {
CGContextAddLineToPoint(ctx, pointBottomRight.x, rect.origin.y + [location scope] - ([self strokeWidth] / 2));
}
// Draw
CGContextStrokePath(ctx);
}
#pragma mark - Helper Methods
- (void)copyFrom:(XYZTimeoutLayer *)layer {
[self setPadding:[layer padding]];
[self setProgress:[layer progress]];
[self setStrokeWidth:[layer strokeWidth]];
[self setStrokeColor:[layer strokeColor]];
[self setDurationOfTimeoutAnimation:[layer durationOfTimeoutAnimation]];
}
- (CABasicAnimation *)createAnimationForKey:(NSString *)key {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
[animation setDuration:[self durationOfTimeoutAnimation]];
[animation setFromValue:[[self presentationLayer] valueForKey:key]];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
return animation;
}
- (CGRect)drawRect {
CGRect rect = [self bounds];
CGRect drawRect = CGRectMake([self padding], [self padding],
rect.size.width - (self.padding * 2),
rect.size.height - (self.padding * 2));
return drawRect;
}
@end
XYZTimeoutLocation
@import Foundation;
@import CoreGraphics;
/**
* An enum used to mark end of timeout line.
*/
typedef NS_ENUM(NSUInteger, Edge) {
/**
* The end of timeout line is at top.
*/
TOP = 0,
/**
* The end of timeout line is at left.
*/
LEFT,
/**
* The end of timeout line is at bottom.
*/
BOTTOM,
/**
* The end of timeout line is at right.
*/
RIGHT
};
/**
* A class that holds end of timeout line.
*/
@interface XYZTimeoutLocation : NSObject
/**
* The edge of the view where timeout line ended.
*/
@property(nonatomic) Edge edge;
/**
* The end scope to draw.
*/
@property(nonatomic) CGFloat scope;
/**
* Calculates scope for specified percent value for given rect.
*
* @param percent A percent to calculate scope for it.
* @param rect A rect to calculate scope for it.
*
* @return A scope of rect for specified percent.
*/
+ (CGFloat)scopeForPercent:(CGFloat)percent rect:(CGRect)rect;
/**
* Returns an instance of MVPTimeoutLocation that holds edge of view and scope to draw for specified percent value for given rect.
*
* @param percent A percent to calculate scope for it.
* @param rect A rect to calculate scope for it.
*
* @return An instance of MVPTimeoutLocation that holds edge of view and scope to draw.
*/
+ (XYZTimeoutLocation *)endLocationForPercent:(CGFloat)percent rect:(CGRect)rect;
@end
#import "XYZTimeoutLocation.h"
@implementation XYZTimeoutLocation
+ (XYZTimeoutLocation *)endLocationForPercent:(CGFloat)percent rect:(CGRect)rect {
CGFloat scope = [XYZTimeoutLocation scopeForPercent:percent rect:rect];
XYZTimeoutLocation *location = [[XYZTimeoutLocation alloc] init];
if (scope > rect.size.height) {
scope -= rect.size.height;
if (scope > rect.size.width) {
scope -= rect.size.width;
if (scope > rect.size.height) {
scope -= rect.size.height;
location.edge = TOP;
location.scope = scope;
} else {
location.edge = LEFT;
location.scope = scope;
}
} else {
location.edge = BOTTOM;
location.scope = scope;
}
} else {
location.edge = RIGHT;
location.scope = scope;
}
return location;
}
+ (CGFloat)scopeForPercent:(CGFloat)percent rect:(CGRect)rect {
CGFloat scope = (rect.size.width * 2) + (rect.size.height * 2);
CGFloat scopeForPercent = (scope / 100) * percent;
return scopeForPercent;
}
@end
我对[XYZTimeoutLocation endLocationForPercent]
方法不满意。如果有人更了解如何执行此操作,请更新/编辑。
在下面的UIViewController
插入代码中进行测试:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.timeoutView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.timeoutView setStrokeColor:[UIColor greenColor]];
[self.timeoutView setDurationOfTimeoutAnimation:1];
[self.timeoutView setStrokeWidth:10.0f];
[self.timeoutView setPadding:10.0f];
self.percent = 0;
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
if ((self.percent = (self.percent + 10)) > 100) {
self.percent = 0;
}
[self.timeoutView setProgress:self.percent];
}];
}