#objective-c #ios #cocoa-touch #time #nstimer
#objective-c #iOS #cocoa-touch #время #nstimer
Вопрос:
Это мой код для секундомера iPhone. Он работает как ожидалось, останавливается и возобновляется при нажатии кнопок.
Однако, когда я нажимаю «Стоп», таймер не прекращает работать в фоновом режиме, а когда я нажимаю «Пуск», чтобы возобновить его, он обновит время и перейдет к текущему состоянию вместо возобновления с остановленного времени.
Как я могу остановить NSTimer
? Что является причиной этого?
@implementation FirstViewController;
@synthesize stopWatchLabel;
NSDate *startDate;
NSTimer *stopWatchTimer;
int touchCount;
-(void)showActivity {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"mm:ss.SS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
stopWatchLabel.text = timeString;
[dateFormatter release];
}
- (IBAction)onStartPressed:(id)sender {
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1/10 target:self selector:@selector(showActivity) userInfo:nil repeats:YES];
touchCount = 1;
if (touchCount > 1)
{
[stopWatchTimer fire];
}
else
{
startDate = [[NSDate date]retain];
[stopWatchTimer fire];
}
}
- (IBAction)onStopPressed:(id)sender {
[stopWatchTimer invalidate];
stopWatchTimer = nil;
[self showActivity];
}
- (IBAction)reset:(id)sender; {
touchCount = 0;
stopWatchLabel.text = @"00:00.00";
}
Комментарии:
1. Какое действие вызывает ваша кнопка паузы?
2. извините, я имел в виду действие onStopPressed, pause и stop — это одно и то же
Ответ №1:
При расчете текущего отображения всегда используется исходное время запуска таймера, поэтому отображение после приостановки включает интервал, в течение которого таймер был приостановлен.
Проще всего было бы сохранить другое время, NSTimeInterval
скажем secondsAlreadyRun
, когда таймер приостановлен, и добавить это к интервалу времени, который вы вычисляете при возобновлении. Вы захотите обновлять значение таймера startDate
каждый раз, когда таймер начинает отсчет. В reset:
случае вы также очистили бы этот secondsAlreadyRun
интервал.
-(void)showActivity:(NSTimer *)tim {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
// Add the saved interval
timeInterval = secondsAlreadyRun;
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"mm:ss.SS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
stopWatchLabel.text = timeString;
[dateFormatter release];
}
- (IBAction)onStartPressed:(id)sender {
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1/10
target:self
selector:@selector(showActivity:)
userInfo:nil
repeats:YES];
// Save the new start date every time
startDate = [[NSDate alloc] init]; // equivalent to [[NSDate date] retain];
[stopWatchTimer fire];
}
- (IBAction)onStopPressed:(id)sender {
// _Increment_ secondsAlreadyRun to allow for multiple pauses and restarts
secondsAlreadyRun = [[NSDate date] timeIntervalSinceDate:startDate];
[stopWatchTimer invalidate];
stopWatchTimer = nil;
[startDate release];
[self showActivity];
}
- (IBAction)reset:(id)sender; {
secondsAlreadyRun = 0;
stopWatchLabel.text = @"00:00.00";
}
Не забудьте указать это startDate
где-нибудь в подходящем месте! Также имейте в виду, что задокументированный NSTimer
интерфейс предназначен для того, чтобы метод, который вы ему предоставляете, принимал один аргумент, которым будет сам таймер. Кажется, что это работает без этого, но зачем искушать судьбу?
Наконец, поскольку вы так часто используете это NSDateFormatter
, вы можете подумать о том, чтобы сделать это ivar или поместить его в static
хранилище в showActivity:
, вот так:
static NSDateFormatter * dateFormatter = nil;
if( !dateFormatter ){
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"mm:ss.SS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
}
Комментарии:
1. Большое вам спасибо, это гораздо более эффективный способ сделать это. у меня есть одна быстрая ошибка, с которой, надеюсь, вы сможете мне помочь, я, кажется, не могу
secondsAlreadyRun = [NSDate timeIntervalSinceDate:startDate];
приступить к работе. какие переменные мне нужны или что еще нужно сделать? Спасибо!2.
secondsAlreadyRun
должен бытьNSTimeInterval
ivar. Что именно означает «не удается заставить его работать»?3. ну, да, у меня есть
secondsAlreadyRun
какNSTimeInterval
ivar, но он возвращает сообщение об ошибке «недопустимые операнды для двоичного выражения (‘NSTimeInterval’ (он же ‘double’) и ‘id’) и выдает предупреждение «метод класса ‘ timeIntervalSinceDate’ не найден». Должно быть, я делаю что-то не так, спасибо за вашу помощь4. Упс! Я скомпилировал это с помощью brain и перепутал эту строку. Нет метода класса с именем
timeIntervalSinceDate:
. Компилятор предполагает, что метод возвращаетid
, когда он не может найти объявление. Вот почему вы получили это предупреждение. Я исправил эту строку.5.
abs
возвращает целое число, усекаяNSTimeInterval
. Используйтеfabs
или измените правую часть на[[NSDate date] timeIntervalSinceDate:startDate];
Ответ №2:
Таким образом, когда пользователь нажимает «Стоп», а затем «Начать снова», вы не сбрасываете время начала. Но когда вы обновляете метку, вы основываете ее на общем времени, прошедшем с исходного времени запуска до текущего времени.
Итак, если вы запустите таймер на 10 секунд, остановитесь, подождите 10 секунд, а затем запустите снова, таймер покажет 00: 20.00 и с этого момента начнется отсчет снова.
Что вы хотите сделать, это сбрасывать время начала каждый раз, когда пользователь запускает часы, но затем также добавлять истекшее время всех предыдущих запусков. Или что-то подобное.
Кстати, вы пропускаете время запуска каждый раз, когда сбрасываете его сейчас. Незначительная ошибка.
РЕДАКТИРОВАТЬ: похоже, @Josh Caswell думал о том же, но он печатает НАМНОГО быстрее. 🙂
Комментарии:
1. Я думаю, что я только начал раньше вас … 🙂
Ответ №3:
Вы используете ARC или нет?
Если вы используете ARC, похоже, что вы не используете ссылку _strong. Если вы не используете ARC, не похоже, что вы сохраняете ссылку на таймер.
Я публикую это с мобильного устройства, поэтому, возможно, чего-то не хватает.
РЕДАКТИРОВАТЬ: только что заметил, что вы использовали release в другом месте, поэтому я предполагаю, что ARC отсутствует. Вам необходимо сохранить таймер после его настройки, чтобы иметь возможность получить к нему доступ позже и аннулировать.
Ответ №4:
Вы можете использовать NSTimeInterval вместо timer. У меня есть функциональный код для приостановки и остановки таймера.
@interface PerformBenchmarksViewController () {
int currMinute;
int currSecond;
int currHour;
int mins;
NSDate *startDate;
NSTimeInterval secondsAlreadyRun;
}
@end
- (void)viewDidLoad
{
[super viewDidLoad];
running = false;
}
- (IBAction)StartTimer:(id)sender {
if(running == false) {
//start timer
running = true;
startDate = [[NSDate alloc] init];
startTime = [NSDate timeIntervalSinceReferenceDate];
[sender setTitle:@"Pause" forState:UIControlStateNormal];
[self updateTime];
}
else {
//pause timer
secondsAlreadyRun = [[NSDate date] timeIntervalSinceDate:startDate];
startDate = [[NSDate alloc] init];
[sender setTitle:@"Start" forState:UIControlStateNormal];
running = false;
}
}
- (void)updateTime {
if(running == false) return;
//calculate elapsed time
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsed = secondsAlreadyRun currentTime - startTime;
// extract out the minutes, seconds, and hours of seconds from elapsed time:
int hours = (int)(mins / 60.0);
elapsed -= hours * 60;
mins = (int)(elapsed / 60.0);
elapsed -= mins * 60;
int secs = (int) (elapsed);
//update our lable using the format of 00:00:00
timerLabel.text = [NSString stringWithFormat:@"u:u:u", hours, mins, secs];
//call uptadeTime again after 1 second
[self performSelector:@selector(updateTime) withObject:self afterDelay:1];
}
Надеюсь, это поможет. Спасибо
Ответ №5:
Класс таймера, который я создал в Swift для программы таймера, в которой счетчик обновляется каждую секунду с установленного времени. Ответил, чтобы проиллюстрировать быстрое решение и функцию NSTimer.
Таймер можно остановить и перезапустить; он возобновится с того места, где он остановился. Делегат может перехватывать события для запуска, остановки, сброса, завершения и второго событий. Просто проверьте код.
import Foundation
protocol TimerDelegate {
func didStart()
func didStop()
func didReset()
func didEnd()
func updateSecond(timeToGo: NSTimeInterval)
}
// Inherit from NSObject to workaround Selector bug
class Timer : NSObject {
var delegate: TimerDelegate?
var defaultDuration: NSTimeInterval?
var isRunning: Bool {
get {
return self.timer != nil amp;amp; timer!.valid
}
}
private var secondsToGo: NSTimeInterval = 0
private var timer: NSTimer?
init(defaultDuration: NSTimeInterval, delegate: TimerDelegate? = nil) {
self.defaultDuration = defaultDuration
self.delegate = delegate
super.init()
}
func start() {
self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateTimer", userInfo: nil, repeats: true)
self.timer!.tolerance = 0.05
if delegate != nil { delegate!.didStart() }
}
func stop () {
self.timer?.invalidate()
self.timer = nil
if delegate != nil { delegate!.didStop() }
}
func reset() {
self.secondsToGo = self.defaultDuration!
if delegate != nil { delegate!.didReset() }
}
func updateTimer() {
--self.secondsToGo
if delegate != nil { delegate!.updateSecond(self.secondsToGo) }
if self.secondsToGo == 0 {
self.stop()
if delegate != nil { delegate!.didEnd() }
}
}
}