Uygulamamı belirli bir noktada duraklatmak istiyorum. Başka bir deyişle, uygulamamın kodu yürütmesini istiyorum, ancak belirli bir noktada 4 saniye duraklatın ve kodun geri kalanıyla devam edin. Bunu nasıl yapabilirim?
Swift kullanıyorum.
Uygulamamı belirli bir noktada duraklatmak istiyorum. Başka bir deyişle, uygulamamın kodu yürütmesini istiyorum, ancak belirli bir noktada 4 saniye duraklatın ve kodun geri kalanıyla devam edin. Bunu nasıl yapabilirim?
Swift kullanıyorum.
Yanıtlar:
UI iş parçacığından çağrılırsa programınızı kilitleyecek bir uyku yerine, kullanmayı NSTimer
veya bir dağıtım zamanlayıcısını düşünün .
Ancak, geçerli iş parçacığında gerçekten bir gecikmeye ihtiyacınız varsa:
do {
sleep(4)
}
Bu sleep
UNIX işlevini kullanır .
Bir dispatch_after
bloğun kullanılması çoğu durumda sleep(time)
, uykunun yapıldığı iş parçacığının başka iş yapması engellendiğinden kullanmaktan daha iyidir . dispatch_after
üzerinde çalışılan ipliği kullanırken bu arada başka işler yapabilmesi için engellenmez.
Uygulamanızın ana iş parçacığı üzerinde çalışıyorsanız sleep(time)
, kullanıcı arayüzü bu süre zarfında yanıt vermediğinden, kullanımı uygulamanızın kullanıcı deneyimi için kötüdür.
İş parçacığını dondurmak yerine bir kod bloğunun yürütülmesini zamanladıktan sonra gönderir:
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)
hata veriyor:expression type is ambiguous without more context
.now() + .seconds(5)
o basitçe.now() + 5
Hızlı 3.0'da farklı yaklaşımlar arasında karşılaştırma
1. Uyku
Bu yöntemin geri çağrısı yoktur. Kodları 4 saniyede yürütülecek olan bu satırın hemen sonrasına yerleştirin. Zaman geçene kadar kullanıcının test düğmesi gibi UI öğeleriyle yinelenmesini durduracaktır. Uyku başladığında düğme biraz donmuş olsa da, aktivite göstergesi gibi diğer öğeler hala donmadan dönüyor. Uyku sırasında bu eylemi tekrar başlatamazsınız.
sleep(4)
print("done")//Do stuff here
2. Sevkıyat, Performans ve Zamanlayıcı
Bu üç yöntem benzer şekilde çalışır, hepsi arka plan iş parçacığında geri çağrılarla, sadece farklı sözdizimi ve biraz farklı özelliklerle çalışır.
Dağıtım, arka plan iş parçacığında bir şey çalıştırmak için yaygın olarak kullanılır. İşlev çağrısının bir parçası olarak geri çağırma özelliği vardır
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
print("done")
})
Performans aslında basitleştirilmiş bir zamanlayıcıdır. Gecikmeli bir zamanlayıcı ayarlar ve sonra seçici seçerek işlevi tetikler.
perform(#selector(callback), with: nil, afterDelay: 4.0)
func callback() {
print("done")
}}
Ve son olarak, zamanlayıcı ayrıca bu durumda yararlı olmayan geri aramayı tekrarlama yeteneği sağlar
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)
func callback() {
print("done")
}}
Tüm bu üç yöntem için, onları tetiklemek için düğmeye tıkladığınızda, kullanıcı arayüzü donmaz ve tekrar tıklamanıza izin verilir. Düğmeye tekrar tıklarsanız, başka bir zamanlayıcı ayarlanır ve geri arama iki kez tetiklenir.
Sonuç olarak
Dört yöntemin hiçbiri kendi başlarına yeterince iyi çalışmaz. sleep
kullanıcı etkileşimini devre dışı bırakır, böylece ekran " donar " (aslında değil) ve kötü kullanıcı deneyimi sağlar. Diğer üç yöntem ekranı dondurmaz, ancak birden çok kez tetikleyebilirsiniz ve çoğu zaman kullanıcının tekrar arama yapmasına izin vermeden önce geri aramayı beklemek istersiniz.
Böylece daha iyi bir tasarım, ekran engellemeli üç asenkron yöntemden birini kullanacaktır. Kullanıcı düğmeyi tıkladığında, tüm ekranı, üstte bir eğirme etkinliği göstergesiyle yarı saydam bir görünümle kaplayın ve kullanıcıya düğmenin tıklatıldığını bildirir. Ardından geri arama işlevindeki görünümü ve göstergeyi kaldırın, kullanıcıya işlemin düzgün bir şekilde yapıldığını vb. Söyler.
@objc
için geri arama işlevinin önüne eklemeniz gerektiğini unutmayın perform(...)
. Gibi:@objc func callback() {
Palle ile birlikte dispatch_after
kullanımın burada iyi bir seçim olduğunu kabul ediyorum . Ama muhtemelen GCD çağrılarını sevmiyorsunuz çünkü yazmak oldukça can sıkıcı . Bunun yerine bu kullanışlı yardımcıyı ekleyebilirsiniz :
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)
}
}
}
Şimdi kodunuzu aşağıdaki gibi bir arka plan iş parçacığında geciktirirsiniz :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Ana iş parçacığındaki kodun geciktirilmesi daha da basittir:
delay(bySeconds: 1.5) {
// delayed code, by default run in main thread
}
Bazı daha kullanışlı özelliklere sahip bir Çerçeveyi tercih ederseniz , HandySwift'i kontrol edin . Bunu projenize Carthage veya Accio aracılığıyla ekleyebilir ve daha sonra yukarıdaki örneklerde olduğu gibi kullanabilirsiniz:
import HandySwift
delay(by: .seconds(1.5)) {
// delayed code
}
DispatchTime
Swift 2'de mevcut olmadığı için daha önce tür dönüştürmeye ihtiyaç duyuldu let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
.
Bunu Swift 3 ile de yapabilirsiniz.
Bu işlevi gecikmeden sonra böyle yapın.
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 ve Xcode 10.1'de
Gecikmenin toplam 4 yolu var. Bu seçeneklerden 1'i bir süre sonra bir işlevi çağırmak veya yürütmek tercih edilir. Uyku () kullanımda en az bir durumdur.
Seçenek 1.
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.yourFuncHere()
}
//Your function here
func yourFuncHere() {
}
Seçenek 2.
perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)
//Your function here
@objc func yourFuncHere2() {
print("this is...")
}
Seçenek 3.
Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)
//Your function here
@objc func yourFuncHere3() {
}
Seçenek 4.
sleep(5)
Bir şeyleri yürütmek için bir süre sonra bir işlevi çağırmak istiyorsanız uyku kullanmayın .
@Nneonneo'nun cevabı kullanmayı önerdi, NSTimer
ancak nasıl yapılacağını göstermedi. Bu temel sözdizimidir:
let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)
İşte nasıl kullanılabileceğini gösteren çok basit bir proje. Bir düğmeye basıldığında, yarım saniyelik bir gecikmeden sonra bir işlevi çağıracak bir zamanlayıcı başlar.
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")
}
}
Kullanılması dispatch_time
(olduğu gibi Palle cevabı ) başka bir geçerli bir seçenektir. Ancak iptal etmek zordur . İle NSTimer
gerçekleşmeden önce, yapmanız gereken tüm çağrıdır, gecikmiş bir etkinliği iptal etmek
timer.invalidate()
Kullanılması sleep
tüm iş parçacığı üzerinde yapılıyor durur beri, özellikle ana iş parçacığı üzerinde, tavsiye edilmez.
Tam yanıtım için buraya bakın .
Gecikme işlevini kolayca kullanmak için uzantı oluşturabilirsiniz (Sözdizimi: 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'da nasıl kullanılır
self.delay(0.1, closure: {
//execute code
})
Kodunuz zaten bir arka plan iş parçacığında çalışıyorsa , Foundation'da bu yöntemi kullanarak iş parçacığını duraklatın :Thread.sleep(forTimeInterval:)
Örneğin:
DispatchQueue.global(qos: .userInitiated).async {
// Code is running in a background thread already so it is safe to sleep
Thread.sleep(forTimeInterval: 4.0)
}
(Kodunuz ana iş parçacığında çalışırken öneriler için diğer yanıtlara bakın.)
Basit bir zaman gecikmesi oluşturmak için Darwin'i içe aktarabilir ve sonra gecikmeyi yapmak için uyku (saniye) kullanabilirsiniz. Ancak bu sadece birkaç saniye sürer, bu nedenle daha hassas ölçümler için Darwin'i içe aktarabilir ve çok hassas ölçümler için uyku modunu (saniyenin milyonda biri) kullanabilirsiniz. Bunu test etmek için şunu yazdım:
import Darwin
print("This is one.")
sleep(1)
print("This is two.")
usleep(400000)
print("This is three.")
Hangi baskılar, daha sonra 1 saniye bekler ve baskılar, daha sonra 0.4 saniye bekler, sonra baskılar. Hepsi beklendiği gibi çalıştı.
bu en basit
delay(0.3, closure: {
// put her any code you want to fire it with delay
button.removeFromSuperview()
})