2013-04-14 9 views
5

Sto provando ad animare i limiti di una UIView personalizzata mantenendo anche il suo livello delle stesse dimensioni della sua vista genitore. Per fare ciò, sto cercando di animare i limiti degli strati accanto alla sua vista genitore. Ho bisogno che lo strato chiami drawLayer: withContext AS sta animando, quindi il mio disegno personalizzato cambierà la dimensione correttamente insieme ai limiti.Impossibile ottenere un CALayer per aggiornare il suo drawLayer: DURANTE un'animazione di limiti

drawLayer viene chiamato correttamente e viene disegnato correttamente prima di avviare l'animazione. Ma non riesco a far sì che il layer chiami il suo metodo drawLayer su OGNI passo dell'animazione dei limiti. Invece, lo chiama solo una volta, saltando immediatamente verso i "confini finali" nel fotogramma finale dell'animazione.

// self.bg is a property pointing to my custom UIView 
self.bg.layer.needsDisplayOnBoundsChange = YES; 
self.bg.layer.mask.needsDisplayOnBoundsChange = YES; 

[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{ 

    [CATransaction begin]; 
    self.bg.layer.bounds = bounds; 
    self.bg.layer.mask.bounds = bounds; 
    [CATransaction commit]; 

    self.bg.bounds = bounds; 
} completion:nil]; 

Perché i limiti non riportano un cambiamento come l'animazione (non solo il fotogramma finale)? Che cosa sto facendo di sbagliato?

risposta

3

Questo potrebbe o non potrebbe aiutare ...

Molte persone non sono consapevoli che Core Animation ha una caratteristica supercool che consente di definire le proprie proprietà dei layer in modo tale che essi possono essere animati. Un esempio che uso è di assegnare a una sottoclasse CALayer una proprietà thickness. Quando ho animarla con Core Animation ...

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"thickness"]; 
ba.toValue = @10.0f; 
ba.autoreverses = YES; 
[lay addAnimation:ba forKey:nil]; 

... l'effetto è che drawInContext: o drawLayer:... è chiamato ripetutamente per tutta l'animazione, che mi permette di cambiare più volte il modo in cui lo strato è disegnato secondo la sua correntethickness valore di proprietà in ogni momento (un valore intermedio nel corso dell'animazione).

Mi sembra che quello potrebbe essere il tipo di cosa che stai cercando..Se è così, è possibile trovare un esempio scaricabile lavorare qui:

https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch17p498customAnimatableProperty

discussione (dal mio libro) qui:

http://www.apeth.com/iOSBook/ch17.html#_making_a_property_animatable

+0

'viene chiamato ripetutamente durante l'animazione' Non automaticamente, non lo è. È una buona soluzione, ma l'inconveniente è che è necessario imporre 'needsDisplayForKey:' e per farlo è necessario sottoclassi CALayer. E dal momento che ci sono un certo numero di sottoclassi CALayer che potresti aver bisogno di usare (come CAGradientLayer, ecc.) Può essere un po 'un problema nel sedere. – zakdances

+0

Poiché presentationLayer restituisce solo una copia statica, la risposta di I matt è l'unica cosa che funziona per ora. Credo che dovrò aggiungere speciali proprietà personalizzate per larghezza e altezza e animarle accanto ai limiti. Suona corretto? – zakdances

+0

Non posso essere * certo * funzionerà nel modo desiderato, ma questa è certamente la direzione in cui stavo pensando che dovresti entrare quando ho fatto questo suggerimento. – matt

1

Questo perché il livello sul quale si disegna non è lo stesso livello di quello visualizzato sullo schermo.

Quando si anima una proprietà di livello, verrà immediatamente impostato sul valore finale nel livello del modello (come notato) e l'animazione effettiva viene eseguita nel livello di presentazione.

È possibile accedere al livello di presentazione e vedere i valori attuali delle proprietà animate:

CALayer *presentationLayer = (CALayer *)[self.bg.layer presentationLayer]; 
... 

Poiché non hai fornito il tuo metodo drawLayer:withContext, non è chiaro che cosa si vuole disegnare durante l'animazione, ma se vuoi animare le proprietà personalizzate, here is a good tutorial per farlo.

+0

Wow non sapeva di presentationLayer. Grazie per questo. Nel mio drawLayer, ho solo un UIBezierPath con alcuni metodi addArcWithCenter. Potrei aggiungerlo qui, ma onestamente non penso che abbia alcun effetto sul fatto che drawLayer venga chiamato o meno. – zakdances

+0

Posso usare presentationLayer per attivare drawLayer :? Secondo i documenti, i metodi presentationLayer restituiscono una copia del presentationLayer, quindi non sono sicuro se sia possibile osservare la proprietà bounds ... – zakdances

+0

Sì, restituisce una copia del livello di presentazione, è solo per la lettura. Non penso che il metodo drawLayer verrà chiamato durante l'animazione, a meno che non si usi qualcosa come la risposta matts. Ecco perché ho chiesto cosa avevi in ​​quel metodo, per vedere se fosse animabile. – Hjalmar

1

In primo luogo, il livello di una vista di livello (o di hosting) viene sempre ridimensionato per adattarsi ai limiti della sua vista padre. Se si imposta la vista come delegato dei layer, la vista riceverà drawLayer: inContext: in ogni frame. Ovviamente devi assicurarti che Se il tuo livello ha needsDisplayOnBoundsChange == YES.

Ecco un esempio (sul Mac) del ridimensionamento di una finestra, che quindi modifica il percorso del livello sottostante.

// My Nib contains one view and one button. 
// The view has a MPView class and the button action is resizeWindow: 

@interface MPView() { 
    CAShapeLayer  *_hostLayer; 
    CALayer   *_outerLayer; 
    CAShapeLayer  *_innerLayer; 
} 
@end 

@implementation MPView 

- (void)awakeFromNib 
{ 
    [CATransaction begin]; 
    [CATransaction setDisableActions:YES]; 

    _hostLayer = [CAShapeLayer layer]; 
    _hostLayer.backgroundColor = [NSColor blackColor].CGColor; 
    _hostLayer.borderColor = [NSColor redColor].CGColor; 
    _hostLayer.borderWidth = 2; 
    _hostLayer.needsDisplayOnBoundsChange = YES; 
    _hostLayer.delegate = self; 
    _hostLayer.lineWidth = 4; 
    _hostLayer.strokeColor = [NSColor greenColor].CGColor; 
    _hostLayer.needsDisplayOnBoundsChange = YES; 

    self.layer = _hostLayer; 
    self.wantsLayer = YES; 

    [CATransaction commit]; 

    [self.window setFrame:CGRectMake(100, 100, 200, 200) display:YES animate:NO]; 
} 

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx 
{ 
    if (layer == _hostLayer) { 

     CGSize size = layer.bounds.size; 
     CGMutablePathRef path = CGPathCreateMutable(); 

     CGPathMoveToPoint(path, NULL, 0, 0); 
     CGPathAddLineToPoint(path, NULL, size.width, size.height); 

     _hostLayer.path = path; 

     CGPathRelease(path); 
    } 
} 

- (IBAction)resizeWindow:(id)sender 
{ 
    [self.window setFrame:CGRectMake(100, 100, 1200, 800) display:YES animate:YES]; 
} 

@end 
+0

Per impostazione predefinita, il livello di viste non è impostato come delegato? O devo farlo nelle viste init: 'self.layer.delegate = self;' – zakdances

+0

Per impostazione predefinita il delegato è nullo quindi è necessario impostarlo da solo. – aLevelOfIndirection

+0

@yourfriendzak Solo il livello predefinito. Credo che in realtà lanci un'eccezione se il suo delegato non è il suo punto di vista. – Sulthan

Problemi correlati