Swift'te gecikme nasıl oluşturulur?


Yanıtlar:


271

UI iş parçacığından çağrılırsa programınızı kilitleyecek bir uyku yerine, kullanmayı NSTimerveya bir dağıtım zamanlayıcısını düşünün .

Ancak, geçerli iş parçacığında gerçekten bir gecikmeye ihtiyacınız varsa:

do {
    sleep(4)
}

Bu sleepUNIX işlevini kullanır .


4
uyku darwin'i ithal etmeden çalışır, bunun bir değişiklik olup olmadığından emin değilim
Dan Beaulieu

17
usleep (), 1 sn çözünürlükten daha hassas bir şeye ihtiyacınız varsa da çalışır.
2'de prewett

18
usleep () saniyenin milyonda birini alır, bu yüzden usleep (1000000) 1 saniye uyuyacaktır
Harris

40
"Bunu yapma" girişini kısa tuttuğunuz için teşekkür ederiz.
Dan Rosenstark

2
Uzun süredir çalışan arka plan işlemlerini simüle etmek için sleep () kullanıyorum.
William T. Mallard

345

Bir dispatch_afterbloğun kullanılması çoğu durumda sleep(time), uykunun yapıldığı iş parçacığının başka iş yapması engellendiğinden kullanmaktan daha iyidir . dispatch_afterüzerinde çalışılan ipliği kullanırken bu arada başka işler yapabilmesi için engellenmez.
Uygulamanızın ana iş parçacığı üzerinde çalışıyorsanız sleep(time), kullanıcı arayüzü bu süre zarfında yanıt vermediğinden, kullanımı uygulamanızın kullanıcı deneyimi için kötüdür.

İş parçacığını dondurmak yerine bir kod bloğunun yürütülmesini zamanladıktan sonra gönderir:

Swift ≥ 3.0

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

Swift <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
Periyodik eylemler ne olacak?
Çar

1
Apple geliştirici belgelerinde bu makalede açıklanan periyodik görevler için gcd kullanmak için olasılıklar vardır, ancak bunun için bir NSTimer kullanmanızı tavsiye ederim. bu cevap bunu tam olarak nasıl yapacağınızı gösterir.
Palle

2
Xcode 8.2.1 için kod güncellendi. Daha önce .now() + .seconds(4)hata veriyor:expression type is ambiguous without more context
richards

Kuyruktan çağrılan işlevi nasıl askıya alabilirim? @Palle
Mukul More

14
Artık eklemem gerekiyor .now() + .seconds(5)o basitçe.now() + 5
cnzac

45

Hızlı 3.0'da farklı yaklaşımlar arasında karşılaştırma

1. Uyku

Bu yöntemin geri çağrısı yoktur. Kodları 4 saniyede yürütülecek olan bu satırın hemen sonrasına yerleştirin. Zaman geçene kadar kullanıcının test düğmesi gibi UI öğeleriyle yinelenmesini durduracaktır. Uyku başladığında düğme biraz donmuş olsa da, aktivite göstergesi gibi diğer öğeler hala donmadan dönüyor. Uyku sırasında bu eylemi tekrar başlatamazsınız.

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

resim açıklamasını buraya girin

2. Sevkıyat, Performans ve Zamanlayıcı

Bu üç yöntem benzer şekilde çalışır, hepsi arka plan iş parçacığında geri çağrılarla, sadece farklı sözdizimi ve biraz farklı özelliklerle çalışır.

Dağıtım, arka plan iş parçacığında bir şey çalıştırmak için yaygın olarak kullanılır. İşlev çağrısının bir parçası olarak geri çağırma özelliği vardır

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

Performans aslında basitleştirilmiş bir zamanlayıcıdır. Gecikmeli bir zamanlayıcı ayarlar ve sonra seçici seçerek işlevi tetikler.

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

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

Ve son olarak, zamanlayıcı ayrıca bu durumda yararlı olmayan geri aramayı tekrarlama yeteneği sağlar

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


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

Tüm bu üç yöntem için, onları tetiklemek için düğmeye tıkladığınızda, kullanıcı arayüzü donmaz ve tekrar tıklamanıza izin verilir. Düğmeye tekrar tıklarsanız, başka bir zamanlayıcı ayarlanır ve geri arama iki kez tetiklenir.

resim açıklamasını buraya girin

Sonuç olarak

Dört yöntemin hiçbiri kendi başlarına yeterince iyi çalışmaz. sleepkullanıcı etkileşimini devre dışı bırakır, böylece ekran " donar " (aslında değil) ve kötü kullanıcı deneyimi sağlar. Diğer üç yöntem ekranı dondurmaz, ancak birden çok kez tetikleyebilirsiniz ve çoğu zaman kullanıcının tekrar arama yapmasına izin vermeden önce geri aramayı beklemek istersiniz.

Böylece daha iyi bir tasarım, ekran engellemeli üç asenkron yöntemden birini kullanacaktır. Kullanıcı düğmeyi tıkladığında, tüm ekranı, üstte bir eğirme etkinliği göstergesiyle yarı saydam bir görünümle kaplayın ve kullanıcıya düğmenin tıklatıldığını bildirir. Ardından geri arama işlevindeki görünümü ve göstergeyi kaldırın, kullanıcıya işlemin düzgün bir şekilde yapıldığını vb. Söyler.


1
Swift 4'te, nesc çıkarsama özelliği kapalı olduğunda, seçenek @objciçin geri arama işlevinin önüne eklemeniz gerektiğini unutmayın perform(...). Gibi:@objc func callback() {
leanne

32

Palle ile birlikte dispatch_afterkullanımın burada iyi bir seçim olduğunu kabul ediyorum . Ama muhtemelen GCD çağrılarını sevmiyorsunuz çünkü yazmak oldukça can sıkıcı . Bunun yerine bu kullanışlı yardımcıyı ekleyebilirsiniz :

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)
        }
    }
}

Şimdi kodunuzu aşağıdaki gibi bir arka plan iş parçacığında geciktirirsiniz :

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

Ana iş parçacığındaki kodun geciktirilmesi daha da basittir:

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

Bazı daha kullanışlı özelliklere sahip bir Çerçeveyi tercih ederseniz , HandySwift'i kontrol edin . Bunu projenize Carthage veya Accio aracılığıyla ekleyebilir ve daha sonra yukarıdaki örneklerde olduğu gibi kullanabilirsiniz:

import HandySwift    

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

Bu çok yardımcı görünüyor. Hızlı bir 3.0 sürümüyle güncellemeyi düşünür müsünüz? Thanks
grahamcracker1234

HandySwift'i Swift 3'e geçirmeye başladım ve önümüzdeki hafta yeni bir sürüm yayınlamayı planlıyorum. Sonra yukarıdaki senaryoyu da güncelleyeceğim. Bizi izlemeye devam edin.
Jeehut

HandySwift'in Swift 3'e geçişi artık tamamlanmış ve 1.3.0 sürümünde yayınlanmıştır. Ayrıca Swift 3 ile çalışmak için yukarıdaki kodu güncelledim! :)
Jeehut

1
Neden NSEC_PER_SEC ile çarpıp tekrar bölüyorsunuz? Bu gereksiz değil mi? (örn. Çift (Int64 (saniye * Çift (NSEC_PER_SEC))) / Çift (NSEC_PER_SEC)) 'DispatchTime.now () + Çift (saniye) yazamaz mısınız?
Mark A. Donohoe

1
Teşekkürler @MarqueIV, elbette haklısın. Kodu yeni güncelledim. Bu sadece Xcode 8'den otomatik dönüşümün sonucuydu ve DispatchTimeSwift 2'de mevcut olmadığı için daha önce tür dönüştürmeye ihtiyaç duyuldu let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))).
Jeehut

24

Bunu Swift 3 ile de yapabilirsiniz.

Bu işlevi gecikmeden sonra böyle yapın.

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

Swift 4.2 ve Xcode 10.1'de

Gecikmenin toplam 4 yolu var. Bu seçeneklerden 1'i bir süre sonra bir işlevi çağırmak veya yürütmek tercih edilir. Uyku () kullanımda en az bir durumdur.

Seçenek 1.

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

}

Seçenek 2.

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

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

Seçenek 3.

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

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

}

Seçenek 4.

sleep(5)

Bir şeyleri yürütmek için bir süre sonra bir işlevi çağırmak istiyorsanız uyku kullanmayın .


19

NSTimer

@Nneonneo'nun cevabı kullanmayı önerdi, NSTimerancak nasıl yapılacağını göstermedi. Bu temel sözdizimidir:

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

İşte nasıl kullanılabileceğini gösteren çok basit bir proje. Bir düğmeye basıldığında, yarım saniyelik bir gecikmeden sonra bir işlevi çağıracak bir zamanlayıcı başlar.

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")
    }
}

Kullanılması dispatch_time(olduğu gibi Palle cevabı ) başka bir geçerli bir seçenektir. Ancak iptal etmek zordur . İle NSTimergerçekleşmeden önce, yapmanız gereken tüm çağrıdır, gecikmiş bir etkinliği iptal etmek

timer.invalidate()

Kullanılması sleeptüm iş parçacığı üzerinde yapılıyor durur beri, özellikle ana iş parçacığı üzerinde, tavsiye edilmez.

Tam yanıtım için buraya bakın .


7

Swift 3.0'da aşağıdaki uygulamayı deneyin

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

kullanım

delayWithSeconds(1) {
   //Do something
}

7

Gecikme işlevini kolayca kullanmak için uzantı oluşturabilirsiniz (Sözdizimi: Swift 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'da nasıl kullanılır

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

6

Bir saniyeden daha kısa bir gecikme ayarlamanız gerekiyorsa, .seconds parametresini ayarlamanız gerekmez. Umarım bu birisi için faydalıdır.

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
Yapma DispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}✌️ Is muhtemelen daha doğru
eonist

5

Kodunuz zaten bir arka plan iş parçacığında çalışıyorsa , Foundation'da bu yöntemi kullanarak iş parçacığını duraklatın :Thread.sleep(forTimeInterval:)

Örneğin:

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

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

(Kodunuz ana iş parçacığında çalışırken öneriler için diğer yanıtlara bakın.)


3

Basit bir zaman gecikmesi oluşturmak için Darwin'i içe aktarabilir ve sonra gecikmeyi yapmak için uyku (saniye) kullanabilirsiniz. Ancak bu sadece birkaç saniye sürer, bu nedenle daha hassas ölçümler için Darwin'i içe aktarabilir ve çok hassas ölçümler için uyku modunu (saniyenin milyonda biri) kullanabilirsiniz. Bunu test etmek için şunu yazdım:

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

Hangi baskılar, daha sonra 1 saniye bekler ve baskılar, daha sonra 0.4 saniye bekler, sonra baskılar. Hepsi beklendiği gibi çalıştı.


-3

bu en basit

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

2
Bu Vakfın bir parçası değil, tahmin ettiğim gibi 'HandySwift' Cocoapod'a dahil. Cevabınızda bunu açıklığa kavuşturmalısınız.
Jan Nash

Swift'te bir şeyin olmasını bekleyip beklemeyen bir şey var mı?
Saeed Rahmatolahi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.