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


답변:


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.