2010-10-03 13 views
15

Ich habe eine Reihe von 8 UIView-Animationen, die direkt nach dem Laden meiner Ansicht auftreten. Im Moment erreiche ich dies mit der Delegiertenmethode animationDidStop:finished:context, und alles funktioniert wie erwartet. Das Problem ist, dass ich für jede Animation eine neue Methode habe. Der Großteil des Codes in jeder dieser Methoden wird wiederholt, wobei sich nur die Animationsdauer und die tatsächliche Positionierung der Elemente ändern.Die beste Möglichkeit, mehrere sequenzielle UIView-Animationen auszuführen?

Ich habe versucht, eine Methode zu schaffen, und haben den Zusammenhang halten die Parameter aufgerufen werden würde benötigt in geeigneter Weise die Benutzeroberfläche zu ändern, aber es scheint rekursiv selbst über die Höhe der Zeit und ruft zu Ich nenne es:

-(void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context{ 
    NSNumber *number = (NSNumber *)context; 
    int animationStep = [number intValue]; 
    int nextAnimationStep = animationStep + 1; 
    NSNumber *nextAnimationStepNumber = [NSNumber numberWithInt:nextAnimationStep]; 
    NSLog(@"Animation Step: %i", animationStep); 

    CGRect firstFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.secondFeedView.view.frame.size.width, self.secondFeedView.view.frame.size.height); 
    CGRect thirdFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.thirdFeedView.view.frame.size.width, self.thirdFeedView.view.frame.size.height); 

    [UIView beginAnimations:nil context:nextAnimationStepNumber]; 
    [UIView setAnimationBeginsFromCurrentState:YES]; 
    [UIView setAnimationCurve:UIViewAnimationCurveLinear]; 
    [UIView setAnimationDelegate:self]; 

    if (animationStep < 8) 
     [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)]; 

    NSLog(@"Beginning animations"); 

    switch (animationStep) { 
     case 0: 
      [UIView setAnimationDuration:.3]; 
      self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x + 30, self.firstFeedView.view.center.y); 
      break; 
     case 1: 
      [UIView setAnimationDuration:.3]; 
      self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 30, self.firstFeedView.view.center.y); 
      break; 
     case 2: 
      [UIView setAnimationDuration:.3]; 
      [self.secondFeedView.view setFrame:firstFrame]; 
      break; 
     case 3: 
      [UIView setAnimationDuration:.3]; 
      self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x + 30, self.firstFeedView.view.center.y); 
      break; 
     case 4: 
      [UIView setAnimationDuration:.3]; 
      self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 30, self.firstFeedView.view.center.y); 
      break; 
     case 5: 
      NSLog(@"Animation step 6"); 
      [UIView setAnimationDuration:.5]; 
      self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 230, self.firstFeedView.view.center.y); 
      self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 230, self.firstFeedView.view.center.y); 
      break; 
     case 6: 
      [UIView setAnimationDuration:.5]; 
      [self.thirdFeedView.view setFrame:thirdFrame]; 
      break; 
     case 7: 
      [UIView setAnimationDuration:.3]; 
      self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x + 30, self.firstFeedView.view.center.y); 
      break; 
     case 8: 
      [UIView setAnimationDuration:.3]; 
      self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x - 30, self.thirdFeedView.view.center.y); 
      break; 
     default: 
      break; 
    } 

    [UIView commitAnimations]; 
} 

Ich weiß, das ist wahrscheinlich eine naive Umsetzung. Ich bin neu in der iPhone-Entwicklung und bin auf der Suche nach einigen Best Practices, die Sie hier anwenden können. Gehe ich falsch herum?

Antwort

-1
// create the view that will execute our animation 
UIImageView* logo = [[UIImageView alloc] initWithFrame:self.view.frame]; 
// load all the frames of our animation 
logo.animationImages = [NSArray arrayWithObjects: 
           [UIImage imageNamed:@"frame_00001.png"], 

         [UIImage imageNamed:@"frame_00002.png"], 
         [UIImage imageNamed:@"frame_00003.png"], 
         [UIImage imageNamed:@"frame_00004.png"], 
           [UIImage imageNamed:@"frame_00005"], nil]; 

// all frames will execute in 3 seconds 
logo.animationDuration =3.3; 
// repeat the annimation forever 
logo.animationRepeatCount = 1; 
// start animating 
[logo startAnimating]; 
// add the animation view to the main window 
[self.view addSubview:logo]; 
[logo release]; 

dieses in den ViewDidLoad

+0

Jede Ansicht Animation hat ein anderes Verhalten in meinem Fall. Im Prinzip rutscht einer rein, der zweite gleitet herein und springt den ersten aus dem Weg, dann gleitet der dritte leicht rechts in den Blick. Der Code, den Sie oben zeigen, führt die gleiche Animation in allen Ansichten durch, richtig? –

+0

ja das ist richtig nur 1 Animation –

19

Wenn Sie bereit sind zu iOS zu intensivieren 4.0, der neuen Blöcke basierte Animation Ansatz dieser Animation Verkettungs trivial machen. Zum Beispiel:

[UIView animateWithDuration:1.0 animations:^{ view.position = CGPointMake(0.0f, 0.0f); } completion:^(BOOL finished){ 
    [UIView animateWithDuration:0.2 animations:^{ view.alpha = 0.0; } completion:^(BOOL finished){ 
     [view removeFromSuperview]; }]; 
}] 

verursachen view auf (0, 0) über eine Dauer von 1 Sekunde, verblasst 0,2 Sekunden zu animieren, dann aus seiner Super entfernt werden. Diese Animationen werden sequenziell sein.

Der erste Block in der Klassenmethode +animateWithDuration:animations:completion: enthält die sofort zu animierenden Eigenschaftenänderungen und die zweite Aktion, die nach Abschluss der Animation ausgeführt werden soll. Da dieser Callback eine andere Animation dieser Art enthalten kann, können Sie sie verschachteln, um beliebige Animationsketten zu erstellen.

+0

Tolle Idee, aber wir sind verpflichtet, zumindest iOS 3.x für jetzt zu unterstützen. Ich wünschte, wir könnten es tun. Dies würde viele Probleme lösen! –

1

Der "Kontext" ist ein void * und wird daher nicht beibehalten. Es gibt ein paar Optionen:

  • Behalten Sie es bei, wenn Sie es einstellen. Autorelease es am Anfang des Rückrufs. Das fühlt sich eklig an (Objective-C-Funktionen sollen behalten/freigeben, aber leider ist der Kontext kein id).
  • Speichern Sie die Nummer in der Zeichenfolge: int animationStep = [animationID intValue]; und [UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];. Das fühlt sich auch eklig an.
  • Angenommen, dass UIKit/CoreAnimation den Zeiger nicht dereferenziert, int animationStep = (int)context; und [UIView beginAnimations:nil context:(void*)nextAnimationStep];. Dies ist eklig (und verursacht wahrscheinlich undefiniertes Verhalten gemäß dem C-Standard).

Auf einem anderen Hinweis sollte finished:(BOOL)finishedfinished:(NSNumber*)finished sein. Sie haben in den Originaldokumenten einen Fehler gemacht. Es war vermutlich einfacher, die Dokumente so zu ändern, dass sie die API widerspiegelten, anstatt die API zu ändern und die Abwärtskompatibilität magisch aufrecht zu erhalten (obwohl das Übergeben eines Bools sinnvoller ist).

+0

Dies ist ein alter Beitrag. ARC gibt Ihnen tatsächlich eine Lösung für dieses Problem (wenn Sie immer noch die alte Methode beginAnimations: context: ... commitAnimations verwenden. Sie können eine __bridge_retained Umwandlung Ihres NSObject in einen void * -Zeiger und dann in Ihrer Komplettierungsmethode verwenden Verwenden Sie __bridge_transfer.Ich musste dies für einige Übergangsanimationen tun, die die alten Aufrufe verwendeten und es funktioniert. (Der Compiler beschwert sich, aber das Objekt wird beibehalten und ordnungsgemäß freigegeben.) –

10

Wir haben eine Komponente zur Verkettung von Animationsschritten deklarativ mit Blöcken (CPAnimationSequence on Github) erstellt. Wir haben die Motivationen und die Begründung beschrieben in our iOS development blog.

Es gibt Ihnen sehr lesbaren Code, so etwas wie folgt aus:

[[CPAnimationSequence sequenceWithSteps: 
    [CPAnimationStep   for:0.25 animate:^{ self.imageView.alpha = 0.0; }], 
    [CPAnimationStep   for:0.25 animate:^{ self.headline.alpha = 0.0; }], 
    [CPAnimationStep   for:0.25 animate:^{ self.content.alpha = 0.0; }], 
    [CPAnimationStep after:1.0 for:0.25 animate:^{ self.headline.alpha = 1.0; }], 
    [CPAnimationStep   for:0.25 animate:^{ self.content.alpha = 1.0; }], 
    nil] 
runAnimated:YES]; 
+0

Vielen Dank für diesen Code. – burtlo

+0

Das sieht so aus eine nette elegante Lösung. (Abstimmung) Ich muss das github Projekt überprüfen. –

+0

Netter Code. Der blog fragt nach einem Benutzernamen und einem Kennwort login, aber ich sehe keinen Registerlink, kann ich den Blog irgendwo lesen sonst? – lxmfly123

16

Sie Blöcke für diesen Zweck verwenden können und ein sehr sauberes Ergebnis.

NSMutableArray* animationBlocks = [NSMutableArray new]; 

typedef void(^animationBlock)(BOOL); 

// getNextAnimation 
// removes the first block in the queue and returns it 
animationBlock (^getNextAnimation)() = ^{ 
    animationBlock block = (animationBlock)[animationBlocks firstObject]; 
    if (block){ 
     [animationBlocks removeObjectAtIndex:0]; 
     return block; 
    }else{ 
     return ^(BOOL finished){}; 
    } 
}; 

//add a block to our queue 
[animationBlocks addObject:^(BOOL finished){; 
    [UIView animateWithDuration:1.0 animations:^{ 
     //...animation code... 
    } completion: getNextAnimation()]; 
}]; 

//add a block to our queue 
[animationBlocks addObject:^(BOOL finished){; 
    [UIView animateWithDuration:1.0 animations:^{ 
     //...animation code... 
    } completion: getNextAnimation()]; 
}]; 

//add a block to our queue 
[animationBlocks addObject:^(BOOL finished){; 
    NSLog(@"Multi-step Animation Complete!"); 
}]; 

// execute the first block in the queue 
getNextAnimation()(YES); 

Entnommen: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/

+1

Sie müssen nur den Namen dieser URL lieben. :-) – CodeReaper

+2

Dies scheint eine ausgezeichnete Lösung zu sein, aber ich verstehe den Code nicht wirklich. Warum gibt es jedes Mal ein Semikolon am Ende der ersten Zeile, wenn Sie der Warteschlange einen Block hinzufügen? – GoldenJoe

+0

Wunderbare Blöcke und NSMutableArray! – lxmfly123

Verwandte Themen