스위프트에서 지연을 만드는 방법?


답변:


270

UI 스레드에서 호출 된 경우 프로그램을 잠그는 절전 모드 대신 NSTimer디스패치 타이머 사용을 고려하십시오 .

그러나 현재 스레드에서 지연이 정말로 필요한 경우 :

do {
    sleep(4)
}

이것은 sleepUNIX 의 기능을 사용합니다 .


4
잠이 다윈을 가져 오지 않고 작동합니다. 이것이 변경인지 확실하지 않습니다
Dan Beaulieu

17
1 초 해상도보다 더 정확한 것이 필요한 경우 usleep ()도 작동합니다.
prewett

18
usleep ()은 백만 분의 1 초가 걸리므로 usleep (1000000)은 1 초 동안 잠들 것입니다.
Harris

40
"하지 마십시오"프리앰블을 짧게 유지해 주셔서 감사합니다.
Dan Rosenstark

2
sleep ()을 사용하여 장기 실행 백그라운드 프로세스를 시뮬레이션합니다.
William T. Mallard

345

dispatch_after대부분의 경우 sleep(time)절전을 수행하는 스레드가 다른 작업을 수행하지 못하도록 차단하는 것보다 블록을 사용하는 것이 좋습니다 . dispatch_after작업중 인 스레드를 사용할 때 차단되지 않으므로 그 동안 다른 작업을 수행 할 수 있습니다.
응용 프로그램의 주요 스레드에서 작업하는 경우 sleep(time)해당 시간 동안 UI가 응답하지 않기 때문에 앱의 사용자 경험에 좋지 않습니다.

스레드 후 중단하는 대신 코드 블록 실행을 예약 한 후 전달 :

스위프트 ≥ 3.0

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

스위프트 <3.0

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
}

1
주기적 행동은 어떻습니까?
qed

1
애플 개발자 문서 에서이 기사에 설명 된 주기적 작업에 gcd를 사용할 가능성 있지만 NSTimer를 사용하는 것이 좋습니다. 이 답변 은 신속하게 정확하게 수행하는 방법을 보여줍니다.
Palle

2
Xcode 8.2.1에 대한 코드가 업데이트되었습니다. 이전 .now() + .seconds(4): 오류가 있습니다expression type is ambiguous without more context
리차드

큐에서 호출 된 함수를 어떻게 일시 중지 할 수 있습니까? @Palle
Mukul More

14
더 이상 추가 .now() + .seconds(5)할 필요가 없습니다.now() + 5
cnzac

45

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사용자 상호 작용을 비활성화하므로 화면이 " 정지 (실제로는 아님)"하고 사용자 경험이 좋지 않습니다. 다른 세 가지 방법은 화면을 정지시키지 않지만 여러 번 트리거 할 수 있으며 대부분의 경우 사용자가 다시 전화를 걸기 전에 전화를받을 때까지 기다립니다.

따라서 더 나은 디자인은 화면 차단과 함께 세 가지 비동기 방법 중 하나를 사용하는 것입니다. 사용자가 버튼을 클릭하면 상단에 회전 활동 표시기가있는 반투명보기로 전체 화면을 덮어 버튼 클릭이 처리되고 있음을 사용자에게 알립니다. 그런 다음 콜백 기능에서보기 및 표시기를 제거하여 사용자에게 조치가 올바르게 처리되었음을 알리십시오.


1
Swift 4에서는 objc 유추가 해제 된 상태 @objc에서 perform(...)옵션에 대한 콜백 함수 앞에 추가해야합니다 . 그래서처럼@objc func callback() {
리앤

32

내가 사용하는 것을의 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
}

좀 더 편리한 기능을 가진 Framework 를 선호한다면 HandySwift 를 확인 하십시오 . Carthage 또는 Accio통해 프로젝트에 추가 한 다음 위의 예와 동일하게 사용할 수 있습니다.

import HandySwift    

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

이것은 매우 도움이됩니다. swift 3.0 버전으로 업데이트 하시겠습니까? 감사합니다
grahamcracker1234

HandySwift를 Swift 3로 마이그레이션하기 시작했으며 다음 주에 새 버전을 출시 할 계획입니다. 그런 다음 위의 스크립트도 업데이트하겠습니다. 계속 지켜봐 주시기 바랍니다.
Jeehut

HandySwift에서 Swift 3 로의 마이그레이션이 완료되었으며 버전 1.3.0에서 릴리스되었습니다. 또한 Swift 3에서 작동하도록 위의 코드를 업데이트했습니다! :)
Jeehut

1
NSEC_PER_SEC를 곱한 다음 다시 나누는 이유는 무엇입니까? 중복되지 않습니까? (예 : Double (Int64 (seconds * Double (NSEC_PER_SEC))) / Double (NSEC_PER_SEC)) 'DispatchTime.now () + Double (seconds)'만 쓸 수 없습니까?
Mark A. Donohoe

1
@MarqueIV 감사합니다. 물론 그렇습니다. 방금 코드를 업데이트했습니다. Xcode 8에서 자동으로 변환 된 결과였으며 DispatchTimeSwift 2에서는 사용할 수 없었던 유형 변환이 필요했습니다 let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))).
Jeehut

24

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

23

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)

무언가를 실행하기 위해 일정 시간 후에 함수를 호출하려면 sleep을 사용하지 마십시오 .


19

NSTimer

@nneonneo의 답변은 사용을 제안 NSTimer했지만 수행 방법을 보여주지는 않았습니다. 이것은 기본 구문입니다.

let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)

사용 방법을 보여주는 매우 간단한 프로젝트는 다음과 같습니다. 버튼을 누르면 타이머가 시작되어 0.5 초 후에 기능을 호출합니다.

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스레드에서 수행되는 모든 작업을 중지하므로 특히 주 스레드에서 사용 하지 않는 것이 좋습니다.

더 자세한 답변 은 여기 를 참조 하십시오 .


7

Swift 3.0에서 다음 구현을 시도하십시오

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

용법

delayWithSeconds(1) {
   //Do something
}

7

지연 기능을 쉽게 사용할 수 있도록 확장 기능을 만들 수 있습니다 (구문 : 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
})

6

1 초 미만의 지연을 설정해야하는 경우 .seconds 매개 변수를 설정할 필요가 없습니다. 나는 이것이 누군가에게 유용하기를 바랍니다.

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
        // your code hear
})

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

4
이렇게 DispatchQueue.main.asyncAfter(deadline: .now() + 4) {/*Do stuff*/}✌️입니다 아마 더 정확한
eonist

5

코드가 이미 백그라운드 스레드에서 실행중인 경우 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)
}

(코드가 메인 스레드에서 실행될 때 제안에 대한 다른 답변을 참조하십시오.)


3

간단한 시간 지연을 만들려면 Darwin을 가져온 다음 sleep (seconds)을 사용하여 지연을 수행 할 수 있습니다. 단 몇 초 밖에 걸리지 않으므로보다 정확한 측정을 위해서는 Darwin을 가져 와서 매우 정확한 측정을 위해 usleep (백만 분의 1 초)을 사용할 수 있습니다. 이것을 테스트하기 위해 다음과 같이 썼습니다.

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

인쇄 후 1 초 동안 기다렸다가 인쇄 한 다음 0.4 초 동안 기다렸다가 인쇄합니다. 모두 예상대로 작동했습니다.


-3

이것은 가장 간단합니다

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

2
이것은 내가 생각하는 것처럼 'HandySwift'Cocoapod에 포함 된 Foundation의 일부가 아닙니다. 당신은 당신의 대답에서 그것을 명확히해야합니다.
Jan Nash

스위프트에는 무언가 일어나기를 기다리거나 기다리지 않는 것이 있습니까?
Saeed Rahmatolahi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.