具有同时计数线程的UIView动画

时间:2015-08-14 03:20:57

标签: ios objective-c multithreading animation uiview

我正在尝试使用动画块来扩展UIView,这非常有效。但是,我希望UILabel从0开始,每0.01秒加1,直到达到100。 我在动画之后创建了一个线程来完成这个并且它可以工作,但它会导致我设置的动画什么都不做。 我尝试了很多不同的东西,但没有运气。什么是实现这一目标的最佳方式?

我最简单的尝试与其他所有人一样:

[UIView animateWithDuration:1 animations:^{
    _lView.frame = CGRectMake(_lView.frame.origin.x,_lView.frame.origin.y+_lView.frame.size.height,_lView.frame.size.width,-500);
}];

[[[NSThread alloc]initWithTarget:self selector:@selector(startcounting) object:nil]start];


-(void)startcounting{
for(int x=0; x<100; x++){
    [NSThread sleepForTimeInterval:0.01];
    ++_mcount;
    dispatch_async(dispatch_get_main_queue(), ^{
        _cLabel.text = [NSString stringWithFormat:@"%i",_mcount];
    });
  }
}

3 个答案:

答案 0 :(得分:1)

有两个问题:

  1. 关于同时更改标签的帧动画,问题是更改标签的文本会导致重新应用约束。正确的解决方案是不通过更改帧来设置动画,而是通过更改指示帧的约束然后在动画块中调用layoutIfNeeded来设置动画。请参阅Animating an image view to slide upwards

  2. 关于标签的动画:

    • 您无法保证可以快速处理更新。 (事实上​​,你应该计划永远不要超过60 fps。)

    • 即使您能够充分降低更新频率,也永远无法保证它们能够以恒定速率处理(其他东西总能阻止主线程几毫秒,产生不一致的数字递增)。

    所以,你应该使用一个计时器(或更好的&#34;显示链接&#34;,它就像一个计时器,但在屏幕准备好刷新时协调调用),计算经过的时间(这样,计数器不会受到正在发生的其他事情的影响,但是这个值会从0到100迅速变得足以产生你想要的效果),并相应地更新标签。例如:

    @interface ViewController ()
    
    @property (nonatomic, strong) CADisplayLink *displayLink;
    @property (nonatomic) CFTimeInterval startTime;
    
    @property (weak, nonatomic) IBOutlet UILabel *label;
    
    @end
    
    @implementation ViewController
    
    - (void)startDisplayLink {
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
        self.startTime = CACurrentMediaTime();
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    
    - (void)stopDisplayLink {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    - (void)handleDisplayLink:(CADisplayLink *)displayLink {
        CFTimeInterval elapsed = CACurrentMediaTime() - self.startTime;
    
        if (elapsed < 1) {
            self.label.text = [NSString stringWithFormat:@"%.0f", elapsed * 100.0];
        } else {
            self.label.text = @"100";
            [self stopDisplayLink];
        }
    }
    
  3. 因此,结合这两点,只需调整约束(将插座添加到约束后)并调用startDisplayLink(来自主线程),您的标签将在0到100的范围内更新一秒钟作为观点动画:

    [self startDisplayLink];
    self.topConstraint.constant += self.lView.frame.size.height;
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];
    

答案 1 :(得分:0)

_lView.frame = CGRectMake(_lView.frame.origin.x,[[UIScreen mainScreen] bounds].size.height,_lView.frame.size.width,_lView.frame.size.height);    
[[[NSThread alloc]initWithTarget:self selector:@selector(startcounting) object:nil]start];


/*
Comment this code

[UIView animateWithDuration:1 animations:^{
_lView.frame = CGRectMake(_lView.frame.origin.x,_lView.frame.origin.y+_lView.frame.size.height,_lView.frame.size.width,-500);
}];
*/

修改此方法

-(void)startcounting{
int initialY = _lView.frame.origin.y;
for(int x=0; x<=100; x++){
    [NSThread sleepForTimeInterval:0.01];
    dispatch_async(dispatch_get_main_queue(), ^{
        if (initialY>=_lView.frame.origin.y-(x*0.03))//adjust 0.03 as you required
        {
            _lView.frame = CGRectMake(_lView.frame.origin.x,_lView.frame.origin.y-(x*0.03),_lView.frame.size.width,_lView.frame.size.height);//adjust 0.03 as you required
        }
        else
        {
            _lView.frame = CGRectMake(_lView.frame.origin.x,initialY,_lView.frame.size.width,_lView.frame.size.height);
        }
       _cLabel.text = [NSString stringWithFormat:@"%i",x];
    });
}
}

答案 2 :(得分:0)

我终于明白了。我以前应该看过这个。

首先我认为这是约束的问题所以我从UIView中删除了我正在更新的约束。这是必要的,但没有解决问题。

整个视图控制器仍然有自己的约束,我无法在需要时删除它们。所以我所做的是添加了一个带有约束的新UIView,并将UIView设置为在这个新的UIView上动画。

这解决了这个问题,因为动画UIView不再对任何约束负责,也不负责受任何其他约束的影响。

我还不明白的是,当涉及约束时,如何同时更新UILabel会导致动画失败,但是当删除UILabel代码时,工作完全正常。然而,这确实解决了这个问题。