如何修复不更新 UI 的目标 c 应用程序?
Posted
技术标签:
【中文标题】如何修复不更新 UI 的目标 c 应用程序?【英文标题】:How can I fix objective c app not updating UI? 【发布时间】:2020-10-05 05:21:24 【问题描述】:我正在尝试在 Objective C 上为我的应用程序创建一种奇怪的计时器。我希望它能够运行,当用户输入时间并单击开始按钮时,我希望它隐藏键盘和文本字段,以及开始按钮,并显示一个文本字段,然后开始倒计时并定期更新以当前“秒”显示的文本字段,然后当倒计时循环结束时,它应该说计时器完成。
我在整个过程中进行了大量测试,检查我的功能是否正常运行,并且它们都正确输出。但是我的应用程序在它应该隐藏键盘几秒钟后没有隐藏键盘,它没有隐藏开始按钮,并且在循环结束之前它不会在视觉上更新秒数。老实说,我不知道发生了什么……有人可以告诉我怎么回事吗?
谢谢!
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor blueColor];
self.timerSecondsText.hidden = YES;
- (IBAction)startTimer:(id)sender
int timerSeconds = [self.timeFieldVisuals.text intValue];
int timerSecondsFifth = timerSeconds/5;
NSLog(@"timerSeconds is %i",timerSeconds);
NSLog(@"timerSecondsFifth is %i",timerSecondsFifth);
[_timeFieldVisuals resignFirstResponder];
self.timeFieldVisuals.hidden = YES;
self.timerSecondsText.hidden = NO;
if(_timerSecondsText.hidden == NO && _timeFieldVisuals.hidden == YES)
NSLog(@"visual updates successful");
for (int timeElapsed = 0; timerSeconds > 0; timerSeconds--)
//timeElapsed is placeholder to avoid errors
/* 5 divides, the timer delay increases as the
timerSeconds decreases, but timer should run for
user input in the end*/
if(timerSeconds > timerSecondsFifth*4 && timerSeconds <= timerSecondsFifth*6)
[NSThread sleepForTimeInterval:0.4f];
self.timerSecondsText.text = [NSString stringWithFormat:@"%i", timerSeconds];
else if(timerSeconds > timerSecondsFifth*3 && timerSeconds <= timerSecondsFifth*4)
[NSThread sleepForTimeInterval:0.6f];
else if(timerSeconds > timerSecondsFifth*2 && timerSeconds <= timerSecondsFifth*3)
[NSThread sleepForTimeInterval:1.0f];
else if(timerSeconds > timerSecondsFifth && timerSeconds <= timerSecondsFifth*2)
[NSThread sleepForTimeInterval:1.2f];
else if(timerSeconds <= timerSecondsFifth)
[NSThread sleepForTimeInterval:1.8f];
self.timerSecondsText.text = [NSString stringWithFormat:@"%i", timerSeconds];
NSLog(@"timerSeconds is %i",timerSeconds);
self.timerSecondsText.text = @"Timer Complete";
@end
【问题讨论】:
您正在使用sleepForTimeInterval
调用冻结 UI 线程。当 UI 线程被冻结时,UI 不会更新,这不是很明显吗?
@EugeneDudnyk 所以我想通了,但后来我决定调用 [self.view setNeedsDisplay] 和 [self.view layoutIfNeeded],理论上应该强制重绘显示,我在调用之前调用了它们单独的方法,我将循环插入其中。所以据我所知,这应该迫使它在开始循环之前重新绘制,但它不起作用。所以也许我应该尝试使用常规的 NSTimer 而不是使用 sleepForTimeInterval?
是的,NSTimer 对于此类任务来说要容易得多。 setNeedsDisplay 和 layoutIfNeeded 不会将 NSThread 从睡眠中唤醒。您还可以使用 CADisplayLink 强制重复调用所选方法选择器以重绘您的字符串和/或按钮,并在时间结束时停止 displayLink。
用睡眠函数阻塞主线程是错误的,其他是基于意见的
取决于框架。当 UIKit 按原样使用时,阻塞 mainThread 确实没有帮助。有些框架的 UIKit 更新没有在 mainThread 中运行。
【参考方案1】:
下面只是一个例子,你可以做什么来代替[NSThread sleepForTimeInterval:...]
。使用CADisplayLink
和NSTimer
而不是与NSThread 方法进行交互,因为通常UIKit 在MainThread 中运行。因此,将 MainThread 推入睡眠会导致很多问题,就像您已经遇到的那样。
#import "TimingViewController.h"
@interface TimingViewController ()
@property (nonatomic) CADisplayLink *displayLink;
@property (nonatomic) NSTimer *timer;
@end
@implementation TimingViewController
UIButton *btn;
UILabel *label;
UITextField *timerSecondsText;
NSDate *starttime;
BOOL timerFieldIsValid;
NSTimeInterval futureInterval;
- (void)viewDidLoad
[super viewDidLoad];
timerSecondsText = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 80, 40)];
timerSecondsText.hidden = NO;
[timerSecondsText addTarget:self action:@selector(validateEntry:) forControlEvents:(UIControlEventValueChanged)];
[self.view addSubview:timerSecondsText];
timerFieldIsValid = NO;
label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 80, 40)];
label.text = @"--:--";
label.hidden = YES;
[self.view addSubview:label];
btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.titleLabel.text = @"Start";
btn.frame = CGRectMake(10,60,80,40);
btn.hidden = NO;
[btn addTarget:self action:@selector(startTimer) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
- (IBAction)startTimer
if (timerFieldIsValid)
if (!_timer)
[timerSecondsText resignFirstResponder];
timerSecondsText.hidden = YES;
starttime = [NSDate now];
_timer = [NSTimer scheduledTimerWithTimeInterval:futureInterval target:self selector:@selector(timeIsOver) userInfo:nil repeats:NO];
label.hidden = NO;
btn.hidden = YES;
if (!_displayLink)
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updatePulse)];
//_displayLink.preferredFramesPerSecond = 24;
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-(IBAction)validateEntry:(UITextField*)textfield
if (textfield.text.length)
// textfield validation here..
NSTimeInterval possibleInterval = [textfield.text integerValue];
if (possibleInterval > 0)
futureInterval = possibleInterval;
timerFieldIsValid = YES;
else
timerFieldIsValid = NO;
else
timerFieldIsValid = NO;
-(void)timeIsOver
[_timer invalidate];
self.timer = nil;
[_displayLink invalidate];
self.displayLink = nil;
label.hidden = YES;
btn.hidden = NO;
timerSecondsText.hidden = NO;
-(void)updatePulse
label.text = [NSString stringWithFormat:@"%d", starttime.timeIntervalSinceNow];
@end
【讨论】:
非常感谢!但实际上我使用 NSTimer 修复了它,我认为这是一种更简单的方法。我基本上创建了一个函数循环(不是一个循环,而是两个在满足条件之前一直被调用的函数),一个用于计时器,一个用于视觉更新,所以它现在可以完美更新! 很高兴你知道了。以上是关于如何修复不更新 UI 的目标 c 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不更新应用程序的情况下重新加载 Cordova 应用程序的 UI