我是iOS编程的新手,我已经找不到答案了。 在Xcode 5中,我在一个数组上进行迭代,并尝试使用值更改时更新标签。
这是.h文件......
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) NSArray *currentNumber;
@property (strong, nonatomic) IBOutlet UILabel *showLabel;
- (IBAction)start;
@end
这是.m文件的主要部分......
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentNumber = [NSArray arrayWithObjects:@"1", @"2", @"3", @"4", nil];
}
这是一个棘手的地方......
以下作品完美无缺......
- (IBAction)start {
self.showLabel.text = [NSString stringWithFormat:@"new text"];
}
@end
就像这样......
- (IBAction)start {
for (NSString *p in self.currentNumber) {
NSLog(@"%@", p);
sleep(3);
}
}
@end
但是当我用设置.text属性替换NSLog时,它“失败”。时间仍然发生,标签在......之后更新数组中的最后一项
- (IBAction)start {
for (NSString *p in self.currentNumber) {
self.showLabel.text = [NSString stringWithFormat:@"%@", p];
sleep(3);
}
}
@end
最后一点奇怪,如果我使用NSLog,并在调用“for”循环之前尝试更改.text属性,则忽略文本更改,直到循环完成后...
- (IBAction)start {
self.showLabel.text = [NSString stringWithFormat:@"5"];
for (NSString *p in self.currentNumber) {
NSLog(@"%@", p);
sleep(3);
}
}
@end
我错过了什么?
(如果您想查看源文件,可以在https://github.com/lamarrg/iterate
获取它们答案 0 :(得分:2)
正如您所知,UI只会在主线程处理事件时更新。在一个循环中,它不会。
有很多方法可以解决这个问题。
最简单的方法是在后台线程中执行循环。但是有一个问题:这将允许用户继续与您的UI进行交互。而且,UI只能从主线程更新。
您需要将您的作品发送到后台,然后让后台将您的作品发送回主线程。
这听起来很复杂,而且确实很复杂。值得庆幸的是,Apple为Objective-C添加了块和Grand Central Dispatch。您可以使用它们来分解代码块并确保它们在正确的线程上执行。
- (IBAction)start {
[self disableMyUI];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_NORMAL, 0), ^{
// this code will be executed "later", probably after start has returned.
// (in all cases, later should be considered "soon but not immediately.")
for (NSString *p in self.currentNumber) {
dispatch_async(dispatch_get_main_queue(),^{
// this code will be executed "later" by the main loop.
// You may have already moved on to the next thing, and even
// dispatched the next UI update.
// Don't worry; the main queue does things in order.
self.showLabel.text = [NSString stringWithFormat:@"%@", p];
});
sleep(3); // do your heavy lifting here, but keep in mind:
// you're on a background thread.
}
dispatch_async(dispatch_get_main_queue,^{
// this occurs "later," but after other all other UI events queued
// to the main queue.
[self enableMyUI];
});
}
// this line of code will run before work is complete
}
你必须写disableMyUI
和enableMyUI
;确保它们禁用所有内容(如果您使用导航,则包括后退按钮,如果您使用标签栏控制器,则使用标签栏等)。
另一种方法是使用NSTimer
。但是,如果你这样做,你仍然在主线程上做你的工作。如果你可以将你的工作分成可预测的小块,它会工作,但你最好在后台线程上做它。
要记住的一件事:尽管在开发过程中你不太可能遇到问题,但在主线程上做大量工作会导致用户崩溃。在iOS上,有一个进程可以监视应用程序是否响应事件,例如绘制更新。如果应用程序没有及时响应事件,它将被终止。因此,缺乏UI更新并不适合您;你只需要在后台线程中进行耗时的操作。
另见:
答案 1 :(得分:0)
如果您想定期更新标签,请不要使用sleep
。如果你在主线程上调用它,你将阻止UI,这是不太理想的。
使用NSTimer
代替,每N秒触发一次。
这样的事情会:
- (void)startUpdatingLabel {
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(updateLabelWithIndex:) userInfo:@0 repeats:NO];
}
- (void)updateLabel:(NSTimer *)timer {
NSInteger index = [timer.userInfo integerValue];
if (index >= self.currentNumber.count) {
return;
}
self.showLabel.text = [NSString stringWithFormat:@"%@", self.currentNumber[index]];
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(updateLabelWithIndex:) userInfo:@(index+1) repeats:NO];
}
每次调用updateLabel:
时,它会安排一个新计时器,该计时器将在3秒后再次调用它。每次index
值增加并传递。