特定の時点でアプリを一時停止したい。言い換えれば、私は自分のアプリにコードを実行させたいのですが、それからある時点で、4秒間休止してから、残りのコードを続けます。これどうやってするの?
私はSwiftを使っています。
UIスレッドから呼び出された場合にプログラムをロックするスリープの代わりに、NSTimer
またはディスパッチタイマーの使用を検討してください。
しかし、現在のスレッドで本当に遅延が必要な場合は、
... {
sleep(4)
}
これは、UNIXの sleep
関数を使用します。
dispatch_after
ブロックを使用することは、スリープが実行されるスレッドが他の作業を実行することからブロックされるので、ほとんどの場合sleep(time)
を使用するより良いです。 dispatch_after
を使用している場合、作業中のスレッドはブロックされないので、その間に他の作業を行うことができます。
アプリケーションのメインスレッドで作業している場合、sleep(time)
を使用すると、その間UIが応答しなくなるため、アプリケーションのユーザーエクスペリエンスには適しません。
after dispatchは、スレッドをフリーズするのではなく、コードブロックの実行をスケジュールします。
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
// Put your code which should be executed with a delay here
})
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
}
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は実際には単純化されたタイマーです。それは遅れでタイマーを設定して、そしてセレクタによって機能を引き起こします。
perform(#selector(callback), with: nil, afterDelay: 4.0)
func callback() {
print("done")
}}
そして最後に、timerはコールバックを繰り返す機能も提供します。この場合は役に立ちません。
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つをスクリーンブロックで使用することをお勧めします。ユーザーがボタンをクリックしたときに、回転するアクティビティインジケータが上にある半透明のビューで画面全体を覆い、ボタンのクリックが処理されていることをユーザーに知らせます。その後、コールバック関数のビューとインジケータを削除し、アクションが正しく処理されたことなどをユーザーに伝えます。
私はdispatch_after
を使うことが 良い選択 であることをPalleに同意します。しかし、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
}
Framework がより便利な機能もあるなら、HandySwiftをチェックアウトしてください。あなたはそれをあなたのプロジェクトに追加することができますCarthage または Accioそしてそれからそれを上の例のように正確に使う:
import HandySwift
delay(by: .seconds(1.5)) {
// delayed code
}
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")
}
@ 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's answer のように)を使用するのも有効な選択肢です。しかし、 キャンセルするのは難しい 。 NSTimer
を使用すると、遅延イベントが発生する前にそれをキャンセルするには、呼び出すだけです。
timer.invalidate()
sleep
の使用は、特にメインスレッドではお勧めできません。スレッドで行われている作業をすべて停止するためです。
私のより完全な答えは ここ を見てください。
Swift 3.0で次の実装を試してください。
func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
使用法
delayWithSeconds(1) {
//Do something
}
Swift 4.2とXcode 10.1では
あなたは延期するのに4つの方法があります。すべてのうちオプション1はしばらくしてから関数を呼び出すか実行するのが望ましいです。すべてのうちsleep()は、ほとんど使用されていません。
オプション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)
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
}
}
1秒未満の遅延を設定する必要がある場合は、.secondsパラメータを設定する必要はありません。これが誰かに役立つことを願っています。
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
// your code hear
})
単純な時間遅延を作成するには、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秒間待ってから印刷します。すべて正常に機能しました。
遅延機能を簡単に使うための拡張を作成することができる(Syntax: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
})
コードが既にバックグラウンドスレッドで実行されている場合は、 を使用してスレッドを一時停止します - 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)
}
(コードがメインスレッドで実行されているときの提案については、他の回答を参照してください。)