【问题标题】:UIButton IBAction method only drawing to screen once?UIButton IBAction 方法只在屏幕上绘制一次?
【发布时间】:2012-12-12 00:47:07
【问题描述】:

我对我只是为了好玩而尝试的事情有点困惑。我编写了一个名为 animateBars_V1 的小方法,它使用 UIImageViews 数组并更改每个 UIImageView 的高度以显示一组不断变化的彩条。

- (void)animateBars_V1 {
    srandom(time(NULL));
    for(UIImageView *eachImageView in [self barArray]) {
        NSUInteger randomAmount = kBarHeightDefault + (random() % 100);

        CGRect newRect;
        CGRect barRect = [eachImageView frame];

        newRect.size.height = randomAmount;
        newRect.size.width = barRect.size.width;
        newRect.origin.x = barRect.origin.x;
        newRect.origin.y = barRect.origin.y - (randomAmount - barRect.size.height);
        [eachImageView setFrame:newRect];
    }
}

这很好用,然后我添加了一个带有 UIAction 的 UIButton 用于按下按钮时。每次按下按钮时都会调用 animateBars_V1 并更新彩条。

- (IBAction)buttonPressed {
    for(int counter = 0; counter<5; counter++) {
        [self animateBars_V1];
        NSLog(@"COUNTER: %d", counter);
    }
}

我的问题只是为了好玩,我决定每次按下按钮时我都会拨打animateBars_V1 5 次。发生的情况是,直到循环退出之后,条形才会改变。这导致:

Screen as per storyboard
COUNTER: 0
COUNTER: 1
COUNTER: 2
COUNTER: 3
COUNTER: 4
Screen Updates

这是正确的行为吗?我不需要修复或解决方法,因为这只是为了好玩,我更好奇发生了什么以供将来参考。

【问题讨论】:

    标签: iphone objective-c cocoa-touch uibutton


    【解决方案1】:

    如果您在一个循环中多次调用animateBars_V1,则条形的帧会被设置多次,但在渲染它们之前,animateBars_V1 会再次被调用,并且帧会被设置到一个新位置/大小。

    直到循环完成后才会调用渲染(drawRect: 和相关方法) - 因为它是 IBAction,所以必须在主线程中调用它,这意味着所有渲染都被阻塞,直到代码完成。

    当然有几种解决方案。做多动画的一个简单方法是按以下方式使用UIView animateWithDuration:animations:completion:

    - (IBAction)buttonPressed {
        [self animateBarsWithCount:5];
    }
    
    - (void)animateBarsWithCount:(int)count
    {
        [UIView animateWithDuration:.25f animations:^{
            [self animateBars_V1];
        }completion:^(BOOL finished){
            [self animateBarsWithCount:count - 1];
        }];
    }
    
    //animateBars_V1 not repeated
    

    当然,如果你只是想运行一次动画,(但实际上是动画)你应该这样做:

    - (IBAction)buttonPressed {
        [UIView animateWithDuration:.25f animations:^{
            [self animateBars_V1];
        } completion:nil];
    }
    

    【讨论】:

    • 谢谢,这确实有道理,我怀疑发生了类似的事情。我只是想确定我知道为什么以供将来参考。非常感谢...
    • 是的,在 iOS 上绘图不是“实时”的,而是在你“退出”你的方法之后发生的。
    • 谢谢大家,再次非常感谢。
    • UIView animateWithDuration:animations:completion: 很有意思,我一定会加进去的。
    【解决方案2】:

    CrimsonDiego 是对的

    您可以尝试通过以下方式延迟每次通话:

    - (IBAction)buttonPressed {
        for(int counter = 0; counter<5; counter++) {
            float ii = 1.0 * counter / 10;
            [self performSelector:@selector(animateBars_V1) withObject:nil afterDelay:ii];
            //  [self animateBars_V1];
            NSLog(@"COUNTER: %d", counter);
        }
    }
    

    【讨论】:

    • performSelector:withObject:afterDelay 也是一种完全有效的方法。如果单独使用它缺乏动画效果,但它可以在更多情况下使用,而不仅仅是动画。
    • 如果可以的话,只是一个简短的问题, performSelector:withObject:afterDelay: 在这种情况下工作,因为当它“执行选择器”时,方法 -(IBAction)buttonPressed 早已退出并且不是阻塞主线程?
    • 基本上是的,它可以工作,因为它在 5 个新线程中运行“animateBars_V1”,并且主线程在找到它们时重新绘制尺寸,因为最后执行的“animateBars_V1”设置它们
    【解决方案3】:

    问题来了

    for(int counter = 0; counter<5; counter++) {
        [self animateBars_V1];
        NSLog(@"COUNTER: %d", counter);
    }
    

    这个 for 循环在纳秒内执行,您的眼睛无法捕捉到这种变化,因为眼睛只能检测到 1/16 秒。 用于测试什么可以在运行五次的计时器中运行此代码。

    已编辑 删除了 sleep 调用,因为它将使主线程休眠,并且一切都将停止。所以在这里使用Timer

    【讨论】:

    • 不是眼睛速度的问题:Xcode 只是不会为每个循环绘制它,它只是设置一些变量,然后它只绘制线程末尾的最后一个变量值
    • @meronix 按钮操作在主线程上触发,除非您从后台线程显式调用该方法。
    • 对...但是重绘屏幕不是显示在线程的末尾吗?如果没有,如果你使用超过 5 个(例如 500000)的循环,你应该会看到一些东西......眼睛变慢了,但你应该注意到一种闪光,而不仅仅是代码执行的延迟......
    • 这就是我的意思,如果您使用更大的循环(如您建议的 500000),我们将看到闪烁,这意味着代码正在不断更新帧。它不等待线程完成。
    • 我没有尝试过代码,但我认为我们不会出现闪烁...您尝试过吗?
    猜你喜欢
    • 1970-01-01
    • 2010-09-25
    • 2023-03-13
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 2011-01-24
    • 2017-10-22
    • 1970-01-01
    相关资源
    最近更新 更多