【问题标题】:Why does viewDidLoad wait before updating the UI?为什么 viewDidLoad 会在更新 UI 之前等待?
【发布时间】:2010-02-12 09:38:24
【问题描述】:

我只是想了解一下 iPhone 上的 Objective-C 事件模型,从表面上看,我从根本上误解了这里的一些东西。

出于实验的目的,在视图控制器的 -viewDidLoad 方法中,我设置了 UILabel 的文本,然后休眠两秒钟,然后再次更改标签的文本。

我的期望如下:标签将首先显示“第一个文本”,然后两秒钟后它会更新为“第二个文本”。当然,情况并非如此。相反,视图在两秒钟内根本不可见,最后当它变得可见时,它的标签显示为“第二个文本”。

有人可以向我解释发生了什么吗?我很想知道你们将如何实现我在这里的目标。

干杯。

更新 1:这是 viewDidLoad 方法:

- (void)viewDidLoad {
    [super viewDidLoad];
    label.text = @"First Label";
    sleep(2);
    label.text = @"Second Label";
}

更新 2:我在这里犯了一个愚蠢的错误,所以请忽略此更新。

更新 3:我现在已将以下内容添加到我的 viewDidAppear 方法中:

- (void)viewDidAppear: (BOOL)animated {
    [super viewDidAppear: animated];
    label.text = @"First Label";
    sleep(2);
    label.text = @"Second Label";
}

不幸的是,我遇到了完全相同的问题。

更新 4:按照 gerry3 和 Felix 的建议,我现在实现了一个 performSelector,并且成功了!工作一种享受!我将不得不把它交给 gerry3,因为他肯定会尽最大努力帮助我。感谢您的所有贡献!

【问题讨论】:

    标签: iphone objective-c


    【解决方案1】:

    我猜原因是viewDidLoad 像所有 UIKit 交互一样在 mainThread 上运行。 UI 只能在 mainThread 上更新,因此,如果您使用 sleep(2) 阻止 viewDidLoad,您将使 mainThread 进入睡眠状态并停止该线程上的所有用户界面更新。

    如果您想在一段时间后更新 UI,请使用 NSTimer,而不是使用 sleep(2)。或者在 self 上使用performSelector:withDelay: 稍后执行方法而不阻塞 mainThread。

    viewDidAppearviewWillAppear 也是如此。两者都在 mainThread 上运行。

    【讨论】:

    • 嗯...这很有意义,并解释了为什么它正在做它正在做的事情。我的下一个问题当然是我如何设置它,这样就不会这样做!如果我有足够的声誉,我会投票给你!
    • 这个答案不正确。正如我的回答所解释的,当调用 viewDidLoadviewWillAppear: 时,视图还不可见,因此 David 看到的行为是预期的。但是,如果将代码移至 viewDidAppear:,它将按需要运行。
    • viewDidLoad 方法是在 viewDidAppear 之前调用的,所以在哪里调用 performSelector:withDelay: 应该没什么区别。对 mainThread 的阐述是错误的吗?否则将 sleep(2) 放入 viewDidLoad 会相应地推迟 viewWillAppear ..
    • 其实我想说这个是对的。 :-) 没错,调用此方法时视图尚未出现在屏幕上。但如果不是因为睡觉,画面很快就会出现在屏幕上。睡眠暂停了一切。如果这段代码只是移动到 viewDidAppear,它也不会工作,因为在睡眠期间不会发生绘图或用户交互。
    【解决方案2】:

    将修改视图的代码放入viewDidAppear:

    当调用viewDidLoadviewWillAppear: 时,视图还不可见。

    更新
    为了清楚起见,我同意其他人的观点,即正确的方法是使用延迟的方法调用或计时器。

    更新 2
    这是我建议的代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        NSLog(@"View will appear!");
    }
    
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
    
        NSLog(@"View did appear!");
    
        label.text = @"First Label";
        sleep(2);
        label.text = @"Second Label";
    }
    

    以及“正确”的方式:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        label.text = @"First Label";
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        NSLog(@"View will appear!");
    }
    
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
    
        NSLog(@"View did appear!");
    
        [self performSelector:@selector(changeLabelText) withObject:nil afterDelay:2.0f];
    }
    
    - (void)changeLabelText {
        label.text = @"Second Label";
    }
    

    【讨论】:

    • 干杯格里。我已经用 viewDidAppear 和 viewWillAppear 方法更新了我原来的问题。
    • 您必须使用正确的方法签名:两个 view*Appear: 调用都有一个布尔参数(通常是“动画”)。这就是为什么我用冒号结束他们的名字。此外,您应该为两者调用 super 并传递参数。
    • 完美。最后一段代码正是我正在寻找的。非常感谢格里。
    【解决方案3】:

    我不认为你真的想在那个方法中调用 sleep。您应该尝试使用 Timer 来避免阻塞 UI 和整个应用程序。 Timer 可以让你只调用一次或重复调用一些代码。请参阅guide 了解更多信息

    【讨论】:

      【解决方案4】:

      您的示例中 viewDidAppear 和 viewWillAppear 的签名是错误的。它们应该是

      - (void)viewWillAppear: (BOOL)animated {
          NSLog(@"View will appear!");
      }
      
      -(void)viewDidAppear : (BOOL)animated {
          NSLog(@"View did appear!");
      }
      

      框架不会调用带有错误签名的实现,因为消息分发找不到它们。

      【讨论】:

      • 正确。我在对我的回答的评论中提到了这一点。另外,我认为你应该打电话给 super。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多