2013-07-30 2 views
0

그래서 타이머가있는보기 컨트롤러가 있습니다. 하나의 단추가 있습니다. 처음으로보기가로드되면 버튼에 "시작"이라고 표시되어야합니다. 이 탭되면선택기를 호출하지 않고 스레드를 분리하거나 최소한 해결 방법을 만들 수 있습니까?

, "시작"-> "일시 중지"를.

다른 탭 "일시 중지"-> "재개".

"다시 시작"-> "일시 중지"를 누릅니다. 내가 타이머가 정확 원하기 때문에

, 나는 (내가 몇 가지 설명을 부탁드립니다 내가 올바른 방법을 선택했다 생각) 메인 스레드에서 분리 된. 하지만 스레드에서 분리하는 것처럼 실제로 시작 단추 대신 "일시 중지"시작 단추를 만드는 메서드를 호출하는 것 같습니다. 이 문제를 어떻게 해결할 수 있습니까? testTask.showButtonValue의 방법으로

, 기본값 (과 부하) 즉시 호출 Afer 즉, 새 스레드를 생성하고 선택 언급 수행 detachNewThreadSelector를 호출 1.

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    [NSThread detachNewThreadSelector:@selector(startTimer:) toTarget:self withObject:nil]; 

    if (testTask.showButtonValue == 1) { 
     [startButton setTitle:@"Start" forState:UIControlStateNormal]; 
    } else if (testTask.showButtonValue == 2) { 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
    } else if (testTask.showButtonValue == 3){ 
     [startButton setTitle:@"Resume" forState:UIControlStateNormal]; 
    } 
} 
-(IBAction)startTimer:(id)sender{ 
    if (testTask.showButtonValue == 1) { 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 2; 
    } else if (testTask.showButtonValue == 2) { 
     [startButton setTitle:@"Resume" forState:UIControlStateNormal]; 
     [timer invalidate]; 
     timer = nil; 
     testTask.showButtonValue = 3; 
    } else if (testTask.showButtonValue == 3){ 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 2; 
    } 
} 
-(void)timerAction:(NSTimer *)t 
{ 
    if(testTask.timeInterval == 0) 
    { 
     if (self.timer) 
     { 
      [self timerExpired]; 
      [self.timer invalidate]; 
      self.timer = nil; 
     } 
    } 
    else 
    { 
     testTask.timeInterval--; 
    } 
    NSUInteger seconds = (NSUInteger)round(testTask.timeInterval); 
    NSString *string = [NSString stringWithFormat:@"%02u:%02u:%02u", 
         seconds/3600, (seconds/60) % 60, seconds % 60]; 
    timerLabel.text = string; 
    NSLog(@"%f", testTask.timeInterval); 
} 

답변

1

나는 다른 방법으로 이것에 대해 갈 것을 제안 :

@interface SNDViewController() 
@property (weak, nonatomic) IBOutlet UIButton *startButton; 
@property (weak, nonatomic) IBOutlet UILabel *timerLabel; 
@property (nonatomic, strong) NSTimer *timer; 
@property (nonatomic, assign) NSTimeInterval accumulativeTime; 
@property (nonatomic, assign) NSTimeInterval currentReferenceTime; 
@end 

@implementation SNDViewController 

편집 : 뷰로드, self.accumulativeTime 및 업데이트를 초기화 할 때 문제가 같이 당신의 방법을 변경 고정

타이머 라벨. accumulativeTime 변수의 초기화는 실제로 적절한 init * 메소드에서 수행되어야합니다. initWithNibName : 번들 :. 이 시점에서 핵심 데이터에서 타이머 값을 읽습니다.

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    self.accumulativeTime = 300; 
    [self updateTimerLabelWithTotalTime:self.accumulativeTime]; 
} 

- (IBAction)changeTimerState:(UIButton *)sender 
{ 
    if (self.timer == nil) { 
     self.currentReferenceTime = [NSDate timeIntervalSinceReferenceDate]; 
     [self.timer invalidate]; 
     self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES]; 
    } else { 
     //Pause the timer and track accumulative time. 
     NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; 
     [self.timer invalidate]; 
     self.timer = nil; 
     NSTimeInterval timeSinceCurrentReference = now - self.currentReferenceTime; 

편집 : accumalitiveTime에서 빼기 timeSinceCurrentReference,이 카운트 다운 타이머 때문이다. 또한 필요한 경우 핵심 데이터에 저장하기위한 주석을 추가했습니다.

 self.accumulativeTime -= timeSinceCurrentReference; 
     [self updateTimerLabelWithTotalTime:self.accumulativeTime]; 

     //Optionally save self.accumulativeTime to core data for future use. 
    } 

    NSString *buttonTitle = (self.timer != nil) ? @"Pause" : @"Resume"; 
    [self.startButton setTitle:buttonTitle forState:UIControlStateNormal]; 
} 

당신은 같은 showButtonValue 변수로, 불필요한 상태를 저장 할 필요가 없습니다. 대신에 self.timer == nil인지 여부에 따라 일시 중지 또는 재개 여부를 결정할 수 있습니다. 실행중인 타이머가없는 경우 현재 기준 시간을 잡고 새 타이머를 시작하십시오. 타이머는 0.01 초마다 발사되도록 예정되어 있기 때문에 0.1 초로 정확할 것입니다. 버튼의 제목을 "시작"으로 변경할 필요가 없습니다. "일시 중지"또는 "재개"중 하나입니다. 타이머가 사용자에 의해 일시 중지되면 우리는 self.timer을 처리하고 타이머 레이블을 가장 정확한 시간으로 업데이트합니다.

- (void)updateTimer:(id)sender 
{ 
    NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; 
    NSTimeInterval timeSinceCurrentReference = now - self.currentReferenceTime; 

EDIT : self.accumulativeTime에서 빼기 timeSinceCurrentReference은 (시간이 지남에 따라, 즉을 totalTime 감소)에 도착을 totalTime.

NSTimeInterval totalTime = self.accumulativeTime - timeSinceCurrentReference; 

    if (totalTime <= 0) { 
     totalTime = 0; 
     [self.timer invalidate]; 
     self.timer = nil; 

     //What to do when we reach zero? For example, we could reset timer to 5 minutes: 
     self.accumulativeTime = 300; 
     totalTime = self.accumulativeTime; 
     [self.startButton setTitle:@"Start" forState:UIControlStateNormal]; 
    } 

    [self updateTimerLabelWithTotalTime:totalTime]; 
} 

타이머가 발사 될 때마다, 우리는 nowself.currentReferenceTimeself.accumulativeTime에 추가의 차이를 찾아 총 시간을 얻을.

- (void)updateTimerLabelWithTotalTime:(NSTimeInterval)totalTime 
{ 
    NSInteger hours = totalTime/3600; 
    NSInteger minutes = totalTime/60; 
    NSInteger seconds = totalTime; 
    NSInteger fractions = totalTime * 10; 

    self.timerLabel.text = [NSString stringWithFormat:@"%02u:%02u:%02u.%01u", hours, minutes % 60, seconds % 60, fractions % 10]; 
} 

@end 

방법 - (IBAction)changeTimerState:(UIButton *)sender는 "터치 업 내부"이벤트에있는 UIButton (UIControlEventTouchUpInside)에 의해 호출됩니다.

viewDidLoad에서 아무 것도 할 필요가 없습니다.

또한 중요하게도이 작업은 모두 주 스레드에서 수행됩니다. 타이머 라벨을 업데이트하는 주 스레드가 방해가되면 사용자가 볼 수있는 텍스트가 부정확 할 수 있지만 업데이트되면 다시 정확한 것으로 확인 될 수 있습니다. 그것은 당신의 앱이 무엇을하고 있는지에 달려 있습니다. 그러나 모든 UI 업데이트가 주 스레드에서 수행되어야하므로 실제로이를 피할 수는 없습니다.

희망이 도움이됩니다. 명확하지 않은 것이 있으면 알려주세요.

(여기에 해당 Xcode 프로젝트는 : https://github.com/sdods3782/TVTTableViewTest)

+0

문제는 우리가 core Data에 저장되어 timeInterval을 저장해야하기 때문에 지금하고있는 일 대신 카운트 다운 타이머를 설정하기 위해 testTask.timeInterval 속성을 사용해야한다는 것입니다. 어떻게 추가 할 수 있습니까? – EvilAegis

+0

또한 "버튼의 제목을"시작 "으로 변경할 필요가 없으며"일시 중지 "또는"다시 시작 "중 하나입니다. 뷰가 처음으로로드되면 시작이어야합니다 : O. – EvilAegis

+0

마지막으로, "타이머가 0.01 초마다 발사되도록 예정되어 있으며, 0.1 초로 정확할 것으로 기대됩니다." 이로 인해 많은 메모리가 사용됩니까? – EvilAegis

1

입니다.

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    [NSThread detachNewThreadSelector:@selector(startTimer:) toTarget:self withObject:nil]; 
} 
-(IBAction)startTimer:(id)sender 
{ 
    if (testTask.showButtonValue == 1) { 
     [startButton setTitle:@"Start" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 3; 
    } else if (testTask.showButtonValue == 2) { 
     [startButton setTitle:@"Resume" forState:UIControlStateNormal]; 
     [timer invalidate]; 
     timer = nil; 
     testTask.showButtonValue = 3; 
    } else if (testTask.showButtonValue == 3){ 
     [startButton setTitle:@"Pause" forState:UIControlStateNormal]; 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; 
     testTask.showButtonValue = 2; 
    } 
} 
+0

이 코드에서 mainthread가 아닌 다른 스레드에서 사용되는 UIKit 아닌가요? – hfossli

+0

이 방법이 효과가 있었지만 정답으로 만들기 전에 viewDidLoad : O에서 코드의 일부를 삭제했습니다. 사용자가 뒤로 누르고보기로 다시 이동하면 단추에 대한 올바른 레이블이 표시되지 않습니다. 나는 그것을 고치는 방법을 모르겠다. .. – EvilAegis

+0

흠. 내가 그것을 시작한 다음 다시 누르고, 다시보기로 간다면 실제로 스레드 2 번 (또는 그 이상)으로 분리된다. 타이머가 2 배 빠름 : O! 음 ... – EvilAegis