2009-07-29 6 views
1

J'ai un contrôle segmenté utilisé comme bascule. Quand on bascule, je change un tas de contenus dans une vue de table qui prend un temps minuscule mais perceptible (insérer/supprimer des sections dans la vue tableau, animer le changement, etc.). Je veux que le contrôle segmenté réponde immédiatement à la sélection. Donc, dans mon action code de gestion pour l'événement UIControlEventValueChanged du contrôle segmentée, je fais ce qui suit:Maintien d'un UISegmentedControl (parmi d'autres) sensible

- (IBAction)groupingChanged:(id)sender { 
    UISegmentedControl *seg = sender; 
    [tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex]; 
    [self performSelectorOnMainThread:@selector(updateGrouping) 
          withObject:nil 
         waitUntilDone:NO]; 
} 

updateGrouping est:

- (void)updateGrouping { 
    MXAssertMainThread(); 

    [tableView beginUpdates]; 
    ... several table updates 
    [tableView endUpdates]; 
} 

Réglage waitUntilDone:NO permet la méthode groupingChanged avant l'interruption updateGrouping est appelée, mais cela ne semble pas suffisant pour repeindre la vue. Le contrôle segmenté colle jusqu'à ce que la table soit mise à jour, puis elle bascule.

J'ai donc essayé de modifier groupingChanged: pour créer un fil pour la mise à jour comme ceci:

- (void)delayed { 
    [self performSelectorOnMainThread:@selector(updateGrouping) 
          withObject:nil 
         waitUntilDone:NO]; 
} 

- (IBAction)groupingChanged:(id)sender { 
    UISegmentedControl *seg = sender; 
    [tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex]; 
    [self performSelectorInBackground:@selector(delayed) withObject:nil]; 
} 

Et cela fonctionne. Le contrôle segmenté bascule instantanément et la table suit rapidement. Mais je ne suis pas du tout confiant du résultat. Est-ce simplement un effet secondaire de donner un répit au thread principal pendant le démarrage du nouveau thread? Est-ce juste comment j'ai besoin de mettre en file d'attente des mises à jour à l'interface utilisateur? C'est clairement hacky. J'espère que quelqu'un a un meilleur modèle qu'ils suivent pour cette situation.

Répondre

1

Si vous voulez juste vous assurer que le contrôle segmenté est repeint rapidement, je ne voudrais pas plonger dans les threads. Au lieu de cela, je voudrais juste définir une minuterie avec une valeur faible comme 0.1, et cela devrait être suffisant pour mettre le contrôle à jour sans retard notable pour l'utilisateur.

Je l'ai utilisé quand j'ai beaucoup de travail à faire mais j'ai besoin d'une mise à jour rapide de l'interface utilisateur.

Encore un peu "hacky", mais sans l'introduction de threads.

Alors ...

- (IBAction)groupingChanged:(id)sender { 
    UISegmentedControl *seg = sender; 
    [tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex]; 
    [NSTimer scheduledTimerWithTimeInterval:0.1 
            target:self 
            selector:@selector(updateGrouping) 
            userInfo:nil 
            repeats:NO];  

} 
+0

Cela élimine le thread provisoire, donc c'est une amélioration, mais je refuse de croire qu'il n'y a pas de motif commun non-hacky. –

+0

performSelector: withObject: afterDelay est également valide comme le souligne Kendall. Ce problème est que la boucle d'exécution doit être terminée pour permettre à l'interface utilisateur de se mettre à jour. Ce n'est pas vraiment un hack, mais juste en reconnaissant ce fait. De toute façon, je ne connais pas de mise à jour de l'interface utilisateur sans permettre au thread principal de suivre son cours. –

+0

Bien sûr, je réalise la cause. Je suppose que j'espérais un modèle commun pour cela. Crédit accordé (tardif). –

2

penser de cette façon - tout ce que vous faites est généralement fait sur le thread principal, y compris les mises à jour de l'interface utilisateur. Par conséquent, dans votre code d'origine, le code qui a mis à jour la vue de table a été atteint avant le code qui a mis à jour l'interface utilisateur sur le segment. Donc, vous aviez raison de donner plus de temps au thread principal pour terminer les mises à jour de l'interface utilisateur, car le thread principal était autorisé à terminer les mises à jour de l'interface utilisateur pendant que le thread d'arrière-plan traitait les mises à jour.

Une alternative que vous pouvez essayer est d'utiliser performSelector: withObject: afterDelay avec un délai de 0.0 (qui permet au thread principal de traiter d'autres choses avant de procéder avec le sélecteur). Cela peut fonctionner là où performSelectorOnMainThread ne l'a pas fait, car cet appel peut être plus immédiat même s'il finit par faire quelque chose de très similaire.

0

J'ai rencontré le même problème et l'ai résolu en sous-classant UISegmentedControl pour créer un UISegmentedControl retardé.

Dans le contrôle différé, j'ai ignoré addTarget:action:forControlEvents: pour capturer l'action & cible. Ensuite, lorsque l'événement de segment se produit, je lance un NSTimer pour lancer l'action & cible capturée après un délai défini. Le résultat est que l'interface utilisateur est mis à jour pour afficher le segment cliqué et je peux utiliser le DelayedUISegmentedControl comme je le ferais un UISegmentedControl:

// Follows all normal initialization patterns of UISegmentedControl 
UISegmentedControl *segmentedControl = [[DelayedUISegmentedControl alloc] 
    initWithItems:[NSArray arrayWithObjects: @"First", @"Second", nil]]; 

// Adds a delay to the selector; default is 0.25 
[segmentedControl addTarget:self action:@selector(segmentAction:) 
    forControlEvents:UIControlEventValueChanged]; 

Si vous voulez télécharger le contrôle que j'ai ouvert le code source sur google code.

Questions connexes