Swiftで遅延を作成する方法は?


回答:


271

UIスレッドから呼び出された場合にプログラムをロックするスリープの代わりに、NSTimerまたはディスパッチタイマーの使用を検討してください。

ただし、現在のスレッドで本当に遅延が必要な場合:

do {
    sleep(4)
}

これは、sleepUNIX の機能を使用します。


4
ダーウィンをインポートせずにスリープ作品、これは変更がないことを確認された場合
ダン・ボーリュー

17
usleep()も機能しますが、1秒の解像度より正確なものが必要な場合に使用します。
2016

18
usleep()は100万分の1秒かかるため、usleep(1000000)は1秒間スリープします
Harris

40
「これをしないでください」プリアンブルを短くしていただきありがとうございます。
Dan Rosenstark 2017年

2
私はsleep()を使用して、長時間実行されるバックグラウンドプロセスをシミュレートします。
ウィリアムT.マラード

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
定期的なアクションはどうですか?
2016年

1
アップルの開発者向けドキュメントのこの記事で説明されている定期的なタスクにgcdを使用する可能性がありますが、NSTimerを使用することをお勧めします。この回答は、それを迅速に行う方法を示しています。
2016年

2
Xcode 8.2.1用に更新されたコード。以前.now() + .seconds(4)はエラーが発生していました:expression type is ambiguous without more context
リチャーズ2017

キューから呼び出された関数を一時停止するにはどうすればよいですか?@Palle
Mukul More

14
追加.now() + .seconds(5)する必要はもうありません.now() + 5
cnzac

45

Swift 3.0でのさまざまなアプローチの比較

1.睡眠

このメソッドにはコールバックがありません。この行の直後にコードを配置すると、4秒で実行されます。時間がなくなるまで、ユーザーがテストボタンなどのUI要素を反復処理するのを停止します。ボタンはスリープ状態になるとフリーズしますが、アクティビティインジケーターなどの他の要素はフリーズせずに回転しています。睡眠中にこのアクションを再びトリガーすることはできません。

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

ここに画像の説明を入力してください

2.ディスパッチ、実行、タイマー

これらの3つのメソッドは同様に機能します。これらはすべて、コールバックを使用してバックグラウンドスレッドで実行されますが、構文と機能がわずかに異なります。

ディスパッチは通常、バックグラウンドスレッドで何かを実行するために使用されます。関数呼び出しの一部としてコールバックがあります

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

これら3つの方法すべてについて、ボタンをクリックしてトリガーしても、UIはフリーズせず、もう一度クリックすることができます。もう一度ボタンをクリックすると、別のタイマーが設定され、コールバックが2回トリガーされます。

ここに画像の説明を入力してください

結論として

4つの方法のいずれも、それだけでは十分に機能しません。sleepユーザーインタラクションが無効になるため、画面が「フリーズ」します(実際には)、ユーザーエクスペリエンスが低下します。他の3つのメソッドは画面をフリーズしませんが、それらを複数回トリガーできます。ほとんどの場合、コールバックが返されるまで待ってから、ユーザーが再びコールを発信できるようにします。

したがって、より優れた設計は、3つの非同期メソッドのいずれかと画面ブロックを使用することです。ユーザーがボタンをクリックするときは、画面全体を半透明のビューで覆い、上部に回転アクティビティインジケーターを表示して、ボタンクリックが処理されていることをユーザーに伝えます。次に、コールバック関数のビューとインジケーターを削除し、アクションが適切に処理されたことをユーザーに通知します。


1
Swift 4では、objc推論がオフになって@objcいるため、perform(...)オプションのコールバック関数の前に追加する必要があります。このように:@objc func callback() {
傾斜

32

ここで使用することdispatch_after良い選択あるとPalleに同意します。しかし、GCDの呼び出しは非常に煩わしいため、おそらくGCDの呼び出しは気に入らないでしょう。代わりに、この便利なヘルパーを追加できます。

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
}

さらに便利な機能を備えたフレームワークを好む場合は、HandySwiftをチェックアウトしてくださいCarthageまたはAccioを介してプロジェクトに追加し、上記の例とまったく同じように使用できます。

import HandySwift    

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

これは非常に役立つようです。Swift 3.0バージョンで更新してもよろしいですか。ありがとう
grahamcracker1234

私はHandySwiftからSwift 3への移行を開始し、来週新しいバージョンをリリースする予定です。次に、上記のスクリプトも更新します。乞うご期待。
Jeehut

HandySwiftからSwift 3への移行が完了し、バージョン1.3.0でリリースされました。上記のコードをSwift 3で使用できるように更新しました!:)
Jeehut 2016

1
なぜNSEC_PER_SECを掛けてから、もう一度割るのですか?それは冗長ではありませんか?(例:Double(Int64(seconds * Double(NSEC_PER_SEC)))/ Double(NSEC_PER_SEC)) 'DispatchTime.now()+ Double(seconds)'とだけ書けませんか?
Mark A. Donohoe 2016年

1
ありがとう@MarqueIV、あなたはもちろん正しい。コードを更新しました。これは単にXcode 8からの自動変換の結果でDispatchTimeあり、Swift 2では利用できなかったため、以前は型変換が必要でしたlet dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
Jeehut

24

Swift 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

Swift 4.2およびXcode 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)

何かを実行するためにしばらくしてから関数を呼び出したい場合は、sleepを使用しないでください


19

NSTimer

@nneonneoの答えは、使用を提案しましたNSTimerが、その方法を示していませんでした。これは基本的な構文です:

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

これは、それがどのように使用されるかを示す非常に単純なプロジェクトです。ボタンが押されると、0.5秒の遅延後に関数を呼び出すタイマーが起動します。

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(のようにPalleの回答)別の有効なオプションです。ただし、キャンセル困難です。を使用NSTimerすると、遅延したイベントが発生する前にキャンセルするには、次を呼び出すだけです。

timer.invalidate()

sleepスレッドで実行されているすべての作業を停止するため、特にメインスレッドでの使用はお勧めしません。

私のより完全な答えはここを参照してください。


7

Swift 3.0で次の実装を試してください

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

使用法

delayWithSeconds(1) {
   //Do something
}

7

拡張機能を作成して、遅延機能を簡単に使用できます(構文: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での使用方法

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

6

1秒未満の遅延を設定する必要がある場合は、.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
こうDispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}✌️おそらくより正しいです
eonist

5

コードが既にバックグラウンドスレッドで実行されている場合は、Foundationでこのメソッドを使用してスレッドを一時停止します。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

単純な時間遅延を作成するには、Darwinをインポートしてから、sleep(seconds)を使用して遅延を実行します。ただし、この処理には数秒しかかかりません。したがって、より正確な測定を行うには、Darwinをインポートし、usleep(100万分の1秒)を使用して非常に正確な測定を行うことができます。これをテストするために、私は書きました:

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
これはFoundationの一部ではなく、私が想定しているように、 'HandySwift' Cocoapodに含まれています。回答でそれを明確にする必要があります。
Jan Nash

Swiftには何かが起こるのを待つものがあるのでしょうか?
Saeed Rahmatolahi 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.