Обратный отсчет с несколькими десятичными слотами с использованием NSTimer в Swift

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

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var labelTime: UILabel!

    var counter = 10.0000000

    var labelValue: Double {
        get {
            return NSNumberFormatter().numberFromString(labelTime.text!)!.doubleValue
        }
        set {
            labelTime.text = "\(newValue)"
        }
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        labelValue = counter
        var timer = NSTimer.scheduledTimerWithTimeInterval(0.0000001, target: self, selector: ("update"), userInfo: nil, repeats: true)
    }

    func update(){
        labelValue -= 0.0000001
    }


}

Что происходит, так это то, что мой обратный отсчет очень медленный, он просто не работает, и потребуется около 1 часа, чтобы добраться до 0 секунд, а не всего 10 секунд. Любые идеи? Какие изменения я должен внести в свой код? Спасибо


person Pedro Cabaço    schedule 19.06.2015    source источник
comment
Не могли бы вы отредактировать и разделить код на несколько строк. Трудно читать (и понимать), когда это на одной строке. Или это обычная практика для swift?   -  person kebs    schedule 19.06.2015


Ответы (2)


Таймеры не являются сверхточными, а разрешение NSTimer составляет около 1/50 секунды.

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

Вместо того, чтобы пытаться использовать таймер для уменьшения чего-либо каждый раз, когда он срабатывает, создайте таймер, который срабатывает примерно 50 раз в секунду, и пусть он использует математику часов для обновления дисплея на основе оставшегося времени:

var futureTime: NSTimeInterval 

override func viewDidLoad() {
    super.viewDidLoad()
    labelValue = counter

    //FutureTime is a value 10 seconds in the future.
    futureTime = NSDate.timeIntervalSinceReferenceDate() + 10.0 

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

func update(timer: NSTimer)
{
  let timeRemaining = futureTime - NSDate.timeIntervalSinceReferenceDate()
  if timeRemaining > 0.0
  {
    label.text = String(format: "%.07f", timeRemaining)
  }
  else
  {
    timer.invalidate()
    //Force the label to 0.0000000 at the end
    label.text = String(format: "%.07f", 0.0)
  }
}
person Duncan C    schedule 19.06.2015
comment
Я немного отредактировал свой пример кода. Смотрите обновленную версию. - person Duncan C; 19.06.2015
comment
это отлично работает! Благодарю вас! В Swift, как я могу сделать так, чтобы у моего поплавка было только 7 десятичных регистров? Похоже, это не работает с @%.07f - person Pedro Cabaço; 19.06.2015
comment
Что это дает вам? Это не работает невероятно, безумно UN-полезно. - person Duncan C; 19.06.2015
comment
Ахах извините. Теперь он работает, пришлось удалить "@" - person Pedro Cabaço; 19.06.2015
comment
Ой, извини. Это привычка Objective-C, от которой трудно избавиться. (Исправлено) - person Duncan C; 19.06.2015
comment
Эй, извините, что беспокою вас, но мне интересно, как я могу повысить точность обратного отсчета таймера? Я выполняю println, и он не проходит через все слоты, когда у меня есть 3 десятичных слота, и я хочу, чтобы он проходил через КАЖДОЕ число... Как? ПОЖАЛУЙСТА помогите я в отчаянии - person Pedro Cabaço; 27.06.2015
comment
Непонятно, чего вы хотите добиться. Отчаявшись сделать что именно? Консоль отладки (куда идет вывод println) на самом деле довольно медленная. Он не может отображать сообщение каждую 1/1000 секунды. Если вам нужен таймер, который считает каждую 0,001 секунды, это тысячные доли секунды. Вы просто не можете этого сделать. - person Duncan C; 27.06.2015
comment
Вы можете создать задачу с высоким приоритетом в GCD, которая запускается в отдельном потоке и выполняет какое-то действие каждую тысячную долю секунды, но все, что взаимодействует с экраном, просто не поспевает за ней. Экран обновляется только раз в 1/60 секунды. Вы не можете обновить экран быстрее, чем это, ни при каких обстоятельствах. - person Duncan C; 27.06.2015
comment
У меня есть игра, в которой у меня есть таймер, который начинается с 5.000 и начинает обратный отсчет до бесконечности. Цель игры состоит в том, чтобы пользователь остановил таймер на отметке 0,000. Мне не обязательно, чтобы экран обновлялся КАЖДУЮ мили-мили-мили секунду, все, что я хочу, это когда я нажимаю кнопку, чтобы остановить таймер, чтобы он показал мне ТОЧНЫЙ результат! И прямо сейчас он этого не делает... Возможно ли это? Спасибо - person Pedro Cabaço; 28.06.2015
comment
Разместите код кнопки IBAction. Вот где вам нужно рассчитать фактическое оставшееся время. Вы должны смоделировать свой код после кода, который я разместил выше. - person Duncan C; 12.10.2016

Вы пытаетесь заставить его отображать каждую комбинацию от 0,0000001 до 0,99999999 за одну секунду? Экран буквально должен был бы обновляться сто миллионов раз, чтобы отобразить каждое число. Нет реального способа сделать это за одну секунду ни с одной существующей технологией или, вероятно, с любой технологией будущего. Сам экран не может обновляться быстрее, чем 60 раз в секунду, так что это самое быстрое, что вам подойдет.

Вы можете попробовать использовать NSTimer для этой скорости (1/60 = 0,0167). Сам NSTimer не обязательно будет очень точным. Чтобы обновлять экран в каждом кадре, вам придется использовать CADisplayLink (https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/).

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

person Brett    schedule 19.06.2015