Pendant qu'un UIScrollView
(ou une classe dérivée de celui-ci) défile, il semble que tous ceux NSTimers
qui sont en cours d'exécution soient suspendus jusqu'à ce que le défilement soit terminé.
Y a-t-il un moyen de contourner ceci? Des discussions? Un établissement de priorité? N'importe quoi?
ios
uiscrollview
nstimer
mcccclean
la source
la source
Réponses:
Une solution facile et simple à mettre en œuvre consiste à:
NSTimer *timer = [NSTimer timerWithTimeInterval:... target:... selector:.... userInfo:... repeats:...]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
la source
Pour tous ceux qui utilisent Swift 3
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: aSelector, userInfo: nil, repeats: true) RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
la source
timer = Timer(timeInterval: 0.1, target: self, selector: aSelector, userInfo: nil, repeats: true)
comme première commande au lieu deTimer.scheduleTimer()
, carscheduleTimer()
ajoute un minuteur à la boucle d'exécution, et l'appel suivant est un autre ajout à la même boucle d'exécution mais avec un mode différent. Ne faites pas le même travail deux fois.Oui, Paul a raison, c'est un problème de boucle d'exécution. Plus précisément, vous devez utiliser la méthode NSRunLoop:
- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
la source
Ceci est la version rapide.
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true) NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
la source
Vous devez exécuter un autre thread et une autre boucle d'exécution si vous voulez que les minuteries se déclenchent pendant le défilement; puisque les minuteries sont traitées dans le cadre de la boucle d'événements, si vous êtes occupé à traiter le défilement de votre vue, vous ne vous déplacez jamais vers les minuteries. Bien que la pénalité de performance / batterie liée à l'exécution de minuteries sur d'autres threads ne vaut pas la peine d'être traitée dans ce cas.
la source
pour quiconque utilise Swift 4:
timer = Timer(timeInterval: 1, target: self, selector: #selector(timerUpdated), userInfo: nil, repeats: true) RunLoop.main.add(timer, forMode: .common)
la source
tl; dr le runloop effectue un défilement afin qu'il ne puisse plus gérer d'événements - à moins que vous ne régliez manuellement le minuteur pour qu'il puisse également se produire lorsque runloop gère des événements tactiles. Ou essayez une autre solution et utilisez GCD
Une lecture incontournable pour tout développeur iOS. Beaucoup de choses sont finalement exécutées via RunLoop.
Dérivé de la documentation d' Apple .
Qu'est-ce qu'une boucle de course?
Comment la livraison des événements est-elle perturbée?
Que se passe-t-il si le minuteur est déclenché lorsque la boucle d'exécution est en cours d'exécution?
Cela arrive BEAUCOUP DE FOIS, sans que nous nous en rendions compte. Je veux dire que nous avons réglé la minuterie pour qu'elle se déclenche à 10: 10: 10: 00, mais la boucle d'exécution exécute un événement qui prend jusqu'à 10: 10: 10: 05, donc la minuterie est déclenchée 10: 10: 10: 06
Le défilement ou tout autre élément qui maintient la boucle de course occupée changerait-il toutes les fois que ma minuterie va se déclencher?
Comment puis-je changer le mode des RunLoops?
Vous ne pouvez pas. Le système d'exploitation se modifie tout seul pour vous. Par exemple, lorsque l'utilisateur appuie sur, le mode passe à
eventTracking
. Lorsque l'utilisateur appuie sur terminé, le mode revient àdefault
. Si vous voulez que quelque chose soit exécuté dans un mode spécifique, c'est à vous de vous assurer que cela se produit.Solution:
Lorsque l'utilisateur fait défiler, le mode Run Loop devient
tracking
. Le RunLoop est conçu pour changer de vitesse. Une fois que le mode est défini sureventTracking
, il donne la priorité (rappelez-vous que nous avons des cœurs CPU limités) pour toucher les événements. Il s'agit d'une conception architecturale des concepteurs du système d'exploitation .Par défaut, les minuteries ne sont PAS programmées sur le
tracking
mode. Ils sont programmés le:Le
scheduledTimer
dessous fait ceci:RunLoop.main.add(timer, forMode: .default)
Si vous voulez que votre minuterie fonctionne lors du défilement, vous devez faire soit:
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true) // sets it on `.default` mode RunLoop.main.add(timer, forMode: .tracking) // AND Do this
Ou faites simplement:
RunLoop.main.add(timer, forMode: .common)
En fin de compte, faire l'une des choses ci-dessus signifie que votre thread n'est pas bloqué par les événements tactiles. ce qui équivaut à:
RunLoop.main.add(timer, forMode: .default) RunLoop.main.add(timer, forMode: .eventTracking) RunLoop.main.add(timer, forMode: .modal) // This is more of a macOS thing for when you have a modal panel showing.
Solution alternative:
Vous pouvez envisager d'utiliser GCD pour votre minuterie, ce qui vous aidera à «protéger» votre code des problèmes de gestion de la boucle d'exécution.
Pour ne pas se répéter, utilisez simplement:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { // your code here }
Pour les minuteries répétitives, utilisez:
Découvrez comment utiliser DispatchSourceTimer
Creuser plus profondément à partir d'une discussion que j'ai eue avec Daniel Jalkut:
Question: comment GCD (threads d'arrière-plan), par exemple un asyncAfter sur un thread d'arrière-plan, est-il exécuté en dehors du RunLoop? Je crois comprendre que tout doit être exécuté dans un RunLoop
Pas nécessairement - chaque thread a au plus une boucle d'exécution, mais peut avoir zéro s'il n'y a aucune raison de coordonner l'exécution "propriété" du thread.
Les threads sont une offre au niveau du système d'exploitation qui donne à votre processus la possibilité de répartir ses fonctionnalités sur plusieurs contextes d'exécution parallèles. Les boucles d'exécution sont une capacité au niveau de l'infrastructure qui vous permet de diviser davantage un seul thread afin qu'il puisse être partagé efficacement par plusieurs chemins de code.
En règle générale, si vous distribuez quelque chose qui s'exécute sur un thread, il n'aura probablement pas de boucle d'exécution à moins que quelque chose n'appelle
[NSRunLoop currentRunLoop]
qui en créerait implicitement une.En un mot, les modes sont essentiellement un mécanisme de filtre pour les entrées et les minuteries
la source