ฉันต้องการหยุดแอพชั่วคราวที่จุดใดจุดหนึ่ง กล่าวอีกนัยหนึ่งฉันต้องการให้แอปของฉันเรียกใช้รหัส แต่เมื่อถึงจุดหนึ่งหยุดชั่วคราวเป็นเวลา 4 วินาทีแล้วดำเนินการต่อโดยใช้รหัสที่เหลือ ฉันจะทำสิ่งนี้ได้อย่างไร
ฉันใช้สวิฟท์
ฉันต้องการหยุดแอพชั่วคราวที่จุดใดจุดหนึ่ง กล่าวอีกนัยหนึ่งฉันต้องการให้แอปของฉันเรียกใช้รหัส แต่เมื่อถึงจุดหนึ่งหยุดชั่วคราวเป็นเวลา 4 วินาทีแล้วดำเนินการต่อโดยใช้รหัสที่เหลือ ฉันจะทำสิ่งนี้ได้อย่างไร
ฉันใช้สวิฟท์
คำตอบ:
แทนที่จะเป็นโหมดสลีปซึ่งจะล็อกโปรแกรมของคุณหากถูกเรียกจากเธรด UI ลองใช้NSTimer
หรือตั้งเวลาส่ง
แต่ถ้าคุณต้องการความล่าช้าในเธรดปัจจุบัน:
do {
sleep(4)
}
สิ่งนี้ใช้sleep
ฟังก์ชันจาก UNIX
การใช้dispatch_after
บล็อกในกรณีส่วนใหญ่ดีกว่าการใช้sleep(time)
เนื่องจากเธรดที่ดำเนินการสลีปนั้นถูกบล็อกไม่ให้ทำงานอื่น เมื่อใช้dispatch_after
เธรดที่ทำงานอยู่จะไม่ถูกบล็อกดังนั้นจึงสามารถทำงานอื่นได้ในระหว่างนี้
หากคุณกำลังทำงานกับเธรดหลักของแอปพลิเคชันของคุณการใช้sleep(time)
ไม่ดีสำหรับประสบการณ์การใช้งานแอพของคุณเนื่องจาก UI ไม่ตอบสนองในช่วงเวลานั้น
ส่งหลังจากกำหนดเวลาการดำเนินการของบล็อกของรหัสแทนที่จะแช่แข็งเธรด:
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
เปรียบเทียบระหว่างวิธีการต่าง ๆ ใน swift 3.0
1. นอนหลับ
วิธีนี้ไม่มีการโทรกลับ ใส่รหัสโดยตรงหลังจากบรรทัดนี้จะถูกดำเนินการใน 4 วินาที มันจะหยุดผู้ใช้ไม่ให้ทำการวนซ้ำด้วยองค์ประกอบ UI เช่นปุ่มทดสอบจนกว่าจะหมดเวลา แม้ว่าปุ่มจะเป็นชนิดของการแช่แข็งเมื่อนอนหลับในองค์ประกอบอื่น ๆ เช่นตัวบ่งชี้กิจกรรมยังคงหมุนโดยไม่ต้องแช่แข็ง คุณไม่สามารถเรียกการกระทำนี้อีกครั้งในระหว่างที่หลับ
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")
}}
สำหรับวิธีการทั้งสามนี้เมื่อคุณคลิกที่ปุ่มเพื่อเรียกใช้ UI จะไม่หยุดและคุณได้รับอนุญาตให้คลิกอีกครั้ง หากคุณคลิกที่ปุ่มอีกครั้งตัวจับเวลาอื่นจะถูกตั้งค่าและการโทรกลับจะถูกเรียกสองครั้ง
สรุปแล้ว
ไม่มีวิธีใดในสี่ที่ทำงานได้ดีพอเพียงด้วยตัวเอง sleep
จะปิดการใช้งานการโต้ตอบของผู้ใช้ดังนั้นหน้าจอ " ค้าง " (ไม่จริง) และส่งผลให้ประสบการณ์การใช้งานที่ไม่ดี อีกสามวิธีจะไม่หยุดหน้าจอ แต่คุณสามารถเรียกใช้หลายครั้งและส่วนใหญ่คุณต้องการรอจนกว่าคุณจะได้รับการโทรกลับก่อนที่จะอนุญาตให้ผู้ใช้โทรออกอีกครั้ง
ดังนั้นการออกแบบที่ดีกว่าจะใช้หนึ่งในสามวิธีการซิงค์กับการบล็อกหน้าจอ เมื่อผู้ใช้คลิกที่ปุ่มให้ครอบคลุมทั้งหน้าจอด้วยมุมมองโปร่งแสงพร้อมตัวบ่งชี้กิจกรรมการหมุนด้านบนบอกผู้ใช้ว่าการคลิกปุ่มนั้นกำลังถูกจัดการ จากนั้นลบมุมมองและตัวบ่งชี้ในฟังก์ชั่นโทรกลับบอกผู้ใช้ว่าการดำเนินการได้รับการจัดการอย่างเหมาะสม ฯลฯ
@objc
ฟังก์ชัน callback ด้านหน้าสำหรับperform(...)
ตัวเลือก ชอบโดย:@objc func callback() {
ฉันเห็นด้วยกับ Palle ว่าการใช้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
}
ถ้าคุณชอบกรอบที่ยังมีคุณสมบัติที่มีประโยชน์มากขึ้นบางแล้วเช็คเอาท์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
(ตามคำตอบของ Palle ) เป็นอีกตัวเลือกที่ใช้ได้ แต่ก็เป็นเรื่องยากที่จะยกเลิก ด้วย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+)
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.main.asyncAfter(deadline: .now() + 0.5, execute: {
// your code hear
})
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*/}
น่าจะถูกต้องมากขึ้น✌️
หากรหัสของคุณกำลังทำงานอยู่ในเธรดพื้นหลังหยุดเธรดชั่วคราวโดยใช้วิธีนี้ในมูลนิธิ :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)
}
(ดูคำตอบอื่น ๆ สำหรับคำแนะนำเมื่อโค้ดของคุณทำงานบนเธรดหลัก)
หากต้องการสร้างการหน่วงเวลาอย่างง่ายคุณสามารถนำเข้าดาร์วินจากนั้นใช้โหมดสลีป (วินาที) เพื่อหน่วงเวลา แม้ว่าจะใช้เวลาเพียงไม่กี่วินาทีเท่านั้นดังนั้นสำหรับการวัดที่แม่นยำยิ่งขึ้นคุณสามารถนำเข้าดาร์วินและใช้ 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()
})