Gestion multi thread / rafraichir le view

cyril94440cyril94440 Membre
mars 2013 modifié dans Apple Developer Programs #1
Bonjour,



J'ai un ViewController dans lequel je veux faire une espèce d'animation bidon (c'est plus pour comprendre le fonctionnement et les Best Practices).



Donc voilà  le principe : un UIViewController dans lequel je veux faire bouger des dés.

Pour ca, j'ai une classe DesViewController avec une vue tout simple 50x50, qui contient un label.



Le but de l'animation est de faire une boucle qui affiche une dizaine de fois un chiffre entre 1 et 6 dans sa vue.



Pour ca j'ai essayé de faire des performSelectorInBackground , mais je ne sais pas comment faire proprement.



Voilà  à  quoi ressemble l'appel dans mon UIViewController (sachant que la vue des dés est ajoutée dans le viewDidLoad via addSubview) (1 vue par dé)


<br />
[font=Menlo][size=2]<br />
- ([color=#bb2ca2]IBAction[/color])lancerDesButtonPressed:([color=#bb2ca2]id[/color])sender {[/size][/font][font=Menlo][size=2]<br />
    [color=#bb2ca2]int[/color] total = [color=#272ad8]0[/color];[/size][/font]<br />
[font=Menlo][size=2]<br />
    [color=#bb2ca2]for[/color]([color=#bb2ca2]int[/color] x=[color=#272ad8]0[/color];x&lt;[[color=#4f8187]_dices[/color] [color=#3d1d81]count[/color]];x++)[/size][/font][font=Menlo][size=2]<br />
    {[/size][/font][color=#4F8187][font=Menlo][size=2]<br />
[color=#000000]        [/color]DesViewController[color=#000000] *des = [/color]_dices[color=#000000][x];[/color][/size][/font][/color][font=Menlo][size=2]<br />
        total += des.[color=#31595d]launchDice[/color];[/size][/font][font=Menlo][size=2]<br />
    }[/size][/font]<br />
[color=#4F8187][font=Menlo][size=2]<br />
[color=#000000]    [/color]_lancerDesButton[color=#000000].[/color][color=#703daa]hidden[/color][color=#000000] = [/color][color=#bb2ca2]YES[/color][color=#000000];[/color][/size][/font][/color][color=#4F8187][font=Menlo][size=2]<br />
[color=#000000]    [/color]_caracSuivanteButton[color=#000000].[/color][color=#703daa]hidden[/color][color=#000000] = [/color][color=#bb2ca2]NO[/color][color=#000000];[/color][/size][/font][/color][font=Menlo][size=2]<br />
}[/size][/font]<br />




et voici ma classe DesViewController



HEADER


<br />
#import &lt;UIKit/UIKit.h&gt;<br />
@interface DesViewController : UIViewController<br />
{<br />
  <br />
	__weak IBOutlet UILabel *_number;<br />
}<br />
- (NSInteger)launchDice;<br />
@end<br />




M


<br />
- (void)viewDidLoad<br />
{<br />
	[super viewDidLoad];<br />
	// Do any additional setup after loading the view from its nib.<br />
}<br />
- (NSInteger)launchDice<br />
{<br />
  <br />
	int x = 0;<br />
	for(int i=0;i&lt;30;i++)<br />
	{<br />
		x = arc4random() % 6;<br />
		x+=1;<br />
  <br />
		[self performSelectorInBackground:@selector(refreshViewTest:) withObject:[NSNumber numberWithInt:x]];<br />
	  <br />
		[NSThread sleepForTimeInterval:0.1];<br />
	}<br />
  <br />
	return x;<br />
}<br />
- (void)refreshViewTest:(NSNumber*)x<br />
{<br />
	_number.text = [NSString stringWithFormat:@&quot;%@&quot;,x];<br />
}<br />
- (void)didReceiveMemoryWarning<br />
{<br />
	[super didReceiveMemoryWarning];<br />
	// Dispose of any resources that can be recreated.<br />
}<br />




Comme c'est fait ici , le label du dé se rafraichit bien , seulement d'abord pour le premier dé puis après pour le deuxième dé.



J'ai essayé un performSelectorInBackground dans le UIViewController pour déclencher l'anim des dés , mais comme je dois récupérer les valeurs des dés depuis l'appel de la méthode je ne sais pas vraiment comment faire (ajouter un attribut à  mon UIViewController) ? en tout cas ca plante même sans récupérer la valeur.

Bref, quelle est la meilleur façon ??



Merci d'avance !

Réponses

  • C'est bien les launchDice qui doivent être envoyés tous en même temps. Sinon effectivement ils sont animés les uns après les autres.



    Il y a plusieurs façons de récupérer le résultat à  la fin.

    A priori le plus simple serait de lancer les dés dans une même queue dispatch permettant une exécution en parallèle pour tous les dés. Puis d'attendre la fin d'exécution de cette queue pour demander leur valeur aux dés.
  • Merci de ta reponse !

    J'ai essayé de faire un performSelectorInBackground:@selector(launchdice:)



    mais l'appli plante :S
  • On ne peut pas utiliser performSelectorInBackgroung avec une méthode qui retourne une valeur.
  • On ne rafraà®chi jamais l'UI dans un autre thread que le thread principal.
  • 'Ceetix' a écrit:


    On ne rafraà®chi jamais l'UI dans un autre thread que le thread principal.


    Aussi, mais je ne suis pas sûr que ça provoque un plantage.
  • AliGatorAliGator Membre, Modérateur
    mars 2013 modifié #7
    Si ça peut.



    Toute utilisation d'une casse non-thread-safe hors de son thread pour laquelle il a été prévu peut provoquer un plantage.



    Utiliser un NSDateFormatter créé sur un thread dans un autre thread, ou l'utiliser de façon concurrente sur plusieurs threads, et tu auras un plantage assuré genre 1 fois sur 3 (et j'ai mis du temps à  tilter que c'était pour ça)



    Utiliser des méthodes qui agissent sur l'UI, et manipuler des éléments de l'UI, en dehors du main thread peut aussi risquer des plantages
  • C'est bon à  savoir.
  • Du coup, comment faire pour rafraichir l'UI au milieu d'une boucle par exemple ??
  • AliGatorAliGator Membre, Modérateur
    Ca sert à  rien de "rafraà®chir l'UI au milieu d'une boucle" de traitement.

    La RunLoop d'un programme (et d'ailleurs en général c'est vrai quel que soit l'OS le langage et le Runtime, tous les OS graphiques ont à  peu près ce même système) consiste à  :

    - Traiter les données en attente en entrée (clic/tap, entrée clavier, signal reçu, déclencheur type timer, données réseau...)

    - Déclencher les événements associés à  ces données (IBAction, appel de méthode associée à  une notification, etc...)

    - Faire la boucle de rendu puis de dessin à  l'écran (UI)

    - Recommencer



    Bon c'est simplifié mais en gros c'est ça.



    Donc on ne dessine pas l'UI quand ça nous chante, c'est la RunLoop qui demande aux objets de se dessiner au moment où elle est dans la phase de rendu. Nous ce qu'on peut juste faire c'est signaler quels éléments sont à  redessiner (via [... setNeedsDisplay:YES] par exemple) s'il y a des éléments pour lesquels il faudra que la RunLoop appelle de nouveau la méthode "drawRect:". Ou changer la valeur des éléments de l'UI (le text d'un UILabel, etc) dans le main thread, qui s'actualisera quand la RunLoop fera sa passe de rendu de l'UI.



    Si tu veux par exemple changer plein de fois le texte d'un UILabel en boucle pour faire un effet "dé qui défile", le mieux est d'utiliser un NSTimer pour déclencher (disons toutes les 0.1s ou 0.05s par exemple) une méthode (qui va être exécutée dans le Main Thread quand la RunLoop va déclencher le Timer) qui va changer la valeur du label toutes les 0.1 ou 0.05s.
Connectez-vous ou Inscrivez-vous pour répondre.