2013-03-04 15 views
9

Sto cercando di riprodurre alcune delle transizioni 2D in sample presentation di Impress.js in Objective C. In particolare la rotazione, il pan e il ridimensionamento - in questo momento mi sto concentrando sul ridimensionamento.Come scalare la transizione UILabel senza utilizzare molta memoria?

Ho provato a ridimensionare un UILabel al punto in cui "passa lo schermo", come "visualizza i tuoi Grandi Pensieri" -> "e idee minuscole" nella presentazione di esempio.

questo è quello che ho provato finora:

UILabel *label = [[UILabel alloc] init]; 

label.text = @"Hello World!"; 
label.textColor = [UIColor blackColor]; 
label.font = [UIFont fontWithName:@"Arial" size:18.f]; 
[label sizeToFit]; 
label.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2); 
[self.view addSubview:label]; 

label.contentScaleFactor *= 80; 

[UIView animateWithDuration:5 animations:^{ 
    label.transform = CGAffineTransformScale(label.transform, 80, 80); 
}]; 

Purtroppo questo mangia fino a circa ~ 30-60 MB di RAM, a seconda di ciò che il contentScaleFactor e dimensione del carattere iniziale è. Se non aumento il contentScaleFactor, il testo appare sfocato. Aumentare la dimensione del carattere sembra anche consumare altrettanta memoria.

Ecco come si presenta nel profiler:

enter image description here

E questo è solo un singolo UILabel.

C'è un modo per farlo senza consumare così tanta memoria, senza sacrificare la qualità del testo che viene reso o le transizioni?

+0

In ogni caso, dare un'occhiata a CATransformLayer come metodo per raggruppare i livelli e trasformarli. –

+0

Domanda aggiornata per concentrarsi sul consumo di memoria dopo il ridimensionamento di un UILabel. La domanda precedente era troppo ampia (e chiusa in quanto tale). Mentre la risposta attualmente accettata è fantastica, non affronta il problema nella domanda aggiornata. Mentre vorrei mettere un altro taglie su di esso, ho un piccolo rappresentante per farne cadere altri 500. – Luke

risposta

4

Project download link

non credo che sia necessario lasciare al quarzo per raggiungere questo obiettivo la riproduzione. Tutto ciò che hai descritto e tutto ciò che ho ricavato dall'incombenza di Impress.js sembra essere replicabile attraverso l'applicazione di trasformazioni (principalmente 2D, alcune 3D) a un set di UILabels aggiunto a una vista contenitore che può essere spostata liberamente all'interno vista principale

Per fare questo, il progetto ho creato utilizza una sottoclasse di UILabel dal titolo "ImpressLabel" con una funzione di init in più in cui invece di passare l'etichetta di un fotogramma, si passa una dimensione, un punto centrale e un CGFloat per la rotazione dell'etichetta sull'asse Z. Questa trasformazione viene applicata all'istanza all'istanziazione in modo tale che quando si impostano le etichette verranno visualizzate sullo schermo già nella posizione e nella trasformazione specificate.

Quindi, per quanto riguarda la configurazione del testo, è possibile passare l'etichetta a NSAttributedString anziché a NSString. Questo vi permette di modificare diverse parti della stringa in modo indipendente, così diverse parole del marchio possono essere diverse dimensioni, font, colori, colori di sfondo, ecc Ecco un esempio dei due paragrafi precedenti:

ImpressLabel *label1 = [[ImpressLabel alloc] initWithSize:CGSizeMake(260.0f, 80.0f) andCenterPointInSuperview:CGPointMake(500.0f, 500.0f) andRotationInSuperview:0.0f andEndingScaleFactor:1.3]; 

NSMutableAttributedString *firstLabelAttributes = [[NSMutableAttributedString alloc] initWithString:@"then you should try\nimpress.js*\n* no rhyme intended"]; 

[firstLabelAttributes addAttribute:NSFontAttributeName 
          value:[UIFont systemFontOfSize:label1.font.pointSize - 2] 
          range:NSMakeRange(0, 19)]; 

[firstLabelAttributes addAttribute:NSFontAttributeName 
          value:[UIFont systemFontOfSize:label1.font.pointSize - 8] 
          range:NSMakeRange(firstLabelAttributes.string.length - 19, 19)]; 

[firstLabelAttributes addAttribute:NSFontAttributeName 
          value:[UIFont boldSystemFontOfSize:label1.font.pointSize + 14] 
          range:NSMakeRange(23, 11)]; 


[label1 setNumberOfLines:3]; 
[label1 setAttributedText:firstLabelAttributes]; 
[label1 setTextAlignment:NSTextAlignmentCenter]; 
[containmentView addSubview:label1]; 

Ora, a più del fegato di tutta l'operazione. Come accennato in precedenza, questa sottoclasse aggiunge un tocco di tocco a ciascuna etichetta. Quando viene riconosciuto un tocco, accadono alcune cose. La vista contenente le etichette eseguirà il pan/back/away regolandone la scala. Inoltre, inizierà a ruotare e regolerà il suo punto di ancoraggio nella vista principale in modo che quando l'animazione si arresta, l'etichetta selezionata sarà centrata sullo schermo con l'orientamento corretto. Poi, naturalmente, mentre tutto sta andando sull'alfa dell'etichetta selezionata verrà portato a 1.0f mentre l'alfa del resto verrà abbassato a 0.25f.

- (void)tapForRotationDetected:(UITapGestureRecognizer *)sender { 
    CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; 
    [scale setToValue:[NSNumber numberWithFloat:0.8]]; 
    [scale setAutoreverses:YES]; 
    [scale setDuration:0.3]; 

    //Create animation to adjust the container views anchorpoint. 
    CABasicAnimation *adjustAnchor = [CABasicAnimation animationWithKeyPath:@"anchorPoint"]; 
    [adjustAnchor setFromValue:[NSValue valueWithCGPoint:self.superview.layer.anchorPoint]]; 
    [adjustAnchor setToValue:[NSValue valueWithCGPoint:CGPointMake(self.center.x/self.superview.frame.size.width, self.center.y/self.superview.frame.size.height)]]; 
    [adjustAnchor setRemovedOnCompletion:NO]; 
    [adjustAnchor setFillMode:kCAFillModeForwards]; 

    //Create animation to rotate the container view within its superview. 
    CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; 

    //Create the animation group to apply these transforms 
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; 
    [animationGroup setAnimations:@[adjustAnchor,rotation]]; 
    [animationGroup setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; 
    [animationGroup setDuration:0.6]; 

    //Apply the end results of the animations directly to the container views layer. 

    [self.superview.layer setTransform:CATransform3DRotate(CATransform3DIdentity, DEGREES_TO_RADIANS(-self.rotationInSuperview), 0.0f, 0.0f, 1.0f)]; 
    [self.superview.layer setAnchorPoint:CGPointMake(self.center.x/self.superview.frame.size.width, self.center.y/self.superview.frame.size.height)]; 
    [self.superview.layer addAnimation:animationGroup forKey:@"animationGroup"]; 

    //Animate the alpha property of all ImpressLabels in the container view. 
    [self.superview bringSubviewToFront:self]; 
    [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 
     for (ImpressLabel *label in sender.view.superview.subviews) { 
      if ([label isKindOfClass:[ImpressLabel class]]) { 
       if (label != self) { 
        [label setAlpha:0.25f]; 
       }else{ 
        [label setAlpha:1.0f]; 
       } 

      } 
     } 
    } completion:nil]; 
} 

Ora, per risolvere alcuni dei dubbi elencati nella domanda.

  1. ho fatto il profilo di questo progetto in strumento di assegnazioni Instruments' e consuma solo circa 3,2 MB generale, in modo direi che questo approccio è abbastanza efficace.

  2. L'esempio che ho fornito anima la maggior parte degli oggetti nello spazio 2D, con l'eccezione dell'animazione di ridimensionamento che è un'illusione nella migliore delle ipotesi. Quello che ho fatto qui vuole essere un esempio di come questo possa essere fatto e non è una dimostrazione completa al 100% perché, come ho affermato sopra, l'animazione non è la mia area di competenza. Tuttavia, esaminando i documenti, sembra che la chiave per far ruotare un'etichetta nella terza dimensione e regolarne la panoramica per ruotare tutte le etichette e lasciare l'etichetta selezionata piatta sia l'uso di CATransform3DInvert(). Anche se non ho ancora avuto il tempo di capire che funziona, sembra che potrebbe essere esattamente ciò che è necessario in questo scenario.

  3. Per quanto riguarda il mirroring, non credo che ci saranno problemi con il ridimensionamento corretto di tutto. Osservando Apple Multiple Display Programming Guide, sembra che l'oggetto passato a NSNotificationUIScreenDidConnectNotification sia un oggetto UIScreen. Poiché questo è il caso, è possibile richiedere facilmente questo limite di visualizzazione e regolare di conseguenza i fotogrammi delle etichette e la vista del contenitore.

Nota: In questo esempio, solo 0, 90, 180, e -90 gradi trasforma animati correttamente 100%, a causa coordinate del punto di ancoraggio vengono generati in modo errato. Sembra che la soluzione risieda nello CGPointApplyAffineTransform(<#CGPoint point#>, <#CGAffineTransform t#>), ma ancora una volta non ho avuto tutto il tempo per giocarci come avrei voluto. Ad ogni modo, questo dovrebbe essere abbastanza per iniziare con la tua riproduzione.

Questo ha sicuramente suscitato il mio interesse e quando avrò la possibilità di lavorare di nuovo su questo, aggiornerò volentieri questo post con qualsiasi nuova informazione. Spero che questo ti aiuti!

+0

Fantastico! "Ancora una cosa ... hai notato che è in 3D" non è fondamentale. La rotazione e il panning sono le cose più grandi. Quando c'è una rotazione e una padella, la padella sembra sussultare all'ultimo secondo. Il ridimensionamento 2D sarebbe bello. Dovresti mettere il progetto su Github invece di Dropbox in modo che sia sempre disponibile sulla risposta. – Luke

+0

@Luke Purtroppo non sono stato in grado di farlo funzionare al 100%. Il salto si verifica solo quando si tenta di animare su/da etichette che non sono ruotate ad angolo retto. Ciò è dovuto al fatto che le coordinate del punto di ancoraggio dei contenitori non sono state impostate correttamente dalla trasformazione. Come ho affermato nella risposta 'CGPointApplyAffineTransform()' sembra che potrebbe curare questo, ma devo ancora essere in grado di farlo funzionare. E lo terrò su Dropbox per ora e quando toglierò alcuni bug lo ospiterò volentieri su Github. –

+0

Ok, grazie per lo sforzo, lo apprezzo davvero! – Luke

1

Penso che sarebbe di aiuto se si disegna il testo su un CALayer. Una libreria che potrebbe aiutare con questo è: https://github.com/ZaBlanc/CanvasKit Disegna il prossimo passo dell'animazione su un nuovo CALayer e trasforma in quello. Si potrebbe concatenare le animazioni richiesti usando questa libreria: https://github.com/yangmeyer/CPAnimationSequence o questa libreria: https://github.com/clayallsopp/Walt

+0

L'ultimo link è Ruby. Il CanvasKit sembra usare [NSString drawAtPoint]. Il consumo di memoria sembra inferiore a quello di UILabel, quindi vale la pena provarlo, grazie! – Luke