Как создать задержку (delay) в Swift? Нужен простой и надёжный способ
Правильный способ: DispatchQueue.main.asyncAfter
Самый распространённый и рекомендуемый способ создать задержку в Swift — использовать Grand Central Dispatch (GCD) через метод asyncAfter. Этот метод позволяет выполнить код на указанной очереди (например, главной) после заданной задержки, не блокируя поток, на котором вы сейчас находитесь. В отличие от Thread.sleep, интерфейс остаётся отзывчивым.
Вот простой пример, который делает именно то, что вам нужно:
import UIKit
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
print("Кнопка нажата. Начинаем задержку...")
// Задержка в 2 секунды на главном потоке
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Прошло 2 секунды! Выполняем код.")
// Здесь можно, например, перейти на другой экран
// self.performSegue(withIdentifier: "showNextScreen", sender: nil)
}
print("Этот код выполнится сразу, не дожидаясь задержки")
}
}
Как это работает
DispatchQueue.main— это главная очередь, которая отвечает за обновление интерфейса. Весь код, который работает с UI, должен выполняться именно на ней..asyncAfter— метод, который планирует выполнение замыкания (closure) через указанный промежуток времени.deadline: .now() + 2.0— указывает время, когда код должен выполниться..now()— это текущий момент, к нему прибавляем 2 секунды.
Обратите внимание: код после вызова asyncAfter (в примере — print("Этот код выполнится сразу...")) выполняется немедленно, не дожидаясь задержки. Это ключевое отличие от блокирующего sleep.
Если нужно выполнить задержку НЕ на главном потоке
Иногда нужно выполнить задержку в фоновом потоке, например, перед загрузкой данных из сети:
DispatchQueue.global(qos: .background).async {
// Тяжёлая работа в фоне
print("Начинаем фоновую задачу")
// Задержка 3 секунды на этой же фоновой очереди
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 3.0) {
print("Фоновая задача продолжена через 3 секунды")
// После этого можно вернуться на главный поток для обновления UI
DispatchQueue.main.async {
print("Обновляем интерфейс на главном потоке")
}
}
}
Важные моменты
- Не используйте
Thread.sleepна главном потоке — это заморозит приложение на время задержки, и система может даже убить его (покажется, что приложение зависло). - Задержка не является точной до миллисекунды — GCD гарантирует, что код выполнится не раньше указанного времени, но может выполниться чуть позже, если система занята.
- Отмена задержки — если нужно отменить запланированное выполнение, можно использовать
DispatchWorkItemи методcancel(). Но для простых случаевasyncAfterболее чем достаточно.
Этот подход считается стандартом в Swift и используется в продакшн-приложениях. Он простой, понятный и не блокирует интерфейс.
<<Альтернативный способ: Timer (NSTimer)
Другой популярный способ создать задержку — использовать Timer. Этот подход особенно удобен, если вам нужно выполнять код периодически (каждые N секунд), но он отлично подходит и для однократной задержки.
Вот пример с однократным таймером:
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
print("Запускаем таймер на 2 секунды...")
// Создаём таймер, который сработает один раз
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in
print("Прошло 2 секунды! Таймер сработал.")
// Здесь ваш код
}
print("Код после таймера выполнится сразу")
}
}
Когда использовать Timer вместо asyncAfter
- Повторяющиеся действия — если нужно выполнять код с интервалом (например, обновлять таймер обратного отсчёта),
Timerс параметромrepeats: trueбудет удобнее. - Отмена в любой момент — таймер легко остановить методом
invalidate(), сохранив ссылку на него. - Более читаемый код — для некоторых разработчиков синтаксис таймера интуитивно понятнее, чем
asyncAfter.
Важно: Не забывайте, что таймер должен быть запущен на главном потоке (по умолчанию scheduledTimer так и делает), иначе он может не сработать. Если вы создаёте таймер в фоновом потоке, нужно явно добавить его в RunLoop.
Оба способа (asyncAfter и Timer) корректно работают с UI и не блокируют интерфейс. Выбирайте тот, который вам кажется понятнее под конкретную задачу.