Я хочу призупинити свою програму в певний момент. Іншими словами, я хочу, щоб моя програма виконувала код, але потім у певний момент зробіть паузу на 4 секунди, а потім продовжуйте роботу з рештою коду. Як я можу це зробити?
Я використовую Swift.
Я хочу призупинити свою програму в певний момент. Іншими словами, я хочу, щоб моя програма виконувала код, але потім у певний момент зробіть паузу на 4 секунди, а потім продовжуйте роботу з рештою коду. Як я можу це зробити?
Я використовую Swift.
Відповіді:
Замість сну, який заблокує вашу програму, якщо вона буде викликана з потоку користувальницького інтерфейсу, подумайте про використання NSTimer
або таймер відправки.
Але, якщо вам дійсно потрібна затримка поточного потоку:
do {
sleep(4)
}
Для цього використовується sleep
функція UNIX.
Використовувати dispatch_after
блок в більшості випадків краще, ніж використовувати, sleep(time)
оскільки нитка, на якій виконується сон, забороняється виконувати інші роботи. при використанні dispatch_after
потоку, над яким працює, не блокується, тим часом він може виконувати іншу роботу.
Якщо ви працюєте над основною темою програми, використання sleep(time)
файлів погано впливає на користування вашим додатком, оскільки інтерфейс користувача не реагує на цей час.
Відправте після планування виконання блоку коду замість заморожування потоку:
let seconds = 4.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
// 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
}
.now() + .seconds(4)
дає помилку:expression type is ambiguous without more context
.now() + .seconds(5)
це просто.now() + 5
Порівняння між різними підходами у швидкому 3.0
1. Сон
Цей метод не має зворотного дзвінка. Покладіть коди безпосередньо після того, як цей рядок буде виконано за 4 секунди. Це зупинить користувача від ітерації з елементами інтерфейсу, як тестова кнопка, поки час не пройде. Хоча кнопка замерзла, коли починається сон, інші елементи, як-от індикатор активності, все ще крутяться без замерзання. Ви не можете повторити цю дію під час сну.
sleep(4)
print("done")//Do stuff here
2. Відправлення, виконання та таймер
Ці три методи працюють аналогічно, вони працюють у фоновому режимі із зворотними дзвінками, просто з різним синтаксисом та дещо іншими функціями.
Диспетчер зазвичай використовується для запуску чогось на фоновому потоці. Він має зворотний виклик як частину виклику функції
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")
}}
Для всіх цих трьох методів, коли ви натискаєте кнопку, щоб запустити їх, інтерфейс не замерзне, і вам дозволяється натискати на нього ще раз. Якщо ви знову натиснете на кнопку, встановиться інший таймер, і зворотний виклик буде спрацьовано двічі.
На закінчення
Жоден із чотирьох методів не працює досить добре, лише сам по собі. sleep
вимкне взаємодію з користувачем, тому екран " замерзає " (насправді) і призводить до поганої роботи користувача. Інші три способи не заморожують екран, але ви можете їх запускати кілька разів, і більшість разів потрібно зачекати, поки ви отримаєте зворотній дзвінок, перш ніж дозволити користувачеві здійснити дзвінок знову.
Таким чином, кращим дизайном буде використання одного з трьох методів асинхронізації з блокуванням екрана. Коли користувач натискає кнопку, накрийте весь екран деяким напівпрозорим видом із вгорі індикатором активності обертання, повідомляючи користувачеві, що натискання кнопки обробляється. Потім видаліть подання та індикатор у функції зворотного дзвінка, повідомивши користувачеві, що дія належним чином виконується тощо.
@objc
перед функцією зворотного виклику perform(...)
опцію. @objc func callback() {
Я згоден з Палле, що використання dispatch_after
тут є хорошим вибором . Але вам, мабуть, не подобаються дзвінки 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
}
DispatchTime
це не було в Swift 2. Це було let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
раніше.
Ви також можете це зробити за допомогою 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 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)
Якщо ви хочете викликати функцію через деякий час, щоб виконати щось, не використовуйте сон .
Відповідь @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 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
})
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
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}
це, мабуть, правильніше ✌️
Якщо ваш код уже працює у фоновому потоці, призупиніть потік, використовуючи цей метод у 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)
}
(Див. Інші відповіді щодо пропозицій, коли ваш код працює в основному потоці.)
Щоб створити просту затримку в часі, ви можете імпортувати Дарвін, а потім скористатися сном (секундами), щоб зробити затримку. Це займає лише цілі секунди, тому для більш точних вимірювань ви можете імпортувати Дарвіна і використовувати сплячий (мільйони секунди) для дуже точного вимірювання. Щоб перевірити це, я написав:
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()
})