2010-05-04 3 views
0

J'ai un IBAction avec un code simple à l'intérieur:Dans Cocoa, pourquoi un champ de texte n'apparaîtra-t-il qu'après l'exécution complète de IBAction?

-(IBAction)change:(id)sender { 
    [textfield setHidden:NO]; 
    [self dolengthyaction]; 
} 

« textfield » est un NSTextField dans un fichier nib, et -'dolengthyaction » est une fonction qui prend environ une minute pour terminer l'exécution.

Ma question est: Pourquoi le champ de texte n'est-il pas affiché tant que l'opération "dolengthyaction" n'est pas terminée? Je veux qu'il soit révélé avant que la campagne de dolengthy commence à prendre place. Est-ce un problème inhérent ou y at-il quelque chose qui ne va pas avec mon code? (ou dans une autre partie de mon code?)

Je ne suis toujours pas très bon en programmation donc je m'excuse si j'ai mal formulé quelque chose et formulé quelque chose de mal.

EDIT: Il n'y a pas grand-chose d'autre que ce IBAction et -dolengthyaction ...

-(void)doLengthyAction { 
    sleep(10); 
} 
-(IBAction)change:(id)sender { 
    [textfield setHidden:NO]; 
    [self doLengthyAction]; 
    [textfield setHidden:YES]; 
} 

Tout ce que je veux vraiment faire est afficher l'étiquette lorsque l'action est en cours d'exécution et de le cacher quand il est fait .

Fondamentalement, cela signifie qu'il ne s'affiche pas du tout pour le moment.

En réalité, dans -doLengthyAction ce n'est pas sleep (10) mais plutôt une opération NSFileManager qui copie environ 50 Mo de matériel. Le code était plutôt long, mais si vous voulez que je l'affiche je peux. Je l'ai testé avec sleep() mais ça ne marche pas non plus.

+0

Je ne suis pas sûr de répondre à choisir comme « la solution », car ils ont tous l'expliquer ... – Vervious

+0

Soit de drawonward ou de réponses de wbyoung. L'exemple de wbyoung est vraiment sympa aussi, mais ça ne marchera que sur 10.6. – JeremyP

Répondre

6

Toutes les opérations de dessin (y compris la clandestinité et montrant des vues) sont déclenchés à partir de la boucle d'exécution. La boucle d'exécution ne peut pas effectuer l'événement suivant avant le retour de votre fonction.

Si vous avez une opération qui prend plus d'une seconde, vous devez l'exécuter dans un thread. Une fois l'opération terminée, utilisez performSelectorOnMainThread pour modifier l'interface utilisateur sur le thread principal.

+0

Je vois. Cela l'explique aussi. – Vervious

+0

La réponse ci-dessus est légèrement trompeuse. Vous pouvez forcer le dessin à se produire en dehors de la boucle d'exécution en appelant simplement [affichage de la fenêtre]. – Leibowitzn

+0

@Liebowitzn Je pensais iPhone, ce qui vous limite au dessin passif. Bonne prise. – drawnonward

1

Je pense qu'il y a un problème avec le reste de votre code. Cela ne devrait pas arriver.

En savoir plus?

1

ne peux pas dire ce qui est transpirant exactement à la recherche au reste du code, mais une façon hackish serait d'essayer:

-(IBAction)change:(id)sender { 
    [textfield setHidden:NO]; 
    [self performSelector:@selector(doLenghtyAction) withObject:nil afterDelay:0.1]; --> or maybe even 0.0 
} 
+0

C'est une suggestion intéressante que j'utiliserais dans le futur mais j'essaye de cacher le champ de texte dès que la chose longue est exécutée. J'ai essayé votre suggestion et elle cache à nouveau le champ de texte à peu près immédiatement si je colle un [textfield setHidden: YES] à la fin de celui-ci. :) – Vervious

+0

Oh, c'est vrai. Peu importe mon commentaire. Horace a eu une réponse similaire qui a résolu le problème de mon premier commentaire. +1 cependant: D – Vervious

1
-(void)doLengthyAction { 
    sleep(10); 
    [textfield setHidden:YES]; 
} 

-(IBAction)change:(id)sender { 
    [textfield setHidden:NO]; 
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doLengthyAction) userInfo:nil repeats:NO]; 
} 
+1

+1 Bien contourné, il serait bon de savoir pourquoi l'original ne fonctionne pas. – Vervious

+0

l'original: setHidden> doLengthyAction> setHidden n'autorise aucun intervalle de temps pour l'actualisation de l'interface utilisateur. l'état du champ de texte est en effet changé deux fois. Cependant, le premier changement n'a pas été actualisé à l'écran. lorsque l'interface utilisateur a une chance de rafraîchir l'écran, le champ de texte est déjà au 2ème état. – ohho

+0

Je vois. Donc ce n'est pas ma faute, c'est juste une chose inhérente à Cocoa. Je vois. : D merci! – Vervious

5

Comme mentionné dans certaines des réponses précédentes, l'application doit retourner à la boucle d'exécution principale avant qu'elle ne soit redessinée (c'est une optimisation pour éviter de redessiner lorsque vous apportez plusieurs modifications).

Vous devriez vraiment gérer les choses sur un thread d'arrière-plan si elles doivent fonctionner pendant une longue période. Si vous ne le faites pas, l'interface utilisera le ballon de plage pendant que votre opération est en cours.

Si vous ciblez 10.6+, vous pouvez utiliser GCD et blocs comme suit pour exécuter les choses facilement en arrière-plan (et ne pas avoir à définir plusieurs méthodes pour faire avancer les choses):

-(void)doLengthyAction { 
    sleep(10); 
} 
-(IBAction)change:(id)sender { 
    [textfield setHidden:NO]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     [self doLengthyAction]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [textfield setHidden:YES]; 
     }); 
    }); 
} 

Bien sûr en l'utilisant, vous devrez vous assurer que ce qui se passe dans la longue action est sûr pour les threads, mais vous obtiendrez une meilleure expérience utilisateur. Pour plus d'informations sur ce type de code CGD, vous pouvez vouloir read this.

0

Essayez ceci:

-(IBAction)change:(id)sender { 
    [textfield setHidden:NO]; 
    [[textfield window] display]; // force the window to update 
    [self doLengthyAction]; 
    [textfield setHidden:YES]; 
} 
Questions connexes