テンポを刻む道具を自作して、楽器の練習に励むのも一興です。ということで、メトロノームのiPhoneアプリを作ってみます。
動作イメージ
XcodeからiOS6 iPhone Simulatorで動かすとこんな感じになります。
ポイント
振り子のおもりを指で上下に動かせるようにして、そこでテンポを変更できるようにしました。カチッカチッという振り子は、棒の一番下にCALayerのanchorを設定して、Rotationのアニメーションをテンポに合わせてNSTimerで動かしています。
サンプルコード
#import “ViewController.h”
#import <QuartzCore/QuartzCore.h>
@interface ViewController () {
UIView *pendulum;
BOOL isPlaying;
BOOL even;
float tempo;
}
@end
@implementation ViewController
– (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor brownColor];
tempo = 100;
isPlaying = NO;
[self createMetronome];
[self createPendulum];
[self createStartBtn];
}
– (void)createMetronome
{
float topW = 50;
float bottomW = topW * 5;
float topH = 50;
float bottomH = topH * 8;
// frame
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(160 – topW/2.0, topH)];
[path addLineToPoint:CGPointMake(160 + topW/2.0, topH)];
[path addLineToPoint:CGPointMake(160 + bottomW/2.0, bottomH)];
[path addLineToPoint:CGPointMake(160 – bottomW/2.0, bottomH)];
[path closePath];
CAShapeLayer *sl = [[CAShapeLayer alloc] init];
sl.fillColor = [UIColor blackColor].CGColor;
sl.strokeColor = [UIColor orangeColor].CGColor;
sl.lineWidth = 5;
sl.path = path.CGPath;
[self.view.layer addSublayer:sl];
}
// orange trapezoid
// use even odd
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(160 – topW/2.0 * 4.1, topH * 6.5)];
[path addLineToPoint:CGPointMake(160 + topW/2.0 * 4.1, topH * 6.5)];
[path addLineToPoint:CGPointMake(160 + bottomW/2.0, bottomH)];
[path addLineToPoint:CGPointMake(160 – bottomW/2.0, bottomH)];
[path closePath];
CAShapeLayer *sl = [[CAShapeLayer alloc] init];
sl.fillColor = [UIColor orangeColor].CGColor;
sl.path = path.CGPath;
[self.view.layer addSublayer:sl];
}
// memory
CALayer *scale = [CALayer layer];
scale.frame = CGRectMake(160 – topW * 0.3, topH + 40, topW * 0.6, 230);
scale.backgroundColor = [UIColor whiteColor].CGColor;
[self.view.layer addSublayer:scale];
}
– (void)createPendulum
{
pendulum = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 220)];
pendulum.backgroundColor = [UIColor clearColor];
pendulum.layer.anchorPoint = CGPointMake(0.5, 1.0);
pendulum.layer.position = CGPointMake(160, 320);
[self.view addSubview:pendulum];
CALayer *line = [CALayer layer];
line.frame = CGRectMake(15, 0, 10, 220);
line.backgroundColor = [UIColor lightGrayColor].CGColor;
[pendulum.layer addSublayer:line];
UILabel *weight = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
weight.backgroundColor = [UIColor orangeColor];
weight.layer.cornerRadius = 10;
weight.layer.borderColor = [UIColor yellowColor].CGColor;
weight.layer.borderWidth = 4;
weight.center = CGPointMake(20, 100);
weight.text = @”100″;
weight.textAlignment = NSTextAlignmentCenter;
weight.font = [UIFont boldSystemFontOfSize:15];
weight.textColor = [UIColor whiteColor];
[pendulum addSubview:weight];
weight.userInteractionEnabled = YES;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(slide:)];
[weight addGestureRecognizer:pan];
}
– (void)slide:(UIPanGestureRecognizer*)gr
{
CGPoint p = [gr locationInView:pendulum];
if (p.y < 200 && p.y > 10) {
gr.view.center = CGPointMake(gr.view.center.x, p.y);
[(UILabel*)gr.view setText:[NSString stringWithFormat:@”%d”, (int)p.y]];
tempo = p.y;
}
}
– (void)createStartBtn
{
UILabel *btn = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 80, 80)];
btn.center = CGPointMake(160, 480);
btn.text = @”PUSH”;
btn.textAlignment = NSTextAlignmentCenter;
btn.layer.cornerRadius = 40;
btn.layer.cornerRadius = 40;
[self.view addSubview:btn];
btn.userInteractionEnabled= YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(push)];
[btn addGestureRecognizer:tap];
}
– (void)push
{
isPlaying = !isPlaying;
if (isPlaying) {
[self swing:nil];
[NSTimer scheduledTimerWithTimeInterval:60.0/tempo target:self selector:@selector(swing:) userInfo:nil repeats:YES];
}
}
– (void)swing:(NSTimer*)sender
{
if (isPlaying) {
float duration = 60.0 / tempo;
[UIView animateWithDuration:duration animations:^{
if (even) {
pendulum.transform = CGAffineTransformMakeRotation(+ M_PI/4.0);
} else {
pendulum.transform = CGAffineTransformMakeRotation(- M_PI/4.0);
}
}];
even = !even;
} else {
[sender invalidate];
[UIView animateWithDuration:1.0 animations:^{
pendulum.transform = CGAffineTransformIdentity;
}];
}
}
– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end