Domanda

È da molto tempo che provo a giocarci e non riesco a trovare l'approccio migliore.Mi sto confondendo perché sembrano esserci risposte/opinioni diverse su come eseguire questo compito apparentemente semplice.

Voglio essere in grado di avere una classe riutilizzabile chiamata ActivityIndicatorController.Questo controller ha due metodi principali:activateIndicator e deactivateIndicator.Richiede un UIView come argomento/proprietà nonché una NSString per un'etichetta.Dopo l'attivazione, disattiverà l'interazione dell'utente in UIView e aggiungerà una visualizzazione secondaria rettangolare (con alfa e angoli arrotondati), un controllo UIActivityIndicator e un UILabel per il testo di stato.Ciò è auspicabile perché in questo modo non è necessario disporre di codice UIActivityIndicatorView personalizzato in ciascun controller di visualizzazione o impostare un ActivityIndicator in ciascun NIB.

Il problema che sto riscontrando fondamentalmente è come avviare questo processo di aggiunta e animazione di ActivityIndicator.Alcuni metodi che ho provato non mostrano affatto la nuova vista.Altri funzionano, ma ActivityIndicator non si anima.

Ho provato a utilizzare [NSThread detachNewThreadSelector:@selector(startAnimating) toTarget:activityIndicator withObject:nil] all'interno del metodoactivateIndicator, ma questo non visualizza il nuovo UIView.

Ho provato a utilizzare [NSThread detachNewThreadSelector:@selector(activateIndicator) toTarget:activityIndicatorController withObject:nil] da un metodo chiamante, ma questo metterebbe l'intera creazione del nuovo UIView in un thread separato.

Ora alla domanda:

Parte 1:Capisco che tutta l'interfaccia utente dovrebbe essere gestita nel thread principale, è corretto?

Parte 2:Qual è la differenza/vantaggio/svantaggio dell'utilizzo di [NSThread detachThreadSelector] rispetto a NSOperation?

Parte 3:È meglio:

(a) invia l'operazione lunga a un nuovo thread in background con una richiamata al thread principale OR

(b) invia il metodo UIActivityIndicatorView startAnimating a un thread separato ed esegui il lungo processo sul thread principale

E perché?

Ecco il mio codice attuale:

Classe 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];
}

Codice di chiamata da più punti nell'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;
È stato utile?

Soluzione

Parte 1:Capisco che tutta l'interfaccia utente dovrebbe essere gestita nel thread principale, è corretto?

Corretto.

Parte 2:Qual è la differenza/vantaggio/svantaggio dell'utilizzo di [NSThread detachThreadSelector] rispetto a NSOperation?

NSOperation è un'interfaccia di livello superiore che consente di accodare operazioni, creare diverse operazioni che dipendono l'una dall'altra, ecc.Altre opzioni per lavorare con le attività in background sono performSelectorOnMainThread:.../performSelectorInBackground:... e spedizione Grand Central.

Parte 3:È meglio:

(a) invia l'operazione lunga a un nuovo thread in background con una richiamata al thread principale OR

(b) invia il metodo UIActivityIndicatorView startAnimating a un thread separato ed esegui il lungo processo sul thread principale

A causa della risposta alla domanda 1, (a) è la tua unica opzione.

Altri suggerimenti

Metti il ​​tuo lungo lavoro in un thread separato, in questo modo non blocca completamente l'interfaccia utente nel caso in cui sia necessaria qualche interazione (ad esempio per annullare l'operazione).Quindi, il tuo ActivityIndicatorController dovrebbe chiamare il thread principale per eseguire tutte le operazioni dell'interfaccia utente, ad esempio:

@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.

Una volta visualizzato e animato, l'indicatore di attività continuerà ad animarsi anche se il thread principale è bloccato.

Tuttavia, la vista non ha alcuna possibilità di essere visualizzata perché il ciclo di esecuzione deve ancora essere eseguito perché è in attesa di un'operazione lunga.

Quindi penso che quello di cui hai bisogno sia giusto performSelector:withObject:afterDelay con ritardo 0, quindi la tua lunga operazione verrà messa in coda ed eseguita dopo che il tuo indicatore sarà diventato visibile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top