UIScrollView स्क्रॉल खत्म होने तक NSTimer को रोक देता है


84

जबकि UIScrollView(या एक व्युत्पन्न वर्ग) स्क्रॉल कर रहा है, ऐसा लगता है जैसे NSTimersकि सभी समाप्त हो रहे हैं जब तक स्क्रॉल समाप्त नहीं हो जाता है।

क्या इस से निकाल पाने के लिए कोई तरीका है? धागे? एक प्राथमिकता सेटिंग? कुछ भी?



सात साल बाद ... stackoverflow.com/a/12625429/294884
Fattie

जवाबों:


201

समाधान को लागू करने के लिए एक आसान और सरल तरीका है:

NSTimer *timer = [NSTimer timerWithTimeInterval:... 
                                         target:...
                                       selector:....
                                       userInfo:...
                                        repeats:...];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

2
UITrackingRunLoopMode मोड है - और इसके सार्वजनिक - आप विभिन्न टाइमर व्यवहार बनाने के लिए उपयोग कर सकते हैं।
टॉम एंडरसन

इसे प्यार करें, GLKViewController के साथ एक आकर्षण की तरह काम करता है। जब UIScrollView प्रकट होता है और वर्णित के रूप में अपना खुद का टाइमर शुरू करें, बस यस को कंट्रोल करें। स्क्रॉलव्यू खारिज होने पर इसे उलट दें और इसे सही करें! मेरा अपडेट / रेंडर लूप बहुत महंगा नहीं है, जिससे मदद मिल सके।
जीरो बुमा

3
बहुत बढ़िया! क्या मुझे अमान्य होने पर टाइमर को हटाना होगा या नहीं? धन्यवाद
जैकोपो Penzo

यह स्क्रॉल करने के लिए काम करता है लेकिन ऐप जब बैकग्राउंड में चला जाता है तो टाइमर बंद कर देता है। दोनों को प्राप्त करने के लिए कोई समाधान?
पुलकित शर्मा

23

स्विफ्ट 3 का उपयोग करने वाले किसी के लिए भी

timer = Timer.scheduledTimer(timeInterval: 0.1,
                            target: self,
                            selector: aSelector,
                            userInfo: nil,
                            repeats: true)


RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)

7
इसके timer = Timer(timeInterval: 0.1, target: self, selector: aSelector, userInfo: nil, repeats: true)बजाय पहले कमांड के रूप में कॉल करना बेहतर होता है Timer.scheduleTimer(), क्योंकि scheduleTimer()टाइमर को रनलूप में जोड़ा जाता है, और अगली कॉल उसी रनअप में एक और जोड़ रही है लेकिन अलग मोड के साथ। एक ही काम दो बार मत करो।
एक्सीड ब्राइट

8

हाँ, पॉल सही है, यह एक रन लूप मुद्दा है। विशेष रूप से, आपको NSRunLoop विधि का उपयोग करने की आवश्यकता है:

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode

7

यह स्विफ्ट वर्जन है।

timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
            NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)

6

यदि आप स्क्रॉल करते समय फायर करना चाहते हैं तो आपको एक और धागा और दूसरा रन लूप चलाना होगा; चूंकि घटना पाश के हिस्से के रूप में टाइमर संसाधित होते हैं, यदि आप अपने दृश्य स्क्रॉल करने में व्यस्त हैं, तो आप कभी भी टाइमर के आसपास नहीं पहुंचते हैं। यद्यपि अन्य थ्रेड्स पर चलने वाले टाइमर की पूर्ण / बैटरी जुर्माना इस मामले को संभालने के लायक नहीं हो सकती है।


11
इसके लिए एक और सूत्र की आवश्यकता नहीं है, काशिफ के हालिया उत्तर को देखें । मैंने इसकी कोशिश की और यह बिना किसी अतिरिक्त धागे के काम करता है।
progrmr

3
गौरैया पर तोपों की शूटिंग। एक धागे के बजाय, सही रन लूप मोड में टाइमर को शेड्यूल करें।
uliwitness

2
मुझे लगता है कि काशीफ़ का उत्तर नीचे का सबसे अच्छा उत्तर है क्योंकि इस मुद्दे को ठीक करने के लिए आपको केवल 1 पंक्ति का कोड जोड़ना होगा।
डेमियन मर्फी

3

किसी के लिए भी स्विफ्ट 4 का उपयोग करें:

    timer = Timer(timeInterval: 1, target: self, selector: #selector(timerUpdated), userInfo: nil, repeats: true)
    RunLoop.main.add(timer, forMode: .common)

1

tl; dr रनलूप स्क्रॉल कर रहा है ताकि यह किसी भी अधिक घटनाओं को संभाल न सके - जब तक कि आप मैन्युअल रूप से टाइमर सेट न करें ताकि यह तब भी हो सके जब रनअप टच घटनाओं को संभाल रहा हो। या एक वैकल्पिक समाधान का प्रयास करें और GCD का उपयोग करें


किसी भी iOS डेवलपर के लिए पढ़ना चाहिए। रनऑलॉप के माध्यम से अंततः बहुत सी चीजों को अंजाम दिया जाता है।

Apple के डॉक्स से व्युत्पन्न ।

एक रन लूप क्या है?

एक रन लूप बहुत पसंद है इसका नाम लगता है। यह एक लूप है जो आपके धागे में प्रवेश करता है और आने वाली घटनाओं के जवाब में इवेंट हैंडलर चलाने के लिए उपयोग करता है

घटनाओं का वितरण कैसे बाधित होता है?

क्योंकि जब आप रन लूप चलाते हैं, तो टाइमर और अन्य आवधिक घटनाओं को वितरित किया जाता है, उस लूप को दरकिनार करने से उन घटनाओं का वितरण बाधित होता है। इस व्यवहार का विशिष्ट उदाहरण तब होता है जब आप लूप दर्ज करके और बार-बार आवेदन से घटनाओं का अनुरोध करके माउस-ट्रैकिंग रूटीन को लागू करते हैं। क्योंकि आपका कोड सीधे घटनाओं को हड़प रहा है, इसलिए एप्लिकेशन को उन घटनाओं को सामान्य रूप से भेजने देने के बजाय, सक्रिय माउस आपके माउस-ट्रैकिंग रूटीन से बाहर निकलने और आवेदन पर नियंत्रण वापस करने तक फायर करने में असमर्थ होगा।

यदि रन लूप निष्पादन के बीच में है तो टाइमर को निकाल दिया जाए तो क्या होगा?

यह कभी भी, हमारे लिए बिना सूचना के बहुत कुछ होता है। मेरा मतलब है कि हम टाइमर को 10: 10: 10: 00 पर सेट करते हैं, लेकिन रनअप एक घटना को अंजाम दे रहा है जो 10: 10: 10: 05 तक होती है, इसलिए टाइमर को 10: 10: 10: 06 निकाल दिया जाता है।

इसी तरह, अगर एक टाइमर जब हैंडलर रूटीन को निष्पादित करने के बीच में चलता है, तो टाइमर फायर करता है, टाइमर अपने हैंडलर रूटीन को लागू करने के लिए रन लूप के माध्यम से अगली बार इंतजार करता है। यदि रन लूप बिल्कुल नहीं चल रहा है, तो टाइमर कभी भी फायर नहीं करता है।

स्क्रॉलिंग या कुछ भी हो सकता है जो रनअप को हर समय व्यस्त रखता है, जब मेरा टाइमर आग लगने वाला हो?

आप केवल एक बार या बार-बार ईवेंट उत्पन्न करने के लिए टाइमर कॉन्फ़िगर कर सकते हैं। एक दोहराई जाने वाली टाइमर अपने आप शेड्यूल किए गए फायरिंग समय के आधार पर स्वचालित रूप से फिर से शुरू होती है, न कि वास्तविक फायरिंग समय के आधार पर। उदाहरण के लिए, यदि एक टाइमर को एक विशेष समय पर और उसके बाद हर 5 सेकंड में आग लगने के लिए निर्धारित किया जाता है, तो निर्धारित गोलीबारी का समय हमेशा मूल 5 सेकंड के अंतराल पर गिर जाएगा, भले ही वास्तविक गोलीबारी का समय देरी हो। यदि फायरिंग के समय में इतनी देरी हो जाती है कि यह निर्धारित फायरिंग के समय में से एक या अधिक बार छूट जाता है, तो टाइमर को केवल एक बार छूटी हुई समयावधि के लिए निकाल दिया जाता है। मिस्ड अवधि के लिए फायरिंग के बाद, टाइमर को अगले निर्धारित फायरिंग समय के लिए पुनर्निर्धारित किया जाता है।

मैं RunLoops के मोड को कैसे बदल सकता हूं?

आप नहीं कर सकते। ओएस सिर्फ आपके लिए खुद को बदलता है। जैसे जब उपयोगकर्ता टैप करता है, तब मोड स्विच हो जाता है eventTracking। जब उपयोगकर्ता नल समाप्त हो जाते हैं, तो मोड वापस चला जाता है default। यदि आप चाहते हैं कि कुछ विशिष्ट मोड में चलाया जाए, तो यह आपके लिए सुनिश्चित है कि ऐसा होता है।


समाधान:

जब उपयोगकर्ता स्क्रॉल कर रहा है तो रन लूप मोड बन जाता है tracking। RunLoop को गियर बदलने के लिए डिज़ाइन किया गया है। एक बार जब मोड सेट हो जाता है eventTracking, तो यह घटनाओं को छूने के लिए प्राथमिकता देता है (याद रखें कि हमारे पास सीमित सीपीयू कोर है)। यह ओएस डिजाइनरों द्वारा एक वास्तुशिल्प डिजाइन है

डिफ़ॉल्ट रूप से टाइमर trackingमोड पर शेड्यूल नहीं किए जाते हैं । वे इस पर निर्धारित हैं:

एक टाइमर बनाता है और इसे डिफ़ॉल्ट मोड में वर्तमान रन लूप पर शेड्यूल करता है।

scheduledTimerनीचे इस करता है:

RunLoop.main.add(timer, forMode: .default)

यदि आप चाहते हैं कि आपका टाइमर स्क्रॉल करते समय काम करे तो आपको या तो करना होगा:

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

या बस करो:

RunLoop.main.add(timer, forMode: .common)

अंततः उपरोक्त में से एक करने का मतलब है कि आपका धागा स्पर्श घटनाओं से अवरुद्ध नहीं है । जो इसके बराबर है:

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.

दूसरा तरीका:

आप अपने टाइमर के लिए जीसीडी का उपयोग करने पर विचार कर सकते हैं जो आपको लूप प्रबंधन के मुद्दों से अपने कोड को "ढाल" करने में मदद करेगा।

केवल उपयोग न करने के लिए:

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    // your code here
}

बार-बार उपयोग के लिए:

DispatchSourceTimer का उपयोग करने का तरीका देखें


डैनियल जलकुट के साथ एक चर्चा से गहरी खुदाई:

प्रश्न: GCD (बैकग्राउंड थ्रेड्स) कैसे होता है जैसे कि बैकग्राउंड थ्रेड पर एक asyncAfter RunLoop के बाहर निष्पादित किया जाता है? इससे मेरी समझ यह है कि सब कुछ एक RunLoop के भीतर निष्पादित किया जाना है

जरूरी नहीं - प्रत्येक धागे में एक रन लूप हो, लेकिन शून्य हो सकता है यदि थ्रेड के निष्पादन "स्वामित्व" को समन्वित करने का कोई कारण नहीं है।

थ्रेड्स एक OS स्तर का खर्च है जो आपकी प्रक्रिया को कई समानांतर निष्पादन संदर्भों में अपनी कार्यक्षमता को विभाजित करने की क्षमता देता है। रन लूप्स एक फ्रेम-लेवल खर्च है जो आपको एक सिंगल थ्रेड को अलग करने की अनुमति देता है ताकि इसे कई कोड पाथ द्वारा कुशलता से साझा किया जा सके।

आमतौर पर यदि आप किसी ऐसी चीज़ को भेजते हैं, जो किसी थ्रेड पर चलती है, तो संभवत: इसमें कोई रन-अप नहीं होगा, जब तक कि कुछ ऐसे कॉल नहीं आएंगे, [NSRunLoop currentRunLoop]जो किसी एक को बनाते हैं।

संक्षेप में, मोड मूल रूप से इनपुट और टाइमर के लिए एक फिल्टर तंत्र हैं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.