Как я могу использовать Timer (ранее NSTimer) в Swift?

257

Я попытался

var timer = NSTimer()
timer(timeInterval: 0.01, target: self, selector: update, userInfo: nil, repeats: false)

Но я получил ошибку, сказав

'(timeInterval: $T1, target: ViewController, selector: () -> (), userInfo: NilType, repeats: Bool) -> $T6' is not identical to 'NSTimer'
user3225917
источник
1
"Как я могу использовать NSTimer в Swift?" - так же, как вы используете его в Objective-C. Его API не изменился.
Парамагнитный круассан

Ответы:

535

Это будет работать:

override func viewDidLoad() {
    super.viewDidLoad()
    // Swift block syntax (iOS 10+)
    let timer = Timer(timeInterval: 0.4, repeats: true) { _ in print("Done!") }
    // Swift >=3 selector syntax
    let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
    // Swift 2.2 selector syntax
    let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: #selector(MyClass.update), userInfo: nil, repeats: true)
    // Swift <2.2 selector syntax
    let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}

// must be internal or public. 
@objc func update() {
    // Something cool
}

Для Swift 4 метод, для которого вы хотите получить селектор, должен быть доступен Objective-C, поэтому @objcатрибут должен быть добавлен в объявление метода.

Оскар Сванрос
источник
2
Я бы добавил, что класс с этими методами должен быть объектом NSObject, иначе вы получите нераспознанную ошибку селектора
Joshua
27
Начиная с Xcode 6.1, мне пришлось добавить «@objc» в заголовок функции следующим образом: «@objc func update () {». Без этого приложение вылетает при первом пожаре.
Кев
Вы можете объявить таймер Var: NSTimer! Первоначально и использовать его при необходимости!
Нигилан
1
Возможно, более полезная версия синтаксиса блока: let timer = Timer.scheduledTimer (withTimeInterval: время ожидания, повторы: ложные) {_ in print ("Done.")}
Тео Сартори
Вы не можете использовать 'let timer = Timer (timeInterval: 0.4, repeat: true) {_ in print ("Done!")} ", Это не запустит таймер, и вы не сможете заставить его повторяться. Вы должны использовать Timer.scheduledTimer.
Siamaster
149

Повторное событие

Вы можете использовать таймер для выполнения действия несколько раз, как показано в следующем примере. Таймер вызывает метод для обновления метки каждые полсекунды.

введите описание изображения здесь

Вот код для этого:

import UIKit

class ViewController: UIViewController {

    var counter = 0
    var timer = Timer()

    @IBOutlet weak var label: UILabel!

    // start timer
    @IBAction func startTimerButtonTapped(sender: UIButton) {
        timer.invalidate() // just in case this button is tapped multiple times

        // start the timer
        timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
    }

    // stop timer
    @IBAction func cancelTimerButtonTapped(sender: UIButton) {
        timer.invalidate()
    }

    // called every time interval from the timer
    func timerAction() {
        counter += 1
        label.text = "\(counter)"
    }
}

Задержка события

Вы также можете использовать таймер, чтобы запланировать одноразовое событие на какое-то время в будущем. Основное отличие от приведенного выше примера заключается в том, что вы используете repeats: falseвместо true.

timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)

В приведенном выше примере вызывается метод с именем delayedActionдве секунды после установки таймера. Это не повторяется, но вы все равно можете позвонитьtimer.invalidate() если вам нужно отменить событие до того, как оно произойдет.

Ноты

  • Если есть шанс запустить экземпляр таймера несколько раз, убедитесь, что сначала вы сделали недействительным старый экземпляр таймера. В противном случае вы потеряете ссылку на таймер и больше не сможете его остановить. (см. этот Q & A )
  • Не используйте таймеры, когда они не нужны. См. Раздел таймеров в Руководстве по энергоэффективности для приложений для iOS .

Связанный

Suragch
источник
1
@raddevus, спасибо, что сообщили мне об этом. Я удалил старый комментарий Swift 3.
Сурагч
31

Обновлен до Swift 4, используя userInfo:

class TimerSample {

    var timer: Timer?

    func startTimer() {
        timer = Timer.scheduledTimer(timeInterval: 5.0,
                                     target: self,
                                     selector: #selector(eventWith(timer:)),
                                     userInfo: [ "foo" : "bar" ],
                                     repeats: true)
    }

    // Timer expects @objc selector
    @objc func eventWith(timer: Timer!) {
        let info = timer.userInfo as Any
        print(info)
    }

}
igraczech
источник
2
Покажите рабочий пример, что означают «пользовательский» и «данные», если функция ожидает NSTimerобъект
Carlos.V
1
Это действительно не имеет значения. Вы можете хранить все, что вам нужно, в словаре userInfo, в данном случае это произвольная пара ключ-значение.
igraczech
Это полезно, но не работает в Swift 3, рабочий пример: Timer.scheduledTimer (timeInterval: 1.0, target: self, селектор: #selector (событие), userInfo: «Информация отправлена», повторяется: true)
Бобби
28

Начиная с iOS 10, также существует новый метод фабрики Timer на основе блоков, который чище, чем использование селектора:

    _ = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
        label.isHidden = true
    }
Джош Хоманн
источник
1
То, как вы это делаете, не лучше ли просто удалить _ = и начать с самого начала Timer?
Мед
2
Вы можете опустить _ =, если вы отключите предупреждение о неиспользованном значении или если вам просто наплевать на предупреждения. Я не люблю проверять код с предупреждениями.
Джош Хоманн
22

Swift 3, предварительно iOS 10

func schedule() {
    DispatchQueue.main.async {
      self.timer = Timer.scheduledTimer(timeInterval: 20, target: self,
                                   selector: #selector(self.timerDidFire(timer:)), userInfo: nil, repeats: false)
    }
  }

  @objc private func timerDidFire(timer: Timer) {
    print(timer)
  }

Swift 3, iOS 10+

DispatchQueue.main.async {
      self.timer = Timer.scheduledTimer(withTimeInterval: 20, repeats: false) { timer in
        print(timer)
      }
    }

Ноты

  • Это должно быть в главной очереди
  • Функция обратного вызова может быть общедоступной, частной, ...
  • Функция обратного вызова должна быть @objc
onmyway133
источник
1
Насколько я понимаю, что только обратный вызов таймера должен быть в главной очереди и что следующее будет немного более эффективным: self.timer = Timer.scheduledTimer (withTimeInterval: 20, repeat: false) {timer in DispatchQueue.main.async {print (таймер)}}
Матье Френетт
Мой таймер не сработал от одного из моих Объектов, и это сделало трюк :)
Reimond Hill
@ReimondHill Вы должны изменитьсяtimeInterval
onmyway133
17

Проверить с:

Swift 2

var timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("update"), userInfo: nil, repeats: true)

Свифт 3, 4, 5

var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
Midhun MP
источник
2
Я уже пробовал это, но он говорит: «Не удалось найти перегрузку для« init », который принимает предоставленные аргументы»
user3225917
1
То же самое здесь, я получил ошибку «Не удалось найти перегрузку для« init », которая принимает предоставленные аргументы». Эта линия действительно работает?
Яншун Тай
Я получаю ту же ошибку, что и @yangshun. Какой тип объекта должен selfбыть? UIView в порядке?
SimplGy
@SimpleAsCouldBe: да, все в порядке
депутат Midhun
func amountSubmitSuccess () {self.view.hideToastActivity () self.view.makeToast (сообщение: «Сумма успешно зарегистрирована») var timer = NSTimer.scheduledTimerWithTimeInterval (0.5, target: self, selector: "moveToBidderPage", userInfo: nil, repeat: false)} func moveToBidderPage () {let loginPageView = self.storyboard? .instantiateViewControllerWithIdentifier ("bidderpageID") как! BidderPage self.navigationController? .PushViewController (loginPageView, animated: true)}
AG
11

Вам нужно будет использовать Timer вместо NSTimer в Swift 3.

Вот пример:

Timer.scheduledTimer(timeInterval: 1, 
    target: self, 
    selector: #selector(YourController.update), 
    userInfo: nil, 
    repeats: true)

// @objc selector expected for Timer
@objc func update() {
    // do what should happen when timer triggers an event
}
Ондрей Квасновский
источник
11

Swift 5

Я лично предпочитаю Таймер с закрытием блока:

    Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
       // TODO: - whatever you want
    }
ВИССА
источник
Имейте в виду, это доступно только в MacOS 10.12 или новее. Не уверен насчет ios.
Джефф-х
Это доступно и в iOS.
Wissa
7

для swift 3 и Xcode 8.2 (приятно иметь блоки, но если вы компилируете для iOS9 и хотите userInfo):

...

        self.timer = Timer(fireAt: fire,
                           interval: deltaT,
                           target: self,
                           selector: #selector(timerCallBack(timer:)),
                           userInfo: ["custom":"data"],
                           repeats: true)

        RunLoop.main.add(self.timer!, forMode: RunLoopMode.commonModes)
        self.timer!.fire()
}

func timerCallBack(timer: Timer!){
        let info = timer.userInfo
        print(info)
    }
ingconti
источник
6

SimpleTimer (Swift 3.1)

Зачем?

Это простой класс таймера в swift, который позволяет вам:

  • Таймер с локальной областью действия
  • змеевидных
  • Один лайнер
  • Используйте регулярные обратные вызовы

Использование:

SimpleTimer(interval: 3,repeats: true){print("tick")}.start()//Ticks every 3 secs

Код:

class SimpleTimer {/*<--was named Timer, but since swift 3, NSTimer is now Timer*/
    typealias Tick = ()->Void
    var timer:Timer?
    var interval:TimeInterval /*in seconds*/
    var repeats:Bool
    var tick:Tick

    init( interval:TimeInterval, repeats:Bool = false, onTick:@escaping Tick){
        self.interval = interval
        self.repeats = repeats
        self.tick = onTick
    }
    func start(){
        timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(update), userInfo: nil, repeats: true)//swift 3 upgrade
    }
    func stop(){
        if(timer != nil){timer!.invalidate()}
    }
    /**
     * This method must be in the public or scope
     */
    @objc func update() {
        tick()
    }
}
eonist
источник
Как тогда остановить таймер внутри этого блока при некоторых условиях?
Мобильный разработчик iOS Android
Просто сохраните ссылку в таймере в классе, затем просто вызовите стоп. Компилятор xcode сообщит вам, нужно ли ему бежать и т. Д.
eonist
3
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)

И Создай Забаву По Названию createEnemy

fund createEnemy ()
{
do anything ////
}
Халед Хамди
источник
3

Сначала объявите свой таймер

var timer: Timer?

Затем добавьте строку в viewDidLoad () или в любую функцию, которую вы хотите запустить таймер

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(action), userInfo: nil, repeats: false)

Это функция, которую вы перезвоните, чтобы сделать что-то, что должно быть @objc

@objc func action () {
print("done")
}
гениальность
источник
2

В Swift 3 что-то вроде этого с @objc:

func startTimerForResendingCode() {
    let timerIntervalForResendingCode = TimeInterval(60)
    Timer.scheduledTimer(timeInterval: timerIntervalForResendingCode,
                         target: self,
                         selector: #selector(timerEndedUp),
                         userInfo: nil,
                         repeats: false)
}




@objc func timerEndedUp() {
    output?.timerHasFinishedAndCodeMayBeResended()
}
Ник Ков
источник
1

Если вы инициируете метод таймера

let timer = Timer(timeInterval: 3, target: self, selector: #selector(update(_:)), userInfo: [key : value], repeats: false)

func update(_ timer : Timer) {

}

затем добавьте его в цикл с помощью метода, который не будет вызываться другим селектором

RunLoop.main.add(timer!, forMode: .defaultRunLoopMode)

ПРИМЕЧАНИЕ. Если вы хотите, чтобы это повторялось, сделайте true для повторов и сохраните ссылку на таймер, иначе метод обновления не будет вызван.

Если вы используете этот метод.

Timer.scheduledTimer(timeInterval: seconds, target: self, selector: #selector(update(_:)), userInfo: nil, repeats: true)

сохраните ссылку для последующего использования, если повторы верны.

Сурджит Раджпут
источник
0

Я пытался сделать в классе NSObject, и это сработало для меня:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {  
print("Bang!") }
Альваро Агуэро
источник
-2

NSTimer был переименован в Timer в Swift 4.2. этот синтаксис будет работать в 4.2:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)
Jamtrax Reunion
источник
Переименование произошло в Swift 3, и другие ответы уже сделали обновление ...
Эрик Ая