处理 UIActivityIndi​​cator 和多线程的最佳方法是什么?

发布于 2024-10-03 23:34:19 字数 3553 浏览 4 评论 0原文

我已经尝试解决这个问题很长时间了,但似乎找不到最好的方法。我很困惑,因为对于如何完成这个看似简单的任务似乎有不同的答案/意见。

我希望能够有一个名为 ActivityIndi​​catorController 的可重用类。该控制器有两个主要方法:activateIndicator 和 deactivateIndicator。它采用 UIView 作为参数/属性以及 NSString 作为标签。激活后,它将关闭 UIView 中的用户交互,并添加一个矩形子视图(带有 alpha 和圆角)、一个 UIActivityIndi​​cator 控件和一个用于状态文本的 UILabel。这是可取的,因为这样我就不必在每个视图控制器中都有自定义 UIActivityIndi​​catorView 代码,也不必在每个 NIB 中设置 ActivityIndi​​cator。

我从根本上遇到的问题是如何启动添加 ActivityIndi​​cator 并对其进行动画处理的过程。我尝试过的一些方法根本不显示新视图。其他的可以工作,但 ActivityIndi​​cator 没有动画。

我尝试在 activateIndicator 方法中使用 [NSThread detachNewThreadSelector:@selector(startAnimating) toTarget:activityIndi​​cator withObject:nil] ,但这不会显示新的 UIView。

我尝试在调用方法中使用 [NSThread detachNewThreadSelector:@selector(activateIndicator) toTarget:activityIndi​​catorController withObject:nil] ,但这会将新 UIView 的整个创建放在一个单独的线程中。

现在回答问题:

第 1 部分:我理解所有 UI 都应该在主线程上处理,这是正确的吗?

第 2 部分:使用 [NSThread detachThreadSelector] 与 NSOperation 的区别/优点/缺点是什么?

第 3 部分:是否更好:

(a) 将冗长的操作发送到新的后台线程并回调主线程,或者

(b) 将 UIActivityIndi​​catorView startAnimating 方法发送到单独的线程并在主线程上运行冗长的过程

并且为什么?

这是我当前的代码:

ActivityViewController 类:

-(void)activateIndicator {
NSLog(@"activateIndicator called");
if (isActivated || !delegateView)
    return;
NSLog(@"activateIndicator started");

[delegateView.view setUserInteractionEnabled:NO];
[delegateView.navigationController.view setUserInteractionEnabled:NO];
[delegateView.tabBarController.view setUserInteractionEnabled:NO];

float w = [[UIScreen mainScreen] bounds].size.width;
float h = [[UIScreen mainScreen] bounds].size.height;

NSLog(@"Width = %f\nHeight = %f", w, h);

if (!disabledView) {
    disabledView = [[[UIView alloc] initWithFrame:CGRectMake((w - kNormalWidth) / 2.0, (h - kNormalHeight) / 2.0, kNormalWidth, kNormalHeight)] autorelease];
    disabledView.center = [[[delegateView.view superview] superview] center];
    [disabledView setBackgroundColor:[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.85]];

CALayer *layer = [disabledView layer];
NSLog(@"layer=%@",layer);
NSLog(@"delegate=%@",[layer delegate]);
layer.cornerRadius = 12.0f;
}

if (!activityIndicator) {
    activityIndicator = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(kNormalWidth / 2, 10.0f, 40.0f, 40.0f)] autorelease];
    [activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
    activityIndicator.center = disabledView.center;
}

if (!activityLabel) {
    activityLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10.0f, 100.0f, kNormalWidth - 20, 38)] autorelease];
    activityLabel.text = labelText;
    activityLabel.textAlignment = UITextAlignmentCenter;
    activityLabel.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f];
    activityLabel.textColor = [UIColor colorWithWhite:1.0f alpha:1.0f];
    activityLabel.center = disabledView.center;
}

[[[delegateView.view superview] superview] addSubview:disabledView];

[[[delegateView.view superview] superview] addSubview:activityIndicator];
[[[delegateView.view superview] superview] addSubview:activityLabel];


[NSThread detachNewThreadSelector:@selector(startAnimating) toTarget:activityIndicator withObject:nil];
}

从应用程序中的多个位置调用代码:

    ActivityIndicatorController *aic = [[ActivityIndicatorController alloc] init];
aic.delegateView = self;
aic.labelText = @"Test...";
[aic activateIndicator];

//DO LENGTHY WORK ON MAIN THREAD

[aic deactivateIndicator];
[aic release], aic = nil;

I have been trying to play around with this for a long time and I can't seem to find the best approach. I am getting confused because there seem to be different answers/opinions on how to accomplish this seemingly simple task.

I want to be able to have a reusable class called ActivityIndicatorController. This controller has two main methods: activateIndicator and deactivateIndicator. It takes a UIView as an argument/property as well as an NSString for a label. Upon activation, it will turn off user interaction in the UIView and add a rectangle subview (with alpha and rounded corners), a UIActivityIndicator control and a UILabel for the status text. This is desirable because that way I don't have to have custom UIActivityIndicatorView code in each view controller or to have to set up an ActivityIndicator in each NIB.

The problem I am fundamentally having is how to kick off this process of adding and animating the ActivityIndicator. Some methods I have tried don't display the new view at all. Others work, but the ActivityIndicator doesn't animate.

I have tried using [NSThread detachNewThreadSelector:@selector(startAnimating) toTarget:activityIndicator withObject:nil] inside the activateIndicator method, but that doesn't display the new UIView.

I have tried using [NSThread detachNewThreadSelector:@selector(activateIndicator) toTarget:activityIndicatorController withObject:nil] from a calling method, but this would put the whole creation of the new UIView in a separate thread.

Now to the question:

Part 1: I understand that all UI should be handled on the main thread, is that correct?

Part 2: What is the difference/advantage/disadvantage of using [NSThread detachThreadSelector] versus NSOperation?

Part 3: Is it better to:

(a) send the lengthy operation to a new background thread with a callback to the main thread OR

(b) send the UIActivityIndicatorView startAnimating method to a separate thread and run the lengthy process on the main thread

AND why?

Here is my current code:

ActivityViewController class:

-(void)activateIndicator {
NSLog(@"activateIndicator called");
if (isActivated || !delegateView)
    return;
NSLog(@"activateIndicator started");

[delegateView.view setUserInteractionEnabled:NO];
[delegateView.navigationController.view setUserInteractionEnabled:NO];
[delegateView.tabBarController.view setUserInteractionEnabled:NO];

float w = [[UIScreen mainScreen] bounds].size.width;
float h = [[UIScreen mainScreen] bounds].size.height;

NSLog(@"Width = %f\nHeight = %f", w, h);

if (!disabledView) {
    disabledView = [[[UIView alloc] initWithFrame:CGRectMake((w - kNormalWidth) / 2.0, (h - kNormalHeight) / 2.0, kNormalWidth, kNormalHeight)] autorelease];
    disabledView.center = [[[delegateView.view superview] superview] center];
    [disabledView setBackgroundColor:[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.85]];

CALayer *layer = [disabledView layer];
NSLog(@"layer=%@",layer);
NSLog(@"delegate=%@",[layer delegate]);
layer.cornerRadius = 12.0f;
}

if (!activityIndicator) {
    activityIndicator = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(kNormalWidth / 2, 10.0f, 40.0f, 40.0f)] autorelease];
    [activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
    activityIndicator.center = disabledView.center;
}

if (!activityLabel) {
    activityLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10.0f, 100.0f, kNormalWidth - 20, 38)] autorelease];
    activityLabel.text = labelText;
    activityLabel.textAlignment = UITextAlignmentCenter;
    activityLabel.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f];
    activityLabel.textColor = [UIColor colorWithWhite:1.0f alpha:1.0f];
    activityLabel.center = disabledView.center;
}

[[[delegateView.view superview] superview] addSubview:disabledView];

[[[delegateView.view superview] superview] addSubview:activityIndicator];
[[[delegateView.view superview] superview] addSubview:activityLabel];


[NSThread detachNewThreadSelector:@selector(startAnimating) toTarget:activityIndicator withObject:nil];
}

Calling Code from multiple places in the app:

    ActivityIndicatorController *aic = [[ActivityIndicatorController alloc] init];
aic.delegateView = self;
aic.labelText = @"Test...";
[aic activateIndicator];

//DO LENGTHY WORK ON MAIN THREAD

[aic deactivateIndicator];
[aic release], aic = nil;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

路还长,别太狂 2024-10-10 23:34:19

第 1 部分:我理解所有 UI 都应该在主线程上处理,对吗?

正确的。

第 2 部分:使用 [NSThread detachThreadSelector] 与 NSOperation 的区别/优点/缺点是什么?

NSOperation 是一个更高级别的接口,允许您对操作进行排队、创建多个相互依赖的操作等。在后台处理任务的其他选项有 performSelectorOnMainThread:.../performSelectorInBackground:... 和 Grand Central Dispatch。

第 3 部分:是否更好:

(a) 将冗长的操作发送到新的后台线程,并回调主线程或

(b) 将 UIActivityIndi​​catorView startAnimating 方法发送到单独的线程并在主线程上运行冗长的过程

因为在问题 1 的答案中,(a) 是您唯一的选择。

Part 1: I understand that all UI should be handled on the main thread, is that correct?

Correct.

Part 2: What is the difference/advantage/disadvantage of using [NSThread detachThreadSelector] versus NSOperation?

NSOperation is a higher-level interface that allows you to queue operations, create several operations that depend on each other, etc. Other options to work with tasks in the background are performSelectorOnMainThread:.../performSelectorInBackground:... and Grand Central Dispatch.

Part 3: Is it better to:

(a) send the lengthy operation to a new background thread with a callback to the main thread OR

(b) send the UIActivityIndicatorView startAnimating method to a separate thread and run the lengthy process on the main thread

Because of the answer to question 1, (a) is your only option.

鸢与 2024-10-10 23:34:19

将您的冗长工作放在一个单独的线程中,这样,如果您确实需要一些交互(例如取消操作),它就不会完全阻塞 UI。然后,您的 ActivityIndi​​catorController 应该调用主线程来执行所有 UI 操作,例如:

@implementation ActivityIndicatorController

    - (void)activateIndicator
    {
        [self performSelectorOnMainThread:@selector(activateOnMainThread)
                               withObject:nil
                            waitUntilDone:YES];
    }

    - (void)activateOnMainThread
    {
        // Do your actual UI stuff here.
    }

    // And similarly for the deactivate method.

Put your lengthy work in a separate thread, that way it doesn't completely block out the UI in case you do need some interaction (say to cancel the operation). Then, your ActivityIndicatorController should call out to the main thread to do all the UI stuff, e.g.:

@implementation ActivityIndicatorController

    - (void)activateIndicator
    {
        [self performSelectorOnMainThread:@selector(activateOnMainThread)
                               withObject:nil
                            waitUntilDone:YES];
    }

    - (void)activateOnMainThread
    {
        // Do your actual UI stuff here.
    }

    // And similarly for the deactivate method.
傲影 2024-10-10 23:34:19

一旦显示并动画化,即使主线程被阻塞,活动指示器也会继续动画化。

然而,视图没有机会出现,因为运行循环尚未执行,因为等待冗长的操作。

所以我认为您需要的只是延迟为 0 的 performSelector:withObject:afterDelay ,这样您的冗长操作将在指标变得可见后排队并执行。

Once displayed and animated, the activity indicator will continue animating even the main thread is blocked.

However, the view has no chance to come up because the run loop has yet to executed because pending lengthy operation.

So I think what you need is just performSelector:withObject:afterDelay with delay 0, so your lengthy operation will be queued and performed after your indicator become visible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文