使用CoreAnimation同步图像,文本和定位 [英] Synchronising image, text, and positioning with CoreAnimation

查看:89
本文介绍了使用CoreAnimation同步图像,文本和定位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是动画初学者,并且已经尝试了CoreAnimation几天了。如果这个问题没有道理,请随时警告我,但我正在努力实现以下目标。我有三个对象:




  • 一个应该是图像,并按照给定的模式移动

  • 一个应该是交换两个图像的UIImage

  • 一个应该是内容更改的文本(CATextLayer?)



这三个动作应该同步发生。



例如,考虑一个显示正弦函数的程序,例如在ECG中,在-1之间振荡和+1:然后,第一张图片将根据当前值(-1,-0.9,-0.8,... 0,+ 0.1,+ 0.2,... 1)移动,交换图片将显示 +对于正值,对于负值,则使用-,文本将在正值和负值之间切换。



我已经尝试过CAAnimationGroup,但我显然缺少一些东西。某些代码:

  {
//移动
的图像CALayer * img1 = [CALayer层];
img1.bounds = CGRectMake(0,0,20,20);
img1.position = CGPointMake(x1,y1);
UIImage * img1Image = [UIImage imageNamed:@ image.png];
img1.contents =(id)img1Image.CGImage;

//更改
的图片CALayer * swap = [CALayer层];
swap.bounds = CGRectMake(0,0,30,30);
swap.position = CGPointMake(x2,y2);
NSString * nameswap = @ img_swap_1.png
UIImage * swapImg = [UIImage imageNamed:nameswap];

//文本
CATextLayer * text = [CATextLayer层];
text.bounds = CGRectMake(0,0,100,100);
text.position = CGPointMake(x3,y3);
text.string = @文本;


//创建动画
CGFloat持续时间= 0.2;
CGFloat totalDuration = 0.0;
CGFloat开始= 0;

NSMutableArray * animarray = [[NSMutableArray alloc] init];
NSMutableArray * swapanimarray = [[NSMutableArray alloc] init];
NSMutableArray * textanimarray = [[NSMutableArray alloc] init];

浮点数prev_x = 0;
float prev_y = 0;

//我得到的移动对象
的值(self.events中的NSDictionary *事件){

float actual_x = [[event valueForKey:@ x] floatValue];
float actual_y = [[event valueForKey:@ y] floatValue];


//图像移动动画
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@ position];
CGPoint startPt = CGPointMake(prev_x,prev_y);
CGPoint endPt = CGPointMake(actual_x,actual_y);
anim.duration =持续时间;
anim.fromValue = [NSValue valueWithCGPoint:startPt];
anim.toValue = [NSValue valueWithCGPoint:endPt];
anim.beginTime =开始;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animarray addObject:anim];


//图片交换动画
CABasicAnimation * swapanim = [CABasicAnimation animationWithKeyPath:@ contents];

swapanim.duration =期限;
swapanim.beginTime =开始;

NSString * swapnamefrom = [NSString stringWithFormat:@%@。png,prev_name];
NSString * swapnameto = [NSString stringWithFormat:@%@。png,current_name];
UIImage * swapFromImage = [UIImage imageNamed:swapnamefrom];
UIImage * swapToImage = [UIImage imageNamed:swapnameto];
swapanim.fromValue =(id)(swapFromImage.CGImage);
swapanim.toValue =(id)(swapToImage.CGImage);
swapanim.fillMode = kCAFillModeForwards;
swapanim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[swapanimarray addObject:swapanim];

//文本动画
CABasicAnimation * textanim = [CABasicAnimation animationWithKeyPath:@ contents];
textanim.duration =持续时间;
textanim.fromValue = @嘿;
textanim.toValue = @ Hello;
textanim.beginTime =开始;
[textanimarray addObject:textanim];


//最终时间设置
prev_x = actual_x;
prev_y = actual_y;
开始=开始+持续时间;
totalDuration =开始+持续时间;

}

}


CAAnimationGroup *组= [CAAnimationGroup动画];
[group setDuration:totalDuration];
group.removedOnCompletion =否;
group.fillMode = kCAFillModeForwards;
[group setAnimations:animarray];

CAAnimationGroup * swapgroup = [CAAnimationGroup动画];
[交换组setDuration:totalDuration];
swapgroup.removedOnCompletion =否;
swapgroup.fillMode = kCAFillModeForwards;
[swapgroup setAnimations:swapanimarray];

CAAnimationGroup *文本组= [CAAnimationGroup动画];
[textgroup setDuration:totalDuration];
textgroup.removedOnCompletion =否;
textgroup.fillMode = kCAFillModeForwards;
[textgroup setAnimations:textanimarray];

[ball addAnimation:group forKey:@ position];
[交换addAnimation:flaggroup forKey:@ position];
[text addAnimation:textgroup forKey:@ contents];

[self.layer addSublayer:ball];
[self.layer addSublayer:swap];
[self.layer addSublayer:text];


}

现在...问题:



1)每次交换时,交换映像都会恢复为原始映像。因此,如果我交换A-> B,我会看到它在预期的持续时间内从A变到B,然后又恢复为A。我已经在SO上阅读了许多关于此的线程,但无法使其正常工作



2)以定时方式更改文本层的字符串...使用此基础结构是否可能?基本上,我试图使文本和交换图像在第一个图像移动时就发生变化,如示例中所述。



3)设置委托尽管CABasicAnimation对CAAnimationGroup起作用,但它没有任何作用:因此,您不能为整个动画组为每个动画管理诸如animationDidStop之类的事件。



4)(3)之后,是否可以使用CAAnimationGroup拦截事件以创建停止/开始行为?假设我想要播放/停止按钮,并从刚放过的位置继续播放动画?



作为一个结论性问题,我只是想知道如果有人做了类似的事情,最重要的是,这种做事的方式(使用CAAnimationGroup)实际上是走的路,还是最好使用CAKeyFrameAnimation或其他方式。

解决方案

尽管有一些解决方法,我还是设法解决了这个问题。



1)这里的窍门是

  removedOnCompletion = NO; 
fillMode = kCAFillModeForwards;

需要分配给每个动画,而不是CAAnimationGroup。



2)这需要为CATextLayer确定一个单独的动画,并为 string属性设置动画。在这种情况下,代码是正确的,但内容键值应为

  [text addAnimation:textgroup forKey:@ contentAnimate]; 

或者,请参见第3点。回调操作方式允许更改label.text。 p)

3)做到这一点的唯一方法是实际上不使用CAAnimationGroup并设置一系列CABasicAnimation。通用模式是:在函数中创建第一个CABasicAnimation,分配委托。在animationDidStop方法中,向创建CABasicAnimation的函数添加回调。这样,将创建每个动画。同时,这允许截取动画中的特定事件并对事件做出反应。

 -(void)performNextAnimation:(int )index {
// ...这自然会给您一个#index对象...
NSDictionary * thisEvent = [self.events objectAtIndex:index];
float prev_x = [thisEvent valueForKey:@ prev_x];
float prev_y = [thisEvent valueForKey:@ prev_x];
float actual_x = [thisEvent valueForKey:@ prev_x];
float actual_y = [thisEvent valueForKey:@ prev_x];
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@ position];
GPoint startPt = CGPointMake(prev_x,prev_y);
CGPoint endPt = CGPointMake(actual_x,actual_y);
anim.duration = 0.5;
anim.fromValue = [NSValue valueWithCGPoint:startPt];
anim.toValue = [NSValue valueWithCGPoint:endPt];
anim.beginTime =开始;
[anim setDelegate:self];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[对象addAnimation:anim forKey:@ position];
}

-(void)animationDidStop:(CAAnimation *)theAnimation完成:(BOOL)flag {
// ...您的条件在这里...
// ...您的操作转到此处...
//例如设置label.text
[self performNextAnimation];
}

4)从3开始,在CAAnimationGroup中是不可能的。阅读文档时,我想说的是CAAnimationGroup不适用于每个事件都代表时间向前迈进的序列(为此,您需要像以前那样将CAKeyFrameAnimation或CABasicAnimation序列与回调函数一起使用)。 CAAnimationGroup用于链接的动画,该动画应全部执行或不执行(类似于CATransaction)。


I am a bit of a beginner with animations and have been experimenting with CoreAnimation for a couple of days. Feel free to warn me if this question does not make sense, but I'm trying to achieve the following. I have three objects:

  • one should be an image, moving according to a given pattern
  • one should be an UIImage that swaps two images
  • one should be a text (CATextLayer?) whose content changes

The three actions should happen in sync.

As an example, think about a programme showing a sinusoid function, like in a ECG, oscillating between -1 and +1: the first image would then move according to the current value (-1, -0.9, -0.8, ... 0, +0.1, +0.2, ... 1), the swap image would show "+" for positive values and "-" for negative values, and the text would alternate between "Positive" and "Negative".

I've tried with CAAnimationGroup but I'm clearly missing something. Some code:

{
    // image that moves
    CALayer *img1 = [CALayer layer];
    img1.bounds = CGRectMake(0, 0, 20, 20);
    img1.position = CGPointMake(x1,y1);
    UIImage *img1Image = [UIImage imageNamed:@"image.png"];
    img1.contents = (id)img1Image.CGImage;

    // image that changes 
    CALayer *swap = [CALayer layer];
    swap.bounds = CGRectMake(0, 0, 30, 30);
    swap.position = CGPointMake(x2,y2);
    NSString* nameswap = @"img_swap_1.png"
    UIImage *swapImg = [UIImage imageNamed:nameswap];

    // text
    CATextLayer *text = [CATextLayer layer];
    text.bounds = CGRectMake(0, 0, 100, 100);
    text.position = CGPointMake(x3,y3);
    text.string = @"Text";


    // create animations
    CGFloat duration = 0.2;
    CGFloat totalDuration = 0.0;
    CGFloat start = 0;

    NSMutableArray* animarray = [[NSMutableArray alloc] init];
    NSMutableArray* swapanimarray = [[NSMutableArray alloc] init];
    NSMutableArray* textanimarray = [[NSMutableArray alloc] init];

    float prev_x = 0;
    float prev_y = 0;

    // I get my values for moving the object
    for (NSDictionary* event in self.events) {

            float actual_x = [[event valueForKey:@"x"] floatValue];
            float actual_y = [[event valueForKey:@"y"] floatValue];


            // image move animation
            CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];            
            CGPoint startPt = CGPointMake(prev_x,prev_y);
            CGPoint endPt = CGPointMake(actual_x, actual_y);
            anim.duration = duration;
            anim.fromValue = [NSValue valueWithCGPoint:startPt];
            anim.toValue = [NSValue valueWithCGPoint:endPt];
            anim.beginTime = start;
            anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];            
            [animarray addObject:anim];


            // image swap animation
                        CABasicAnimation *swapanim = [CABasicAnimation animationWithKeyPath:@"contents"];

            swapanim.duration = duration;
            swapanim.beginTime = start;

            NSString* swapnamefrom = [NSString stringWithFormat:@"%@.png", prev_name];
            NSString* swapnameto = [NSString stringWithFormat:@"%@.png", current_name];
            UIImage *swapFromImage = [UIImage imageNamed:swapnamefrom];
            UIImage *swapToImage = [UIImage imageNamed:swapnameto];
            swapanim.fromValue = (id)(swapFromImage.CGImage);
            swapanim.toValue = (id)(swapToImage.CGImage);
            swapanim.fillMode = kCAFillModeForwards;
            swapanim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];      
            [swapanimarray addObject:swapanim];

            //text animation
            CABasicAnimation *textanim = [CABasicAnimation animationWithKeyPath:@"contents"];            
            textanim.duration = duration;
            textanim.fromValue = @"Hey";
            textanim.toValue = @"Hello";
            textanim.beginTime = start;
            [textanimarray addObject:textanim];


            // final time settings
            prev_x = actual_x;
            prev_y = actual_y;
            start = start + duration;
            totalDuration = start + duration;

        }

    }


    CAAnimationGroup* group = [CAAnimationGroup animation];
    [group setDuration:totalDuration]; 
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;
    [group setAnimations:animarray];

    CAAnimationGroup* swapgroup = [CAAnimationGroup animation];
    [swapgroup setDuration:totalDuration]; 
    swapgroup.removedOnCompletion = NO;
    swapgroup.fillMode = kCAFillModeForwards;
    [swapgroup setAnimations:swapanimarray];

    CAAnimationGroup* textgroup = [CAAnimationGroup animation];
    [textgroup setDuration:totalDuration]; 
    textgroup.removedOnCompletion = NO;
    textgroup.fillMode = kCAFillModeForwards;
    [textgroup setAnimations:textanimarray];

    [ball addAnimation:group forKey:@"position"];
    [swap addAnimation:flaggroup forKey:@"position"];
    [text addAnimation:textgroup forKey:@"contents"];

    [self.layer addSublayer:ball];
    [self.layer addSublayer:swap];
    [self.layer addSublayer:text];


}

Now... the problems:

1) the swap image reverts to the original at every swap. So, if I swap A->B, I see it going from A to B in the expected duration time, but then reverting to A. I've read a number of threads on SO about this but couldn't get it to work.

2) changing the string of the text layer in a timed fashion... is this possible with this infrastructure? Basically, I'm trying to get the text and the swap image to change as soon as the first image moves, as described in the example.

3) setting the delegate for the CABasicAnimation doesn't have any effect, although it does for the CAAnimationGroup: as a result, you can't manage events like animationDidStop for every single animation, just for the whole group. Is there any alternative way to do so?

4) following from 3), is it possible, using CAAnimationGroup, to intercept the events to create a stop/start behaviour? Let's suppose I wanted to have play/stop buttons, and to resume the animation from exactly where I had left it?

As a conclusive question, I would simply like to know if anyone did something similar and, most importantly, if this way of doing things (using a CAAnimationGroup) is actually the way to go or if it's better to use CAKeyFrameAnimation or something else.

解决方案

I managed to solve the problem, albeit with some workarounds.

1) The trick here is that

removedOnCompletion = NO;
fillMode = kCAFillModeForwards;

need to be assigned to every single animation, not to the CAAnimationGroup.

2) This requires a separate animation to be determined for the CATextLayer, and animating the "string" property. In this case, the code was right except for the "contents" key-value which should have been

[text addAnimation:textgroup forKey:@"contentAnimate"];

Alternatively, see point 3. The callback way of operation allows to change a label.text.

3) The only way to do this is to actually not use the CAAnimationGroup and set up a sequence of CABasicAnimation. The general schema is: create the first CABasicAnimation in a function, assign the delegate. In the animationDidStop method, add a callback to the function that creates the CABasicAnimation. This way, each animation will be created. At the same time, this allows to intercept and react to specific events within the animation.

-(void)performNextAnimation:(int)index {
    // ... this gives for granted you have an object for #index ...
    NSDictionary* thisEvent = [self.events objectAtIndex:index];
    float prev_x = [thisEvent valueForKey:@"prev_x"];
    float prev_y = [thisEvent valueForKey:@"prev_x"];
    float actual_x = [thisEvent valueForKey:@"prev_x"];
    float actual_y = [thisEvent valueForKey:@"prev_x"];
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];            
    GPoint startPt = CGPointMake(prev_x,prev_y);
    CGPoint endPt = CGPointMake(actual_x, actual_y);
    anim.duration = 0.5;
    anim.fromValue = [NSValue valueWithCGPoint:startPt];
    anim.toValue = [NSValue valueWithCGPoint:endPt];
    anim.beginTime = start;
    [anim setDelegate:self];
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];            
    [object addAnimation:anim forKey:@"position"];
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
    // ... your conditions go here ...
    // ... your actions go here ... 
    //     e.g. set label.text
    [self performNextAnimation];
}

4) Following from 3, this is not possible in CAAnimationGroup. Reading the docs, I would say that CAAnimationGroup is not intended to be used for sequences in which each event represents a step forward in time (for this you need to use CAKeyFrameAnimation or a sequence of CABasicAnimation with a callback function, as I did). CAAnimationGroup is intended for linked animation that should be executed on an all-or-nothing basis (similarly to CATransaction).

这篇关于使用CoreAnimation同步图像,文本和定位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆