स्विफ्ट में देरी कैसे करें?


262

मैं अपने ऐप को एक निश्चित बिंदु पर रोकना चाहता हूं। दूसरे शब्दों में, मैं चाहता हूं कि मेरा ऐप कोड को निष्पादित करे, लेकिन फिर एक निश्चित बिंदु पर, 4 सेकंड के लिए रुकें, और फिर बाकी कोड के साथ जारी रखें। मैं यह कैसे कर सकता हूँ?

मैं स्विफ्ट का इस्तेमाल कर रहा हूं।


जवाबों:


270

एक नींद के बजाय, जो आपके प्रोग्राम को लॉक कर देगा यदि यूआई थ्रेड से कॉल किया जाता है, तो उपयोग करने NSTimerया प्रेषण टाइमर पर विचार करें ।

लेकिन, अगर आपको वास्तव में मौजूदा धागे में देरी की जरूरत है:

do {
    sleep(4)
}

यह sleepUNIX से फ़ंक्शन का उपयोग करता है ।


4
नींद डार्विन को आयात किए बिना काम करती है, यकीन नहीं होता कि यह एक बदलाव है
दान ब्यूलियू

17
usleep () काम करता है, भी, अगर आपको कुछ और सटीक चाहिए जो 1 सेकंड का रिज़ॉल्यूशन हो।
प्रीवेट

18
usleep () सेकंड का दसवां हिस्सा लेता है, इसलिए usleep (1000000) 1 सेकंड के लिए सो जाएगा
हैरिस

40
"यह मत करो" प्रस्तावना को कम रखने के लिए धन्यवाद।
दान रोसेनस्टार्क

2
मैं लंबी चलने वाली पृष्ठभूमि प्रक्रियाओं का अनुकरण करने के लिए नींद () का उपयोग करता हूं।
विलियम टी। मैलार्ड

345

dispatch_afterब्लॉक का उपयोग करना ज्यादातर मामलों में बेहतर होता है sleep(time)क्योंकि जिस धागे पर नींद का प्रदर्शन किया जाता है वह अन्य काम करने से अवरुद्ध होता है। जब dispatch_afterउस थ्रेड का उपयोग किया जाता है जिस पर काम नहीं किया जाता है, तो यह अवरुद्ध नहीं होता है, इसलिए यह इस बीच अन्य काम कर सकता है।
यदि आप अपने एप्लिकेशन के मुख्य थ्रेड पर काम कर रहे हैं, तो sleep(time)आपके ऐप के उपयोगकर्ता अनुभव के लिए उपयोग करना बुरा है क्योंकि उस समय के दौरान UI अप्रतिसादी होता है।

थ्रेड को फ्रीज करने के बजाय कोड के एक ब्लॉक के निष्पादन के कार्यक्रम के बाद डिस्पैच करें:

स्विफ्ट ≥ 3.0

let seconds = 4.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
    // Put your code which should be executed with a delay here
}

स्विफ्ट <3.0

let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 4 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
    // Put your code which should be executed with a delay here
}

1
आवधिक कार्यों के बारे में क्या?
प्रात: काल

1
ऐप्पल डेवलपर दस्तावेज़ीकरण से इस लेख में वर्णित आवधिक कार्यों के लिए जीसीडी का उपयोग करने की संभावनाएं हैं, लेकिन मैं उसके लिए एक एनटीएसएमर का उपयोग करने की सलाह दूंगा। यह उत्तर दिखाता है कि स्विफ्ट में बिल्कुल ऐसा कैसे करें।
पालले १

2
कोड Xcode 8.2.1 के लिए अपडेट किया गया। पहले .now() + .seconds(4)त्रुटि देता है:expression type is ambiguous without more context
रिचर्ड्स

मैं कतार से बुलाए गए फ़ंक्शन को कैसे निलंबित कर सकता हूं? @Palle
मुकुल मोर

14
अब आपको .now() + .seconds(5)इसे जोड़ने की आवश्यकता नहीं है.now() + 5
cnzac

45

स्विफ्ट 3.0 में विभिन्न दृष्टिकोणों के बीच तुलना

1. नींद

इस विधि में कॉल बैक नहीं है। इस पंक्ति के 4 सेकंड में निष्पादित होने के बाद सीधे कोड रखें। यह उपयोगकर्ता को परीक्षण बटन जैसे UI तत्वों के साथ पुनरावृत्ति करने से रोक देगा जब तक कि समय समाप्त नहीं हो जाता। यद्यपि बटन सोते समय जम जाता है, लेकिन गतिविधि संकेतक जैसे अन्य तत्व अभी भी बिना ठंड के घूम रहे हैं। आप इस क्रिया को नींद के दौरान फिर से ट्रिगर नहीं कर सकते।

sleep(4)
print("done")//Do stuff here

यहां छवि विवरण दर्ज करें

2. डिस्पैच, प्रदर्शन और टाइमर

ये तीन तरीके समान रूप से काम करते हैं, ये सभी बैकग्राउंड थ्रेड पर कॉल बैक के साथ चल रहे हैं, बस अलग-अलग सिंटैक्स और थोड़े अलग फीचर्स के साथ।

डिस्पैच का उपयोग आमतौर पर बैकग्राउंड थ्रेड पर कुछ चलाने के लिए किया जाता है। यह फ़ंक्शन कॉल के भाग के रूप में कॉलबैक है

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
    print("done")
})

प्रदर्शन वास्तव में एक सरलीकृत टाइमर है। यह देरी के साथ एक टाइमर सेट करता है, और फिर चयनकर्ता द्वारा फ़ंक्शन को ट्रिगर करता है।

perform(#selector(callback), with: nil, afterDelay: 4.0)

func callback() {
    print("done")
}}

और अंत में, टाइमर कॉलबैक को दोहराने की क्षमता भी प्रदान करता है, जो इस मामले में उपयोगी नहीं है

Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)


func callback() {
    print("done")
}}

इन तीनों विधि के लिए, जब आप उन्हें ट्रिगर करने के लिए बटन पर क्लिक करते हैं, तो UI फ्रीज नहीं होगा और आपको फिर से उस पर क्लिक करने की अनुमति होगी। यदि आप फिर से बटन पर क्लिक करते हैं, तो एक और टाइमर सेट किया जाता है और कॉलबैक को दो बार ट्रिगर किया जाएगा।

यहां छवि विवरण दर्ज करें

निष्कर्ष के तौर पर

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

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


1
ध्यान दें कि स्विफ्ट 4 में, ओब्जेक्ट डिस्कशन बंद होने के साथ, आपको विकल्प के @objcलिए कॉलबैक फ़ंक्शन के सामने जोड़ना होगा perform(...)। जैसे:@objc func callback() {
लीन

32

मैं पाले से सहमत हूं कि यहां प्रयोग dispatch_afterकरना एक अच्छा विकल्प है। लेकिन आपको शायद जीसीडी कॉल पसंद नहीं है क्योंकि वे लिखने के लिए काफी परेशान हैं । इसके बजाय आप इस सहायक को जोड़ सकते हैं :

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

अब आप अपने कोड को इस तरह से बैकग्राउंड थ्रेड में देरी करते हैं :

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

मुख्य धागे पर विलंब कोड भी सरल है:

delay(bySeconds: 1.5) { 
    // delayed code, by default run in main thread
}

यदि आप एक ऐसी फ्रेमवर्क पसंद करते हैं जिसमें कुछ अधिक उपयोगी विशेषताएं हैं, तो हैंडवाइस लिफ्ट चेकआउट करें । आप इसे Carthage या Accio के माध्यम से अपनी परियोजना में जोड़ सकते हैं, फिर इसे ऊपर दिए गए उदाहरणों की तरह उपयोग कर सकते हैं:

import HandySwift    

delay(by: .seconds(1.5)) { 
    // delayed code
}

यह बहुत मददगार लगता है। क्या आप इसे एक तेज 3.0 संस्करण के साथ अद्यतन करने का मन बनाते हैं। धन्यवाद
grahamcracker1234

मैंने हैंड्सविफ्ट को स्विफ्ट 3 में माइग्रेट करना शुरू कर दिया और अगले सप्ताह एक नया संस्करण जारी करने की योजना बनाई। फिर मैं ऊपर की स्क्रिप्ट को भी अपडेट करने वाला हूं। बने रहें।
जेहुत

हैंड्सविफ्ट का स्विफ्ट 3 में माइग्रेशन अब पूरा हो गया है और संस्करण 1.3.0 में जारी किया गया है। मैंने स्विफ्ट 3 के साथ काम करने के लिए उपरोक्त कोड भी अपडेट किया है! :)
जेहुत

1
आप NSEC_PER_SEC को फिर से फिर से विभाजित करके गुणा क्यों कर रहे हैं? क्या यह बेमानी नहीं है? (उदाहरण डबल (Int64 (सेकंड * डबल (NSEC_PER_SEC))) / डबल (NSEC_PER_SEC)) क्या आप केवल 'DispatchTime.now () + Double (सेकंड) नहीं लिख सकते?
मार्क ए। डोनोहे

1
धन्यवाद @MarqueIV, आप बिल्कुल सही हैं। मैंने अभी कोड अपडेट किया है। यह केवल Xcode 8 से स्वचालित रूपांतरण का परिणाम था और DispatchTimeSwift 2 में उपलब्ध नहीं होने से पहले टाइप रूपांतरण की आवश्यकता थी। यह let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))पहले था ।
जेहुत

24

आप स्विफ्ट 3 के साथ भी ऐसा कर सकते हैं।

देरी के बाद फ़ंक्शन निष्पादित करें।

override func viewDidLoad() {
    super.viewDidLoad()

    self.perform(#selector(ClassName.performAction), with: nil, afterDelay: 2.0)
}


     @objc func performAction() {
//This function will perform after 2 seconds
            print("Delayed")
        }

23

स्विफ्ट 4.2 और एक्सकोड 10.1 में

आपके पास देरी करने के लिए कुल 4 तरीके हैं। इन विकल्पों में से 1 कुछ समय के बाद किसी फ़ंक्शन को कॉल या निष्पादित करने के लिए बेहतर है। नींद () प्रयोग में कम से कम मामला है।

विकल्प 1।

DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    self.yourFuncHere()
}
//Your function here    
func yourFuncHere() {

}

विकल्प 2।

perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)

//Your function here  
@objc func yourFuncHere2() {
    print("this is...")
}

विकल्प 3।

Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)

//Your function here  
@objc func yourFuncHere3() {

}

विकल्प 4।

sleep(5)

यदि आप किसी कार्य को करने के लिए कुछ समय के बाद किसी फ़ंक्शन को कॉल करना चाहते हैं तो नींद का उपयोग न करें ।


19

NSTimer

@Nneonneo द्वारा उत्तर का उपयोग करने का सुझाव दिया, NSTimerलेकिन यह नहीं दिखाया कि यह कैसे करना है। यह मूल वाक्यविन्यास है:

let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)

यहाँ यह दिखाने के लिए एक बहुत ही सरल परियोजना है कि इसका उपयोग कैसे किया जा सकता है। जब एक बटन दबाया जाता है तो यह एक टाइमर शुरू करता है जो आधे सेकंड की देरी के बाद फ़ंक्शन को कॉल करेगा।

import UIKit
class ViewController: UIViewController {

    var timer = NSTimer()
    let delay = 0.5
    
    // start timer when button is tapped
    @IBAction func startTimerButtonTapped(sender: UIButton) {

        // cancel the timer in case the button is tapped multiple times
        timer.invalidate()

        // start the timer
        timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)
    }

    // function to be called after the delay
    func delayedAction() {
        print("action has started")
    }
}

का उपयोग करना dispatch_time( पल्ले के जवाब में ) एक और वैध विकल्प है। हालांकि, इसे रद्द करना कठिन है । साथ NSTimer, एक देरी घटना को रद्द करने से पहले ऐसा होता है, तुम सब करने की जरूरत है कॉल है

timer.invalidate()

उपयोग करने sleepकी अनुशंसा नहीं की जाती है, विशेष रूप से मुख्य धागे पर, क्योंकि यह धागे पर किए जा रहे सभी काम को रोक देता है।

मेरे फुलर जवाब के लिए यहां देखें ।


7

स्विफ्ट 3.0 में निम्नलिखित कार्यान्वयन का प्रयास करें

func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { 
        completion()
    }
}

प्रयोग

delayWithSeconds(1) {
   //Do something
}

7

आप विलंब फ़ंक्शन को आसानी से उपयोग करने के लिए एक्सटेंशन बना सकते हैं (सिंटैक्स: स्विफ्ट 4.2+)

extension UIViewController {
    func delay(_ delay:Double, closure:@escaping ()->()) {
        DispatchQueue.main.asyncAfter(
            deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
    }
}

UIViewController में उपयोग कैसे करें

self.delay(0.1, closure: {
   //execute code
})

6

यदि आपको एक सेकंड से कम की देरी सेट करने की आवश्यकता है, तो .seconds पैरामीटर सेट करना आवश्यक नहीं है। मुझे उम्मीद है कि यह किसी के लिए उपयोगी है।

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
        // your code hear
})

5
DispatchQueue.global(qos: .background).async {
    sleep(4)
    print("Active after 4 sec, and doesn't block main")
    DispatchQueue.main.async{
        //do stuff in the main thread here
    }
}

4
Doing DispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}Is is more correct ✌️
eonist

5

यदि आपका कोड पहले से ही बैकग्राउंड थ्रेड में चल रहा है, तो फाउंडेशन में इस विधि का उपयोग करके थ्रेड को रोकें :Thread.sleep(forTimeInterval:)

उदाहरण के लिए:

DispatchQueue.global(qos: .userInitiated).async {

    // Code is running in a background thread already so it is safe to sleep
    Thread.sleep(forTimeInterval: 4.0)
}

(सुझाव के लिए अन्य उत्तर देखें जब आपका कोड मुख्य धागे पर चल रहा हो।)


3

एक सरल समय देरी बनाने के लिए, आप डार्विन को आयात कर सकते हैं और फिर देरी करने के लिए नींद (सेकंड) का उपयोग कर सकते हैं। हालांकि, केवल पूरे सेकंड लगते हैं, इसलिए अधिक सटीक माप के लिए आप डार्विन को आयात कर सकते हैं और बहुत सटीक माप के लिए usleep (सेकंड का मिलियन) का उपयोग कर सकते हैं। यह परीक्षण करने के लिए, मैंने लिखा:

import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")

कौन सा प्रिंट करता है, फिर 1 सेकंड और प्रिंट के लिए इंतजार करता है, फिर 0.4 सेकंड के लिए इंतजार करता है। सभी ने उम्मीद के मुताबिक काम किया।


-3

यह सबसे सरल है

    delay(0.3, closure: {
        // put her any code you want to fire it with delay
        button.removeFromSuperview()   
    })

2
यह फाउंडेशन का हिस्सा नहीं है, यह है, जैसा कि मैं मानता हूं, 'हैंडीसॉफ्ट' कोकोपॉड में शामिल है। आपको अपने उत्तर में स्पष्ट करना चाहिए।
जाॅन नैश

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