3分でできるプログラミング
「指でクルクルする時計」です。
出来上がったら
自分の作ったアプリで、こどもと一緒に「とけい」の勉強をしてみませんか。
道具
Xcode (これ書いている時点で使っているのは、ver4.3です)
材料
QuartsCore.framework ×1
時計の針 ×2
文字盤 ×1
とけいの針(短針と長針の二つ)を表示するためのClockHandという Object-Cクラスを作りましょう。
ヘッダファイルには、initWithFrame に針の長さと色を指定できるようにしたメソッドを ClockHand.h に追加します。
@interface ClockHand : UIView
-(id)initWithFrame:(CGRect)frame handLength:(float)length handColor:(UIColor*)color;
@end
実装していきます。ClockHand.m を開きましょう。
必要になる変数を用意します。@implementation ClockHand の上に次のコードを書きましょう。selected は気持ちよく針をクルクルさせるために、タッチイベントの発生と終了で設定する値です。
@interface ClockHand() {
BOOL selected; // 針がタップされていれば YES
float handLength; // 針の長さ
CGPoint origin, handPoint; // 時計の中心、針の先端
UIColor *handColor; // 針の色
}
@end
ヘッダに追加した init メソッドで、針の中心、長さ、色などを設定していきます。 @implementation ClockHand の下に、コードを書いていきましょう。時計の中心は、View の中心に設定しました。針の先端の初期位置は、y成分が0の3時方向としています。
-(id)initWithFrame:(CGRect)frame handLength:(float)length handColor:(UIColor*)color
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
handLength = length;
handColor = color;
origin = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0);
handPoint.x = origin.x + handLength;
handPoint.y = origin.y;
selected = NO;
}
return self;
}
View の描画部分を書いていきます。中心から、そのときの針の先端まで線を引く処理を次のように。
– (void)drawRect:(CGRect)rect
{
[handColor set];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 10.0);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextMoveToPoint(ctx, origin.x, origin.y);
CGContextAddLineToPoint(ctx, handPoint.x, handPoint.y);
CGContextStrokePath(ctx);
}
タッチイベントの処理を書いていきます。長針、短針が重なるように View を配置するので、下になったView を選択できるように、hitTest の実装を行います。針の先、30×30 の領域をタッチした場合、そのViewのtouch イベントを拾うようにします。
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 他の針がタッチで動くように、針の先端がタッチされたのでなければnil を返す。
// ここを実装しないと、touch イベントが後ろに重ねた View に伝わらない。
if (selected) {
return self;
}
if (abs(point.x – handPoint.x) < 30 && abs(point.y – handPoint.y) < 30) {
return self;
}
return nil;
}
指を離すまでは、タッチしたときに捕まえた針をまわすようにしたいので、touch の Begin, End, Cancel に selected に関する実装を行います。
– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
selected = YES;
}
– (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
selected = NO;
}
– (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
selected = NO;
}
針を実際に動かす処理を、touch moved に実装していきましょう。
– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint tp = [t locationInView:self];
// とけいの中心からの距離
float rx = tp.x – origin.x;
float ry = tp.y – origin.y;
// 針の長さを調整
float r = hypot(rx, ry); // r = root(x^2 + y^2)
float rate = handLength / r;
handPoint.x = rx * rate + origin.x;
handPoint.y = ry * rate + origin.y;
[self setNeedsDisplay];
}
以上で、針の実装は完了です。
ViewController に組み込んでいきましょう。ViewController には次の二つのインポートが必要になります。(QuartzCore は追加ライブラリなので、Target – BuildPhases – Link Binary With Libraries に追加しておく必要があります。)
#import <QuartzCore/QuartzCore.h>
#import “ClockHand.h”
インポートしたら、viewDidLoad に実装を追加していきましょう。時計版の作成と短針、長針の作成を書いていきます。
– (void)viewDidLoad
{
[super viewDidLoad];
// とけい盤
float r = 125.0;
UIView *board = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2 * r, 2 * r)];
board.center = self.view.center;
board.backgroundColor = [UIColor whiteColor];
board.layer.cornerRadius = 125;
board.layer.borderWidth = 5;
board.layer.borderColor = [[UIColor darkGrayColo
r] CGColor];
for (int i=0; i<12; i++) {
UIView *hour = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
hour.backgroundColor = [UIColor darkGrayColor];
hour.layer.cornerRadius = 4;
float rx = r + 0.9 * r * cos(2 * M_PI * i * (1.0/12.0));
float ry = r + 0.9 * r * sin(2 * M_PI * i * (1.0/12.0));
hour.center = CGPointMake(rx, ry);
[board addSubview:hour];
}
[self.view addSubview:board];
// とけいの短針
ClockHand *shortHand = [[ClockHand alloc] initWithFrame:board.frame handLength:60 handColor:[UIColor redColor]];
[self.view addSubview:shortHand];
// とけいの長針
ClockHand *longHand = [[ClockHand alloc] initWithFrame:board.frame handLength:110 handColor:[UIColor greenColor]];
[self.view addSubview:longHand];
}
以上で完了です。
ぜひ、お試しください。
ソース
ClockHand.h
#import <UIKit/UIKit.h>
@interface ClockHand : UIView
-(id)initWithFrame:(CGRect)frame handLength:(float)length handColor:(UIColor*)color;
@end
CloclHand.m
#import “ClockHand.h”
@interface ClockHand() {
BOOL selected; // 針がタップされていれば YES
float handLength; // 針の長さ
CGPoint origin, handPoint; // 時計の中心、針の先端
UIColor *handColor; // 針の色
}
@end
@implementation ClockHand
-(id)initWithFrame:(CGRect)frame handLength:(float)length handColor:(UIColor*)color
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
handLength = length;
handColor = color;
origin = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0);
handPoint.x = origin.x + handLength;
handPoint.y = origin.y;
selected = NO;
}
return self;
}
– (void)drawRect:(CGRect)rect
{
[handColor set];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 10.0);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextMoveToPoint(ctx, origin.x, origin.y);
CGContextAddLineToPoint(ctx, handPoint.x, handPoint.y);
CGContextStrokePath(ctx);
}
#pragma mark – touch controll
– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
selected = YES;
}
– (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
selected = NO;
}
– (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
selected = NO;
}
– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint tp = [t locationInView:self];
// とけいの中心からの距離
float rx = tp.x – origin.x;
float ry = tp.y – origin.y;
// 針の長さを調整
float r = hypot(rx, ry); // r = root(x^2 + y^2)
float rate = handLength / r;
handPoint.x = rx * rate + origin.x;
handPoint.y = ry * rate + origin.y;
[self setNeedsDisplay];
}
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 他の針がタッチで動くように、針の先端がタッチされたのでなければnil を返す。
// ここを実装しないと、touch イベントが後ろに重ねた View に伝わらない。
if (selected) {
return self;
}
if (abs(point.x – handPoint.x) < 30 && abs(point.y – handPoint.y) < 30) {
return self;
}
return nil;
}
@end
ViewController.m
#import <QuartzCore/QuartzCore.h>
#import “ViewController.h”
#import “ClockHand.h”
@interface ViewController ()
@end
@implementation ViewController
– (void)viewDidLoad
{
[super viewDidLoad];
// とけい盤
float r = 125.0;
UIView *board = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2 * r, 2 * r)];
board.center = self.view.center;
board.backgroundColor = [UIColor whiteColor];
board.layer.cornerRadius = 125;
board.layer.borderWidth = 5;
board.layer.borderColor = [[UIColor darkGrayColor] CGColor];
for (int i=0; i<12; i++) {
UIView *hour = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
hour.backgroundColor = [UIColor darkGrayColor];
hour.layer.cornerRadius = 4;
float rx = r + 0.9 * r * cos(2 * M_PI * i * (1.0/12.0));
float ry = r + 0.9 * r * sin(2 * M_PI * i * (1.0/12.0));
hour.center = CGPointMake(rx, ry);
[board addSubview:hour];
}
[self.view addSubview:board];
// とけいの短針
ClockHand *shortHand = [[ClockHand alloc] initWithFrame:board.frame handLength:60 handColor:[UIColor redColor]];
[self.view addSubview:shortHand];
// とけいの長針
ClockHand *longHand = [[ClockHand alloc] initWithFrame:board.frame handLength:110 handColor:[UIColor greenColor]];
[self.view addSubview:longHand];
}
– (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end