2013-03-10 14 views
14

Sto provando ad animare un UIBezierPath e ho installato un CAShapeLayer per provare a farlo. Sfortunatamente l'animazione non funziona e non sono sicuro che nessuno dei livelli abbia alcun effetto (dato che il codice sta facendo la stessa cosa che faceva prima che avessi i layer).Come rendere animato il mio UIBezierPath con CAShapeLayer?

Questo è il codice reale: piacerebbe qualsiasi aiuto. Draw2D è un'implementazione di UIView incorporata in un UIViewController. Tutto il disegno sta accadendo all'interno della classe Draw2D. La chiamata a [_helper createDrawing ...] popola semplicemente la variabile _uipath con i punti.

Draw2D.h definisce le seguenti proprietà:

#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; 

Di seguito è riportato l'attuazione effettiva:

// 
// 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 

So che probabilmente c'è una ragione evidente per cui il percorso non sta animando come è essere disegno (al contrario di essere mostrato immediatamente che è ciò che accade ora) ma ho continuato a fissare la cosa per così tanto tempo che non riesco a vederlo.

Inoltre, se qualcuno può consigliare un primer di base su CAShapeLayers e animazioni in generale lo apprezzerei. Non ho mai visto niente di buono.

grazie in anticipo.

+0

Animazione, io consiglierei la mia spiegazione: http://www.apeth.com/iOSBook/ch17.html. Spiega cose che non ho visto spiegate da nessun'altra parte. – matt

+0

grazie!Verificherò – marina

risposta

40

Sembra che si sta cercando di animare all'interno drawRect (indirettamente, almeno). Non ha molto senso. Non animare entro drawRect. Lo drawRect viene utilizzato per disegnare un singolo fotogramma. Alcuni animazione è fatto con timer o CADisplayLink che chiama ripetutamente setNeedsDisplay (che causerà iOS per chiamare il drawRect) durante il quale si potrebbe disegnare il singolo fotogramma che mostra l'avanzamento dell'animazione in quel punto. Ma semplicemente non hai drawRect avviare alcuna animazione da solo.

Tuttavia, dal momento che si utilizza Core Animation CAShapeLayer e CABasicAnimation, non è necessario un drawRect personalizzato. Quartz's Core Animation si prende cura di tutto per te. Ad esempio, ecco il mio codice per animare il disegno di un 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 

Poi, quando voglio iniziare a disegnare l'animazione, mi basta chiamare il mio metodo startAnimation. Probabilmente non ho nemmeno bisogno di una sottoclasse UIView per qualcosa di così semplice, dal momento che in realtà non sto cambiando alcun comportamento di UIView. Ci sono sicuramente momenti in cui sottoclassi UIView con un'implementazione personalizzata drawRect, ma qui non è necessario.

Avete chiesto alcuni riferimenti:

+0

Grazie. La mia app che prende come input il tipo di disegno che verrà realizzato nella scena 1 (linea, bezier, frattali, ecc.) E nella scena 2 genererà detto disegno. Il controller della vista Scene2 ha 2 pulsanti in alto - uno per l'aggiornamento - un nuovo disegno dello stesso tipo verrà generato nella pagina - o un pulsante indietro - per tornare alla scena 1 e inserire nuovi parametri. Ha anche il UIView che contiene i disegni reali in fondo - il codice era questo era incluso nel post originale. Conoscendo tutto questo, consiglieresti comunque di andare con una vista anziché con un UIView? – marina

+0

@marina La mia classe 'View' è in realtà una sottoclasse' UIView', quindi non capisco la tua domanda "Vedi come opposta a' UIView' ". Il mio punto principale era che non hai bisogno di fare un codice 'drawRect' personalizzato quando usi il' CAShapeLayer' di Core Animation'. Puoi animare usando, 'drawRect' con timer o' CADisplayLink' nella sottoclasse 'UIView' (che è un po 'complicata), o usando Core Animation. Quando uso 'CAShapeLayer', lo faccio nella sottoclasse' UIViewController' (quindi non è confuso con l'alternativa 'drawRect'), ma questa è una preferenza personale, in quanto funziona in entrambe le sottoclassi' UIView' e 'UIViewController'. – Rob

+0

In breve, non c'è assolutamente nulla di sbagliato nel fare il codice 'CAShapeLayer' nella sottoclasse' UIView' (come illustra il mio esempio). Il mio punto principale è che non è necessario il codice 'drawRect' personalizzato quando si usa' CAShapeLayer'. – Rob

Problemi correlati