IOS : Animate transformation from a line to a bezier curve

后端 未结 1 2056
鱼传尺愫
鱼传尺愫 2020-12-12 21:37

I would like to animate a straight line curving into a bezier curve (from \"_\" to \"n\"), is there a library somewhere that can help me to do it ?

I know how to dra

相关标签:
1条回答
  • 2020-12-12 21:43

    I might do something with a CADisplayLink. For example, you could do this in your view controller using a CAShapeLayer, e.g.:

    #import "ViewController.h"
    #import <QuartzCore/QuartzCore.h>
    
    @interface ViewController ()
    
    @property (nonatomic) CFTimeInterval firstTimestamp;
    @property (nonatomic, strong) CAShapeLayer *shapeLayer;
    @property (nonatomic, strong) CADisplayLink *displayLink;
    @property (nonatomic) NSUInteger loopCount;
    
    @end
    
    static CGFloat const kSeconds = 5.0;
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        [self addShapeLayer];
        [self startDisplayLink];
    }
    
    - (void)addShapeLayer
    {
        self.shapeLayer = [CAShapeLayer layer];
        self.shapeLayer.path = [[self pathAtInterval:0.0] CGPath];
        self.shapeLayer.fillColor = [[UIColor clearColor] CGColor];
        self.shapeLayer.lineWidth = 3.0;
        self.shapeLayer.strokeColor = [[UIColor redColor] CGColor];
        [self.view.layer addSublayer:self.shapeLayer];
    }
    
    - (void)startDisplayLink
    {
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)stopDisplayLink
    {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    - (void)handleDisplayLink:(CADisplayLink *)displayLink
    {
        if (!self.firstTimestamp)
            self.firstTimestamp = displayLink.timestamp;
    
        self.loopCount++;
    
        NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
    
        self.shapeLayer.path = [[self pathAtInterval:elapsed] CGPath];
    
        if (elapsed >= kSeconds)
        {
            [self stopDisplayLink];
            self.shapeLayer.path = [[self pathAtInterval:0] CGPath];
    
            self.statusLabel.text = [NSString stringWithFormat:@"loopCount = %.1f frames/sec", self.loopCount / kSeconds];
        }
    }
    
    - (UIBezierPath *)pathAtInterval:(NSTimeInterval) interval
    {
        UIBezierPath *path = [UIBezierPath bezierPath];
    
        [path moveToPoint:CGPointMake(0, self.view.bounds.size.height / 2.0)];
    
        CGFloat fractionOfSecond = interval - floor(interval);
    
        CGFloat yOffset = self.view.bounds.size.height * sin(fractionOfSecond * M_PI * 2.0);
    
        [path addCurveToPoint:CGPointMake(self.view.bounds.size.width, self.view.bounds.size.height / 2.0)
                controlPoint1:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 - yOffset)
                controlPoint2:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 + yOffset)];
    
        return path;
    }
    
    @end
    

    animated image


    Alternatively, if you wanted to do it by subclassing UIView, you could do it like so:

    #import "View.h"
    #import <QuartzCore/QuartzCore.h>
    
    @interface View ()
    
    @property (nonatomic, strong) CADisplayLink *displayLink;
    @property (nonatomic) CFTimeInterval firstTimestamp;
    @property (nonatomic) CFTimeInterval displayLinkTimestamp;
    @property (nonatomic) NSUInteger loopCount;
    
    @end
    
    static CGFloat const kSeconds = 5.25;
    
    @implementation View
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self startDisplayLink];
        }
    
        return self;
    }
    
    - (void)startDisplayLink
    {
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
        [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)stopDisplayLink
    {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    - (void)handleDisplayLink:(CADisplayLink *)displayLink
    {
        if (!self.firstTimestamp)
            self.firstTimestamp = displayLink.timestamp;
    
        self.displayLinkTimestamp = displayLink.timestamp;
    
        self.loopCount++;
    
        [self setNeedsDisplayInRect:self.bounds];
    
        NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
    
        if (elapsed >= kSeconds)
        {
            [self stopDisplayLink];
            self.displayLinkTimestamp = self.firstTimestamp + kSeconds;
            [self setNeedsDisplayInRect:self.bounds];
            self.statusLabel.text = [NSString stringWithFormat:@"loopCount = %.1f frames/sec", self.loopCount / kSeconds];
        }
    }
    
    - (UIBezierPath *)pathAtInterval:(NSTimeInterval) interval
    {
        UIBezierPath *path = [UIBezierPath bezierPath];
    
        [path moveToPoint:CGPointMake(0, self.bounds.size.height / 2.0)];
    
        CGFloat fractionOfSecond = interval - floor(interval);
    
        CGFloat yOffset = self.bounds.size.height * sin(fractionOfSecond * M_PI * 2.0);
    
        [path addCurveToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height / 2.0)
                controlPoint1:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0 - yOffset)
                controlPoint2:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0 + yOffset)];
    
        return path;
    }
    
    
    - (void)drawRect:(CGRect)rect
    {
        NSTimeInterval elapsed = (self.displayLinkTimestamp - self.firstTimestamp);
    
        UIBezierPath *path = [self pathAtInterval:elapsed];
    
        [[UIColor redColor] setStroke];
        path.lineWidth = 3.0;
        [path stroke];
    }
    
    @end
    

    I test both the subclassed UIView as well as the view controller, and they both resulted in roughly 60 frames per second.

    0 讨论(0)
提交回复
热议问题