時間に合わせて秒針を回転させる:iPhoneアプリでアナログ時計を作る



前回、iPhoneアプリのアナログ時計の秒針を作りました。とりあえず、回ることは回ったのですが、

1分間で1周回る動きをしているだけで、ちゃんと時間を反映していませんでした。


とりあえず、時間を取得するNSDateというクラスがあるということで、ググってみたらコチラのページ(「Sun Limited Mt.」)で紹介されていました。
ソースコードは、以下のようになりました。

- (void)viewWillAppear:(BOOL)animated//
{
    self.view.layer.contents =(id)[UIImage imageNamed:@"oboeyo_clock_back.png"].CGImage;
    
    CALayer *baseLayer =[self.view layer];
    
    CALayer *layer_clock_plate =[CALayer layer];
    layer_clock_plate.bounds =CGRectMake(0, 0, 303, 303);
    layer_clock_plate.position =CGPointMake(160, 180);
    layer_clock_plate.contents =(id)[UIImage imageNamed:@"oboeyo_clock_plate.png"].CGImage;
    [baseLayer addSublayer:layer_clock_plate];
    
    CALayer *sec_hand =[CALayer layer];
    sec_hand.bounds =CGRectMake(0, 0, 10, 150);
    sec_hand.anchorPoint =CGPointMake(0.5, 0.87);
    sec_hand.position =CGPointMake(160, 180);
    sec_hand.contents =(id)[UIImage imageNamed:@"sec_hand.png"].CGImage;
    [baseLayer addSublayer:sec_hand];
    
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
    
    // 回転の開始と終わりの角度を設定 単位はラジアン
    anim.fromValue = [NSNumber numberWithDouble:0];
    anim.toValue = [NSNumber numberWithDouble:360 * M_PI/180];
    
    // 回転軸の設定
    anim.valueFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ];
    
    //1回転あたりのアニメーション時間 単位は秒
    anim.duration = 60;
    
    // アニメーションのリピート回数
    anim.repeatCount = HUGE_VALF;
    
    // アニメーションをレイヤーにセット
    [sec_hand addAnimation:anim forKey:nil];
    
    NSDate *now =[NSDate date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    
    [formatter setDateFormat:@"kk"];
    int now_h = [[formatter stringFromDate:now] intValue];
    [formatter setDateFormat:@"mm"];
    int now_m = [[formatter stringFromDate:now] intValue];
    [formatter setDateFormat:@"ss"];
    int now_s = [[formatter stringFromDate:now] intValue];
    NSLog(@"%d:%d:%d", now_h, now_m, now_s);
//    [super viewWillAppear:animated];
}

まぁ、ここまではActionScriptとそれほど大差ないので、スンナリといきました。この後、ActionScriptなら、取得した秒数にラジアンをかけて角度を出し、秒針のムービークリップのrotateプロパティへ入力、enterframeで毎フレームに更新するだけです。
ところが、CALayerはどうやったら良いのかイマイチ分かりません。
とりあえず、「CALayer 秒針」でググったものの、少しソースコード古かったのか、そのままコピペしてもエラーが出てしまいました。


いろいろ試した結果、コチラのページ(「sonicbrain@wiki」)にあったCALayerの回転と、コチラのページ(「iPhoneアプリ開発グループ」)にあった1秒ずつタイマー処理を組み合わせて、以下のようなソースコードになりました。

CALayer *sec_hand;
NSTimer *timer;

- (void)viewDidLoad
{
    self.view.layer.contents =(id)[UIImage imageNamed:@"oboeyo_clock_back.png"].CGImage;
    
    CALayer *baseLayer =[self.view layer];
    
    CALayer *layer_clock_plate =[CALayer layer];
    layer_clock_plate.bounds =CGRectMake(0, 0, 303, 303);
    layer_clock_plate.position =CGPointMake(160, 180);
    layer_clock_plate.contents =(id)[UIImage imageNamed:@"oboeyo_clock_plate.png"].CGImage;
    [baseLayer addSublayer:layer_clock_plate];
    
    sec_hand =[CALayer layer];
    sec_hand.bounds =CGRectMake(0, 0, 10, 150);
    sec_hand.anchorPoint =CGPointMake(0.5, 0.87);
    sec_hand.position =CGPointMake(160, 180);
    sec_hand.contents =(id)[UIImage imageNamed:@"sec_hand.png"].CGImage;
    [baseLayer addSublayer:sec_hand];

    [self startClock];

	// Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (void) onTimer:(CFRunLoopTimerRef *)timer {
    NSDate *now =[NSDate date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    
    /*
    [formatter setDateFormat:@"kk"];
    int now_h = [[formatter stringFromDate:now] intValue];
    [formatter setDateFormat:@"mm"];
    int now_m = [[formatter stringFromDate:now] intValue];
     */
    [formatter setDateFormat:@"ss"];
    int now_s = [[formatter stringFromDate:now] intValue];
    //NSLog(@"%d:%d:%d", now_h, now_m, now_s);

    CATransform3D transform = CATransform3DIdentity;
    transform = CATransform3DRotate(transform, now_s*6*M_PI/180.0, 0, 0, 1);
    sec_hand.transform = transform;
}

- (void) startClock {
    timer = [NSTimer 
             scheduledTimerWithTimeInterval:1.0
             target: self 
             selector:@selector(onTimer:) 
             userInfo:nil 
             repeats:YES];
}

ちょっと最初に、現在の秒まで針が回転してしまうという意図しない動きが入ってしまったものの、ちゃんと現在の時刻を反映した動きになりました!
嬉しいけど、ちょっと時間かかり過ぎですね。ActionScriptなら1時間もあれば出来そうな動きを、1週間近くかかってしまいました。orz

次は、時間と分を反映させて、まずは時計の形にしたいと思います。