2

私は長い間これをいじろうとしてきましたが、最善のアプローチを見つけることができないようです. この一見単純なタスクを達成する方法については、さまざまな回答/意見があるように思われるため、混乱しています。

ActivityIndi​​catorController という再利用可能なクラスを作成したいと考えています。このコントローラーには、activateIndicator と activateIndicator の 2 つの主要なメソッドがあります。引数/プロパティとして UIView と、ラベルの NSString を取ります。アクティブ化すると、UIView でのユーザー操作がオフになり、四角形のサブビュー (アルファと角の丸いもの)、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;
4

3 に答える 3

2

パート 1: すべての UI をメイン スレッドで処理する必要があることは理解していますが、それでよろしいですか?

正しい。

パート 2: [NSThread detachThreadSelector] と NSOperation の違い/利点/欠点は何ですか?

NSOperationバックグラウンドでタスクを操作するその他のオプションは、performSelectorOnMainThread:.../performSelectorInBackground:...および Grand Central Dispatch です。

パート 3: 次のことを行う方が良いですか?

(a) メイン スレッドへのコールバックを使用して、時間のかかる操作を新しいバックグラウンド スレッドに送信する、または

(b) UIActivityIndi​​catorView の startAnimating メソッドを別のスレッドに送信し、メイン スレッドで長いプロセスを実行します。

質問 1 に対する回答のため、(a) が唯一の選択肢です。

于 2010-11-22T16:32:06.590 に答える
1

長い作業を別のスレッドに入れると、何らかの操作が必要な場合 (操作をキャンセルする場合など)、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.
于 2010-11-22T16:30:11.860 に答える
0

アクティビティ インジケーターは、表示されてアニメーション化されると、メイン スレッドがブロックされていてもアニメーション化を続けます。

ただし、長い操作が保留されているため、実行ループがまだ実行されていないため、ビューが表示される可能性はありません。

performSelector:withObject:afterDelayしたがって、必要なのは遅延 0だけだと思う​​ので、長い操作はキューに入れられ、インジケーターが表示された後に実行されます。

于 2010-11-22T17:24:48.033 に答える