Как создать задержку в Swift?

Я хочу приостановить свое приложение в определенной точке. Другими словами, Я хочу, чтобы мое приложение выполнило код, но затем в определенный момент сделайте паузу на 4 секунды, а затем продолжите с остальной частью кода. Как я могу это сделать?

Я использую Swift.

10 ответов


вместо сна, который будет блокировать вашу программу при вызове из потока пользовательского интерфейса, рассмотрите возможность использования NSTimer или таймер отправки.

но, если вам действительно нужна задержка в текущем потоке:

... {
    sleep(4)
}

использует sleep функция из UNIX.


С помощью dispatch_after блок в большинстве случаев лучше, чем при использовании sleep(time) поскольку поток, на котором выполняется сон, блокируется от выполнения другой работы. при использовании dispatch_after поток, над которым работает, не блокируется, поэтому он может выполнять другую работу в то же время.
если вы работаете над основным потоком вашего приложения, используя sleep(time) плохо для пользовательского опыта вашего приложения, поскольку пользовательский интерфейс не отвечает в течение этого времени.

Отправка после расписания исполнения блок кода, вместо замораживания потока:

Swift ≥ 3.0

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

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


Я согласен с Палле, что с помощью dispatch_after is хороший выбор здесь. Но вам, вероятно, не нравятся вызовы 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. Вы можете добавить его в проект через Карфаген затем используйте его точно так же, как в приведенных выше примерах:

import HandySwift    

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

сравнение между различными подходами в Swift 3.0

1. Спи!--7-->

этот метод не имеет обратного вызова. Положите коды сразу после этой строки, который нужно выполнить в 4 секундах. Это остановит пользователя от итерации с элементами пользовательского интерфейса, такими как кнопка test, пока не пройдет время. Хотя кнопка как бы заморожена, когда начинается сон, другие элементы, такие как индикатор активности, все еще вращаются без замораживания. Вы не можете запустить это действие снова во время сна.

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

enter image description here

2. Отправка, выполнение и таймер

эти три метода работают аналогично, все они работают в фоновом потоке с обратными вызовами, просто с другим синтаксисом и немного разными функциями.

Dispatch обычно используется для запуска чего-то в фоновом потоке. Он имеет обратный вызов как часть вызова функции

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

для всех этих трех методов, когда вы нажимаете на кнопку, чтобы вызвать их, пользовательский интерфейс не замерзнет, и вы можете нажать на него снова. Если вы нажмете на кнопку еще раз, будет установлен другой таймер и будет запущен обратный вызов дважды.

enter image description here

В заключение

ни один из четырех методов не работает достаточно хорошо сами по себе. sleep отключить взаимодействие с пользователем, поэтому экран "замерзает"(не на самом деле) и результаты плохой пользовательский опыт. Остальные три метода не будут замораживать экран, но вы можете запускать их несколько раз, и в большинстве случаев вы хотите подождать, пока не получите обратный вызов, прежде чем позволить пользователю сделать снова звонок.

таким образом, лучший дизайн будет использовать один из трех асинхронных методов с блокировкой экрана. Когда пользователь нажимает на кнопку, накройте весь экран полупрозрачным видом с индикатором вращающейся активности сверху, сообщая пользователю, что щелчок кнопки обрабатывается. Затем удалите представление и индикатор в функции обратного вызова, сообщив пользователю, что действие правильно обработано и т. д.


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 не рекомендуется, особенно в основном потоке, так как он останавливает всю работу, выполняемую в потоке.

посмотреть здесь для моего более полного ответа.


вы также можете сделать это с помощью 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")
        }

попробуйте следующую реализацию в Swift 3.0

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

использование

delayWithSeconds(1) {
   //Do something
}

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

чтобы создать простую временную задержку, вы можете импортировать Darwin, а затем использовать sleep(секунды), чтобы сделать задержку. Это занимает всего несколько секунд, поэтому для более точных измерений вы можете импортировать Дарвина и использовать usleep(миллионные доли секунды) для очень точного измерения. Чтобы проверить это, я написал:

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

который печатает, затем ждет 1 сек и печатает, затем ждет 0.4 сек, затем печатает. Все сработало, как и ожидалось.


Это самый простой

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