Timer.scheduledTimer не работает в Swift 3

Я хочу вызвать метод func adjustmentBestSongBpmHeartRate() каждые 1,1 секунды. Я использовал таймер, но он не работает. Я прочитал документ и нашел много примеров кода, он все еще работает! Я что-то пропустил?

 timer = Timer.scheduledTimer(timeInterval: 1.1, target: self, selector: #selector(self.adjustmentBestSongBpmHeartRate), userInfo: nil, repeats: false)
 timer.fire()

 func adjustmentBestSongBpmHeartRate() {
    print("frr")
 }

Ответ 1

Я обнаружил, что создание таймера в OperationQueue Operation не работает. Я предполагаю, что это потому, что нет runloop.

Поэтому следующий код решил мою проблему:

DispatchQueue.main.async {
    // timer needs a runloop?
    self.timeoutTimer = Timer.scheduledTimer(timeInterval: self.timeout, target: self, selector: #selector(self.onTimeout(_:)), userInfo: nil, repeats: false)
}

Ответ 2

Методы таймера с селектором должны иметь один параметр: сам таймер. Таким образом, ваш код должен выглядеть так: 1

Timer.scheduledTimer(timeInterval: 1.1, 
    target: self, 
    selector: #selector(self.adjustmentBestSongBpmHeartRate(_:), 
    userInfo: nil, 
    repeats: false)

@objc func adjustmentBestSongBpmHeartRate(_ timer: Timer) {
    print("frr")
 }

Обратите внимание, что если ваше приложение работает только на iOS> = 10, вы можете использовать новый метод, который использует для вызова блок, а не цель/селектор. Намного чище и безопаснее:

class func scheduledTimer(withTimeInterval interval: TimeInterval, 
    repeats: Bool, 
    block: @escaping (Timer) -> Void) -> Timer

Этот код будет выглядеть так:

 timer = Timer.scheduledTimer(withTimeInterval: 1.1, 
        repeats: false) {
    timer in
    //Put the code that be called by the timer here.
    print("frr")
    }

Обратите внимание, что если вашему блоку таймера/закрытию требуется доступ к переменным экземпляра из вашего класса, вы должны быть особенно осторожны с self. Вот хороший шаблон для такого рода кода:

 timer = Timer.scheduledTimer(withTimeInterval: 1.1, 
        repeats: false) {

    //"[weak self]" creates a "capture group" for timer
    [weak self] timer in

    //Add a guard statement to bail out of the timer code 
    //if the object has been freed.
    guard let strongSelf = self else {
        return
    }
    //Put the code that be called by the timer here.
    print(strongSelf.someProperty)
    strongSelf.someOtherProperty = someValue
    }

Edit:

1: Я должен добавить, что метод, который вы используете в селекторе, должен использовать динамическую диспетчеризацию Objective-C. Вы можете объявить метод с помощью квалификатора @objc, вы можете объявить весь класс, который определяет селектор, с помощью квалификатора @objc, или вы можете сделать класс, который определяет селектор, подклассом NSObject или любым классом, который наследует из NSOBject. (Довольно часто определяют метод, который таймер вызывает внутри UIViewController, который является подклассом NSObject, поэтому он "просто работает".

EDIT

В Swift 4 методы, которые необходимо вызывать из Objective-C, теперь должны быть индивидуально помечены квалификатором @objc. Комментарий "это просто работает" больше не соответствует действительности.

Ответ 3

Swift 3

В моем случае это сработало после того, как я добавил в свой метод префикс @obj

Class TestClass {
   private var timer: Timer?

   func start() {
        guard timer == nil else { return }
        timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(handleMyFunction), userInfo: nil, repeats: false)
    }

    func stop() {
        guard timer != nil else { return }
        timer?.invalidate()
        timer = nil
    }


    @objc func handleMyFunction() {
        // Code here
    }
}

Ответ 4

Попробуйте это -

if #available(iOS 10.0, *) {
     self.timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
         self.update()
     })
} else {
     self.timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.update), userInfo: nil, repeats: false)
}

В основном проблема должна была быть из-за версии iOS для мобильных устройств.

Ответ 5

Я решил вопрос, заданный мной. Я использую apple watch для управления своим приложением iphone. Я пытаюсь нажать кнопку на яблочном часе, чтобы представить новый диспетчер представлений на iphone.

Когда я пишу таймер в override func viewDidLoad(), таймер не работает. Я перемещаю Таймер на override func viewWillAppear(), он работает.

Я думаю, может быть, что-то не так с контролем над яблочными часами


Ответ 6

Я обнаружил, что если вы попытаетесь инициализировать таймер непосредственно на уровне класса, он не будет работать, если вы нацелены на селектор в этом же классе. Когда он срабатывает, он не может найти селектор.

Чтобы обойти это, я инициализирую таймер только после того, как объект, содержащий селектор, был инициализирован. Если это в том же классе, поместите код инициализации в ViewDidLoad или аналогичный. Просто нет в инициализаторе. Тогда это будет работать. Очередь отправки не требуется.

Кроме того, вам не нужно использовать селектор, который принимает таймер в качестве параметра. Вы можете, но вопреки ответу с тонной голосов, что на самом деле это не так, или, точнее говоря, он отлично работает для меня без него, так же, как у вас это без него.

Между прочим, я думаю, что причина, по которой сработала очередь отправки, заключается в том, что вы заставляете таймер создаваться после инициализации объекта, подтверждая мое приведенное выше утверждение.

let timer:Timer?

override func viewDidLoad(){
    super.viewDidLoad()

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

    timer.fire()

}

func adjustmentBestSongBpmHeartRate() {
    print("frr")
}

Примечание. Это код, набранный из памяти, а не скопированный из Xcode, поэтому он может не скомпилироваться, но, надеюсь, вы поняли идею.

Ответ 7

Swift 5, Swift 4 Простой способ вызова только с асинхронной очередью отправки

DispatchQueue.main.async 
{
   self.andicator.stopAnimating()
   self.bgv.isHidden = true

   Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { _ in

       obj.showAlert(title: "Successfully!", message: "Video save successfully to Library directory.", viewController: self)

   })
}

Ответ 8

Swift3

var timer = Timer()

timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.compruebaConexion), userInfo: nil, repeats: true)

Ответ 9

мои два цента. Я читал о "didLoad" и при вызове. поэтому мы можем использовать задержку:

class ViewController: UIViewController {

    var timer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()
        startTimer()
    }


    final func killTimer(){
        self.timer?.invalidate()
        self.timer = nil
    }


    final private func startTimer() {

        // make it re-entrant:
        // if timer is running, kill it and start from scratch
        self.killTimer()
        let fire = Date().addingTimeInterval(1)
        let deltaT : TimeInterval = 1.0

        self.timer = Timer(fire: fire, interval: deltaT, repeats: true, block: { (t: Timer) in

            print("hello")

        })

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

    }