Подтвердить что ты не робот

IOS: преобразование анимации от линии к кривой безье

Я хотел бы оживить прямую линию, изогнутую в кривую Безье (от "_" до "n" ), есть ли где-нибудь библиотека, которая может мне помочь?

Я знаю, как рисовать кривую Безье с UIBezierPath, я мог бы быстро перерисовывать и постепенно преобразовывать, но если что-то уже делает это, было бы круто: -)

4b9b3361

Ответ 1

Я мог бы что-то сделать с CADisplayLink. Например, вы можете сделать это в своем контроллере просмотра с помощью CAShapeLayer, например:

#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


В качестве альтернативы, если вы хотите сделать это путем подкласса UIView, вы можете сделать это так:

#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

Я тестирую подклассы UIView, а также контроллер представления, и они оба приводят к примерно 60 кадрам в секунду.