当前位置: 首页 > 知识库问答 >
问题:

UITextView不会在后台线程忙时滚动

昌学
2023-03-14

我正在后台线程中运行数学计算。试图在UITextView中实时发布结果。但是,在后台线程完成之前,结果不会显示。为什么不呢?

我在后台启动了一个方法,

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^() {
    [self v2];
});

背景线程方法的形式为,

- (void) v2 {
    NSString *result;
    // ... loop a bunch of times generating lots of results
    for (bunch of stuff to compute) {
      // If using dispatch_async, nothing is displayed until this method finishes
      // If dispatch_sync then it does display and update
      result = [self computeNextValue];
      dispatch_async(dispatch_get_main_queue(), ^() {
        textView.text = result;
      });
    } // end computation
}

在我开始尝试滚动视图之前,这其实不是什么大问题。慢得令人痛苦。因此,我创建了一个NSTimer来定期滚动UITextView。但是,即使计时器弹出并且方法运行以请求滚动,UITextView也不会滚动,直到后台方法完成。

共有3个答案

凌意
2023-03-14

尝试将预定义的调度队列字符串更改为以后台结尾。

__weak MyClass weakSelf = self;
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^() {
      weakSelf.textView.text = result;
    });

此外,还应添加UIActivityIndicator,并在操作开始时开始设置动画,然后在textview之后停止设置动画。文本字段已更新。

这是一个很好的特性,它必须向用户显示当前正在完成一个流程

另外,我会远离NSThread,因为我已经阅读了苹果的一些论坛和文档,它们强调使用GCD和块操作。

公西宏峻
2023-03-14

我想我找到了答案,至少是经验上的。

它看起来像是在UITextView上设置文本或以编程方式启动滚动设置在另一个线程中运行的动画序列。似乎正在发生的是,设置UITextView文本的请求比UITextView设置动画的速度更快。当属性再次更改时,它显然会取消在另一个线程上设置的早期动画。当我不断地向它发送更改请求时,它永远无法在“文本”的值再次更改之前完成它想要做的事情。

我的解决方案涉及多个步骤。

在我最初的方法中,我设置了textView。文本很快就有了新的价值。我设置了一个计时器,定期请求UITextView滚动。在我的修改中,我计算结果字符串的新值,但不将其设置为UITextView。相反,文本是基于计时器在textview上定期设置的。这使文本视图能够跟上进度。

然而,我注意到这仍然不可靠。如果我碰巧在文本还在滚动的时候再次设置文本,就会出现奇怪的效果,比如非常慢的滚动。滚动动画和文本的重复设置似乎仍然是一个问题。

为了解决这个问题,我创建了一个属性来指示视图是否正在滚动。将视图控制器设置为UITextField委托。当我请求视图滚动时,我设置标志以指示其滚动。仅更新内容并在尚未滚动时请求滚动。最后工作得很好。不管我设置计时器有多快,它最终都会适当地等待。

// ViewController.h
@property BOOL isViewScrolling;

// ViewController.m
// initialize property in viewDidLoad
- (void)viewDidLoad {
  self.isViewScrolling = FALSE;
  textView.delegate = self;
  self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(scrollIt) userInfo:nil repeats:TRUE];
}

- (void)scrollIt {
  NSLog(@"scrollit thread=%d", [[NSThread currentThread]isMainThread]);
  if (!self.isViewScrolling) {
    textView.text = self.result;
    NSRange range = NSMakeRange(textView.text.length - 1, 1);
    self.isViewScrolling = TRUE;
    [textView scrollRangeToVisible:range];
  }
}

// UITextView delegate
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
  NSLog(@"Animation stopped");
  self.isViewScrolling = FALSE;
}
申屠喜
2023-03-14

首先,确保你在块中使用的是弱自定义而不是自定义。

__weak MyClass weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^() {
    [weakSelf v2]; 
});

...

__weak MyClass weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^() {
  weakSelf.textView.text = result;
});

但这不会导致延迟更新。我非常怀疑排队的优先级,DISPATCH_QUEUE_PRIORITY_LOW。试着用DISPATCH_QUEUE_PRIORITY_DEFAULT代替。

 类似资料:
  • 如果http请求超时,我试图中断当前线程。我已经为Kafka事务设置了PlatformTransactionManager作为bean。我在方法级别使用@Transactional注释。我们将发布三个主题的信息。在第一个主题中发布消息后,我将添加线程。睡眠(5000),如果执行时间超过6秒,当前线程将从筛选器中断。所以这里的通话被打断了,但信息被发布给了Kafka。我们只是在传达信息。我们不消费任

  • 我正在创建一个javafx程序,该程序的导航菜单通过更改场景的根来工作。根都是从Pane类继承的。有些窗格具有它们运行的后台线程。但是,当根窗格被事件处理程序更改时,窗格会切换,但后台线程不会停止。这会导致线程读取NFC时出现问题,并导致多个线程尝试从NFC读取器读取。 如何关闭后台线程?(从创建线程的窗格外部),还是需要以不同的方式设置线程。(线程设置为Daemon)。线程是在窗格构造函数中创建

  • 在下面的代码中,线程。activeCount()始终返回2,即使executor中的线程在5秒后终止。 我期望Thread.activeCount()在5秒后返回1。为什么它总是返回2?

  • 嗨,我正在做一个项目,我已经达到了我非常困的部分。我试图寻找方法来学习如何在繁忙的等待中编写 while 循环,但我没有找到任何东西,我的代码只是作为无限循环运行。有人可以帮助我解释一个繁忙的等待循环应该如何工作,并帮助我打破这个无限循环吗? 该项目希望做到以下几点:早上,学生醒来后(这需要一段随机的时间),他会去洗手间为新的上学日做准备。如果浴室已经客满,学生需要Rest一下(使用yield()

  • 我原以为这是一个简单的问题,但我很难找到答案。我有一个与JavaFX场景对象关联的ImageView对象,我想从磁盘加载大图像,并使用ImageView一个接一个地显示它们。我一直在努力寻找一种好方法来反复检查图像对象,当它在后台加载完成时,将其设置为ImageView,然后开始加载新的图像对象。我提出的代码(如下)有时有效,有时无效。我很确定我在JavaFX和线程方面遇到了问题。它有时加载第一个