Frage

Ich habe eine Benachrichtigung, dass Feuer in meinem Modell, wenn bestimmte Eigenschaften ändern. Als Ergebnis fängt ein Selektor in einem bestimmten Ansicht Objekt die Meldungen entsprechend die Position der Ansicht zu ändern.

Die Meldungen verursachen eine Ansicht auf dem Fenster in einer bestimmten Richtung (vertikal oder horizontal immer und immer durch eine Standard-Schrittgröße auf dem Fenster) zu bewegen. Es ist möglich, dass die Benutzer-Aktionen mehrere Meldungen zu verursachen nacheinander abzufeuern. Zum Beispiel, 3-Benachrichtigungen gesendet werden, um den Blick nach unten in drei Schritten zu bewegen, dann zwei weitere Mitteilungen könnten die Ansicht zu bewegen, um die richtigen zwei Schritte gesendet werden.

Das Problem ist, dass, wie ich die Animationen ausführen, sie nicht nacheinander geschehen. Also, im vorigen Beispiel, obwohl ich die Ansicht will langsam nach unten bewegen drei Räumen und dann über zwei Räume als Folge der Mitteilungen verschieben, anstatt es am Ende diagonal in die neuen Position zu bewegen.

Hier ist der Code für meine beiden Selektoren (beachten Sie, dass placePlayer die Position der Ansicht setzt nach aktuellen Informationen im Modell):

- (void)moveEventHandler: (NSNotification *) notification
{
    [self placePlayer];

    CABasicAnimation* moveAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    moveAnimation.duration = 3;
    moveAnimation.fillMode = kCAFillModeForwards;  // probably not necessary
    moveAnimation.removedOnCompletion = NO;        // probably not necessary
    [[self layer] addAnimation:moveAnimation forKey:@"animatePosition"];
}

Alle Vorschläge, wie mehrere Anrufe auf diese Methoden Kraft Animation machen Schritt-für-Schritt auszuführen, anstatt alle auf einmal? Dank !!

War es hilfreich?

Lösung 2

Die Lösung, die ich in der Tat eine Warteschlange verwenden implementiert. Hier ist eine ziemlich vollständige Beschreibung:

Das ist alles in einer Ansicht Klasse getan genannt PlayerView. In der Kopfzeile enthalten ich folgende:

#import "NSMutableArray+QueueAdditions.h"

@interface PlayerView : UIImageView {
        Player* representedPlayer;    // The model object represented by the view
        NSMutableArray* actionQueue;  // An array used as a queue for the actions
        bool animatingPlayer;         // Notes if the player is in the middle of an animation
        bool stoppingAnimation;       // Notes if all animations should be stopped (e.g., for re-setting the game)
        CGFloat actionDuration;       // A convenient way for me to change the duration of all animations
// ... Removed other variables in the class (sound effects, etc) not needed for this example
}

// Notifications
+ (NSString*) AnimationsDidStopNotification;

@property (nonatomic, retain) Player* representedPlayer;
@property (nonatomic, retain, readonly) NSMutableArray* actionQueue;
@property (nonatomic, assign) CGFloat actionDuration;
@property (nonatomic, assign) bool animatingPlayer;
@property (nonatomic, assign) bool stoppingAnimation;
// ... Removed other properties in the class not need for this example

- (void)placePlayer;                                        // puts view where needed (according to the model) without animation
- (void)moveEventHandler:(NSNotification *) notification;   // handles events when the player moves
- (void)rotateEventHandler:(NSNotification *) notification; // handles events when the player rotates
// ... Removed other action-related event handles not needed for this example

// These methods actually perform the proper animations
- (void) doMoveAnimation:(CGRect) nextFrame;
- (void) doRotateAnimation:(CGRect)nextFrame inDirection:(enum RotateDirection)rotateDirection;
// ... Removed other action-related methods not needed for this example

// Handles things when each animation stops
- (void) animationDidStop:(NSString*)animationID 
                 finished:(BOOL)finished 
                  context:(void*)context;

// Forces all animations to stop
- (void) stopAnimation;
@end

Als Nebenwirkung, die QueueAdditions Kategorie in NSMutableArray + QueueAdditions.h / m sieht wie folgt aus:

@interface NSMutableArray (QueueAdditions)
- (id)popObject;
- (void)pushObject:(id)obj;
@end

@implementation NSMutableArray (QueueAdditions)
- (id)popObject
{
    // nil if [self count] == 0
    id headObject = [self objectAtIndex:0];
    if (headObject != nil) {
        [[headObject retain] autorelease]; // so it isn't dealloc'ed on remove
        [self removeObjectAtIndex:0];
    }
    return headObject;
}

- (void)pushObject:(id)obj
{
        [self addObject: obj];
}
@end

Als nächstes werden bei der Umsetzung des PlayerView, ich habe folgende:

#import "PlayerView.h"
#import <QuartzCore/QuartzCore.h>

@implementation PlayerView

@synthesize actionQueue;
@synthesize actionDuration;
@synthesize animatingPlayer;
@synthesize stoppingAnimation;


// ... Removed code not needed for this example (init to set up the view's image, sound effects, actionDuration, etc)

// Name the notification to send when animations stop
+ (NSString*) AnimationsDidStopNotification
{
        return @"PlayerViewAnimationsDidStop";
}

// Getter for the representedPlayer property
- (Player*) representedPlayer
{
        return representedPlayer;
}

// Setter for the representedPlayer property
- (void)setRepresentedPlayer:(Player *)repPlayer
{
        if (representedPlayer != nil)
        {
                [[NSNotificationCenter defaultCenter] removeObserver:self];
                [representedPlayer release];
        }
        if (repPlayer == nil)
        {
                representedPlayer = nil;
                // ... Removed other code not needed in this example         
        }
        else
        {
                representedPlayer = [repPlayer retain];

                if (self.actionQueue == nil)
                {
                        actionQueue = [[NSMutableArray alloc] init];
                }
                [actionQueue removeAllObjects];
                animatingPlayer = NO;
                stoppingAnimation = NO;

                [[NSNotificationCenter defaultCenter]
                addObserver:self
                selector:@selector(moveEventHandler:)
                name:[Player DidMoveNotification]
                object:repPlayer ];

                [[NSNotificationCenter defaultCenter]
                addObserver:self
                selector:@selector(rotateEventHandler:)
                name:[Player DidRotateNotification]
                object:repPlayer ];
                // ... Removed other addObserver actions and code not needed in this example         
         }
}


// ... Removed code not needed for this example

- (void) placePlayer
{
        // Example not helped by specific code... just places the player where the model says it should go without animation
}


// Handle the event noting that the player moved
- (void) moveEventHandler: (NSNotification *) notification
{
        // Did not provide the getRectForPlayer:onMazeView code--not needed for the example.  But this
        // determines where the player should be in the model when this notification is captured
        CGRect nextFrame = [PlayerView getRectForPlayer:self.representedPlayer onMazeView:self.mazeView];

        // If we are in the middle of an animation, put information for the next animation in a dictionary
        // and add that dictionary to the action queue.
        // If we're not in the middle of an animation, just do the animation        
        if (animatingPlayer)
        {
                NSDictionary* actionInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                             [NSValue valueWithCGRect:nextFrame], @"nextFrame",
                                             @"move", @"actionType",
                                             @"player", @"actionTarget",
                                             nil];
                [actionQueue pushObject:actionInfo];
        }
        else
        {
                animatingPlayer = YES;  // note that we are now doing an animation
                [self doMoveAnimation:nextFrame];
        }
}


// Handle the event noting that the player rotated
- (void) rotateEventHandler: (NSNotification *) notification
{
        // User info in the notification notes the direction of the rotation in a RotateDirection enum
        NSDictionary* userInfo = [notification userInfo];
        NSNumber* rotateNumber = [userInfo valueForKey:@"rotateDirection"];

        // Did not provide the getRectForPlayer:onMazeView code--not needed for the example.  But this
        // determines where the player should be in the model when this notification is captured
        CGRect nextFrame = [PlayerView getRectForPlayer:self.representedPlayer onMazeView:self.mazeView];

        if (animatingPlayer)
        {
                NSDictionary* actionInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                             [NSValue valueWithCGRect:nextFrame], @"nextFrame",
                                             @"rotate", @"actionType", 
                                             rotateNumber, @"rotateDirectionNumber",
                                             @"player", @"actionTarget",
                                             nil];
                [actionQueue pushObject:actionInfo];
        }
        else
        {
                enum RotateDirection direction = (enum RotateDirection) [rotateNumber intValue];
                animatingPlayer = YES;
                [self doRotateAnimation:nextFrame inDirection:direction];
        }        
}


// ... Removed other action event handlers not needed for this example


// Perform the actual animation for the move action
- (void) doMoveAnimation:(CGRect) nextFrame
{
        [UIView beginAnimations:@"Move" context:NULL];
        [UIView setAnimationDuration:actionDuration];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
        self.frame = nextFrame;        
        [UIView commitAnimations];
}


// Perform the actual animation for the rotate action
- (void) doRotateAnimation:(CGRect)nextFrame inDirection:(enum RotateDirection)rotateDirection
{
        int iRot = +1;
        if (rotateDirection == CounterClockwise)
        {
                iRot = -1;        
        }

        [UIView beginAnimations:@"Rotate" context:NULL];
        [UIView setAnimationDuration:(3*actionDuration)];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

        CGAffineTransform oldTransform = self.transform;
        CGAffineTransform transform = CGAffineTransformRotate(oldTransform,(iRot*M_PI/2.0));
        self.transform = transform;

        self.frame = nextFrame;

        [UIView commitAnimations];
}



- (void) animationDidStop:(NSString*)animationID 
                 finished:(BOOL)finished 
                  context:(void *)context
{
        // If we're stopping animations, clear the queue, put the player where it needs to go 
        // and reset stoppingAnimations to NO and note that the player is not animating
        if (self.stoppingAnimation)
        {
                [actionQueue removeAllObjects];
                [self placePlayer];
                self.stoppingAnimation = NO;
                self.animatingPlayer = NO;
        }

        else if ([actionQueue count] > 0) // there is an action in the queue, execute it
        {
                NSDictionary* actionInfo = (NSDictionary*)[actionQueue popObject];
                NSString* actionTarget = (NSString*)[actionInfo valueForKey:@"actionTarget"];  
                NSString* actionType = (NSString*)[actionInfo valueForKey:@"actionType"]; 

                // For actions to the player...
                if ([actionTarget isEqualToString:@"player"])
                {
                        NSValue* rectValue = (NSValue*)[actionInfo valueForKey:@"nextFrame"];
                        CGRect nextFrame = [rectValue CGRectValue];

                        if ([actionType isEqualToString:@"move"])
                        {
                                [self doMoveAnimation:nextFrame];
                        }
                        else if ([actionType isEqualToString:@"rotate"])
                        {
                                NSNumber* rotateNumber = (NSNumber*)[actionInfo valueForKey:@"rotateDirectionNumber"];
                                enum RotateDirection direction = (enum RotateDirection) [rotateNumber intValue];
                                [self doRotateAnimation:nextFrame inDirection:direction];
                        }
                        // ... Removed code not needed for this example
                }
                else if ([actionTarget isEqualToString:@"cell"])
                {
                            // ... Removed code not needed for this example
                }

        }
        else // no more actions in the queue, mark the animation as done
        {
                animatingPlayer = NO;
                [[NSNotificationCenter defaultCenter]
                 postNotificationName:[PlayerView AnimationsDidStopNotification]
                 object:self
                 userInfo:[NSDictionary dictionaryWithObjectsAndKeys: nil]];
        }
}



// Make animations stop after current animation by setting stopAnimation = YES
- (void) stopAnimation
{
        if (self.animatingPlayer)
        {
                self.stoppingAnimation = YES;
        }
}


- (void)dealloc {
        if (representedPlayer != nil)
        {
                [[NSNotificationCenter defaultCenter] removeObserver:self];
        }
        [representedPlayer release];
        [actionQueue release];
        // …Removed other code not needed for example
        [super dealloc];
}

@end

Erklärung:

Die Ansicht abonniert Benachrichtigungen vom Modellobjekt aneignen (Spieler). Wenn es eine Benachrichtigung erfaßt, überprüft er, ob es bereits eine Animation macht (mit der animatingPlayer Eigenschaft). Wenn ja, ist es die Informationen aus der Meldung erfolgt (unter Hinweis darauf, wie der Spieler soll animiert werden), stellt diese Informationen in ein Wörterbuch, und fügt hinzu, dass Wörterbuch, um die Animation Warteschlange. Wenn es zur Zeit keine Animation geht, wird festgelegt das Verfahren animatingPlayer auf true und ruft einen entsprechenden do [Was] Animation, Routine.

Jedes tun [Was] Animation, Routine führt die richtigen Animationen, eine setAnimationDidStopSelector zu animationDidStop Einstellung: beendet: Kontext :. Wenn jede Animation endet, die animationDidStop: beendet: Kontext: Verfahren (nach der Überprüfung, ob alle Animationen sofort gestoppt werden sollen) wird die nächste Animation in der Warteschlange durchführt, indem das nächste Wörterbuch aus der Warteschlange ziehen und zu interpretieren, um seine Daten die nennen angemessen do [Was] Animationsverfahren. Wenn es keine Animationen in der Warteschlange, dass Routine setzt animatingPlayer auf NO und bucht eine Benachrichtigung, so dass andere Objekte erkennen können, wenn der Spieler in geeigneter Weise seinen aktuellen Lauf von Animationen gestoppt.

Das ist es. Es kann eine einfachere Methode sein (?), Aber das funktionierte ziemlich gut für mich. Schauen Sie sich meine Mazin App im App Store, wenn Sie zu sehen, dass die tatsächlichen Ergebnisse interessiert sind.

Danke.

Andere Tipps

Ich denke, was Sie wollen, könnten hier zu tun ist, eine Warteschlange von Animationen einrichten, die nacheinander passieren müssen und legen Sie die Animationen delegieren, so dass Sie die animationDidStop erhalten: beendet: Nachricht. Auf diese Weise, wenn eine Animation abgeschlossen Sie die nächste in der Warteschlange aufrechnen kann.

Sie sollten darüber nachdenken, mehrere Punkte entlang des Animationspfades in einem Array bereitstellt, wie unten gezeigt.

Das nachstehende Beispiel gibt mehrere Punkte entlang der y-Achse, aber Sie können auch einen Bezier-Pfad angeben, dass Sie Ihre Animation folgen sollen.

Der Hauptunterschied zwischen Grundanimation und Key-Frame-Animation ist, dass Key-Frame mehr Punkte auf dem Weg.

angeben kann,
CAKeyframeAnimation *downMoveAnimation;
downMoveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
downMoveAnimation.duration = 12;
downMoveAnimation.repeatCount = 1;
downMoveAnimation.values = [NSArray arrayWithObjects:           
                              [NSNumber numberWithFloat:20], 
                              [NSNumber numberWithFloat:220], 
                              [NSNumber numberWithFloat:290], nil]; 
   downMoveAnimation.keyTimes = [NSArray arrayWithObjects:     
                                  [NSNumber numberWithFloat:0], 
                                  [NSNumber numberWithFloat:0.5], 
                                  [NSNumber numberWithFloat:1.0], nil]; 

   downMoveAnimation.timingFunctions = [NSArray arrayWithObjects:                                    
   [CAMediaTimingFunction     functionWithName:kCAMediaTimingFunctionEaseIn],   
        // from keyframe 1 to keyframe 2
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], nil]; 
   // from keyframe 2 to keyframe 3

   downMoveAnimation.removedOnCompletion = NO;
   downMoveAnimation.fillMode = kCAFillModeForwards;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top