我在我的模型的火灾时,某些性质改变的通知。其结果是,在特定视图中的对象的选择器捕获通知相应地改变视图的位置。

在通知导致窗口上,以便在一个特定的方向(由窗口上的标准步长大小总是垂直或水平和总是)移动。这有可能对用户的行动会导致几个通知,火一前一后。例如,3个通知可以被发送到移动视图向下三个步骤,然后两个通知可以被发送到视图向右移动两个步骤。

的问题是,当我执行的动画,他们不会连续发生。所以,在前面的例子,但我希望以缓慢向下移动三个空格,然后通过两个空格作为通知的结果移动,而不是它结束了对角移动到新的位置。

下面是用于我的两个选择器的代码(注意placePlayer设置视图的位置根据在模型中的当前信息):

- (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"];
}

如何让多次调用该方法力动画一次执行一步一步的,而不是所有的有什么建议?谢谢!

有帮助吗?

解决方案 2

我实现确实使用队列的解决方案。这是一个相当完整的描述:

这是在所有称为PlayerView一个视图类来完成。在报头中我包括如下:

#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

顺便说一句,在的NSMutableArray + QueueAdditions.h /米QueueAdditions类别是这样的:

@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

接着,在PlayerView的实施,我有以下:

#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

说明:

在视图预订适当从模型对象(运动员)的通知。当它抓住通知时,它会检查,看它是否已经在做的动画(使用animatingPlayer属性)。如果是这样,它从通知(注意玩家应该如何进行动画)开出的信息,把这些信息转换成字典,并添加字典到动画队列。如果没有动画目前正,则该方法将设置animatingPlayer为true,并调用适当的DO [无论]动画例程。

每个做[无论]动画例程执行适当的动画,设置setAnimationDidStopSelector到animationDidStop:完成:上下文:.当每个动画结束时,animationDidStop:完成:context:方法(检查是否所有的动画,应立即停止后)将通过按顺序拨打拉动未来字典从队列中,并解释其数据执行队列中的下一个动画适当的做[无论]动画方法。如果在队列中没有动画,是程序设置animatingPlayer为NO和岗位的通知,以便其他对象可以知道当玩家已经停止恰当其当前的动画运行。

这就是它。有可能是一个更简单的方法(?),但这个工作很适合我。看看我的马青的应用程序在App Store如果你有兴趣在看到实际效果。

感谢。

其他提示

我想你可能想在这里做的是建立需要连续发生,设置动画代表,这样您将收到animationDidStop动画队列:完成:消息。这样,当一个动画完成可以掀起队列中的下一个。

您应该考虑如下所示以阵列沿着动画路径提供多个点。

下面的示例指定多个点沿y轴,但你也可以指定你希望你的动画遵循贝塞尔路径。

基本的动画和关键帧动画之间的主要区别是,关键帧允许你沿着路径指定多个点。

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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top