今日は三角パズルを作成してみました。
あか、あお、きいろ、みどり、4色のさんかく形を
まわして(タップで回転)、ずらして(ゆびで動かせます)
この二つの操作でさんかくを動かしていくと、
風車とか、船とか、いろいろな形を作ることができます。

プログラムのポイント
「問題」
UIViewに三角を表示しているので、
実際には二枚の四角が重なった状態となります。
ここで下になったViewにtouch eventを渡らない。
「解決法」
タッチしたところの色をCGBitmapContextCreateを使って取得
透明だった場合、上にあるViewのuserInteractionEnabledを一時的にNO
に設定して、下のViewのtouch eventを呼び出す。
詳細はメソッド「touchNotClearColorView」を参照



サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

@interface ViewController ()

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    

    self.view.backgroundColor = [UIColor whiteColor];

    

    [self createPiece];

    

    UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(reset)];

    [self.view addGestureRecognizer:swipe];

}

– (void)reset

{

    for (UIView *v in self.view.subviews) {

        [UIView animateWithDuration:2.0 animations:^{

            v.transform = CGAffineTransformIdentity;

        }];

    }

}

– (void) createPiece

{

    float w = 80;

    NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor blueColor], [UIColor yellowColor], [UIColor greenColor], nil];

    for (int i=0; i<8; i++) {

        float x = (i % 4) * w;

        float y = (i / 4) * w + 280;

        

        // triangle A

        UIView *a = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, w)];

        a.backgroundColor = [UIColor clearColor];

        UIBezierPath *path1 = [[UIBezierPath alloc] init];

        [path1 moveToPoint:CGPointMake(0, 0)];

        [path1 addLineToPoint:CGPointMake(w, w)];

        [path1 addLineToPoint:CGPointMake(w, 0)];

        CAShapeLayer *l1 = [[CAShapeLayer alloc] initWithLayer:a.layer];

        l1.fillColor = [[colors objectAtIndex:i % 4] CGColor];

        l1.path = path1.CGPath;

        [a.layer addSublayer:l1];

        [self.view addSubview:a];

        

        // triangle B

        UIView *b = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, w)];

        b.backgroundColor = [UIColor clearColor];

        UIBezierPath *path2 = [[UIBezierPath alloc] init];

        [path2 moveToPoint:CGPointMake(0, 0)];

        [path2 addLineToPoint:CGPointMake(0, w)];

        [path2 addLineToPoint:CGPointMake(w, w)];

        CAShapeLayer *l2 = [[CAShapeLayer alloc] initWithLayer:b.layer];

        l2.fillColor = [[colors objectAtIndex:(i + 1) % 4] CGColor];

        l2.path = path2.CGPath;

        [b.layer addSublayer:l2];

        [self.view addSubview:b];

        a.tag = i;

        b.tag = i + 100;

    }

    

    for (UIView *v in self.view.subviews) {

        

        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];

        [v addGestureRecognizer:tap];

        

        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];

        [v addGestureRecognizer:pan];

    }

}

– (void)tap:(UIGestureRecognizer*)gr

{

    CGPoint p = [gr locationInView:self.view];

    UIView *target = [self touchNotClearColorView:p];

    if (target) {

        [UIView animateWithDuration:0.5 animations:^{

            target.transform = CGAffineTransformRotate(target.transform, M_PI * 0.5);

        }];

    }

}

– (void)pan:(UIPanGestureRecognizer*)gr

{

    static UIView *target;

    CGPoint p = [gr locationInView:self.view];

    if (gr.state == UIGestureRecognizerStateBegan) {

        target = [self touchNotClearColorView:p];

    }

    else if (gr.state == UIGestureRecognizerStateEnded

             || gr.state == UIGestureRecognizerStateCancelled)

    {

        target = nil;

    }

    

    if (target) {

        float currentAngle = [[target.layer valueForKeyPath:@”transform.rotation.z”] floatValue];

        target.transform = CGAffineTransformRotate(target.transform, -currentAngle);

        float oldx = target.transform.tx + target.center.x;

        float oldy = target.transform.ty + target.center.y;

        CGPoint t = CGPointMake(p.x – oldx, p.y – oldy);

        target.transform = CGAffineTransformTranslate(target.transform, t.x, t.y);

        target.transform = CGAffineTransformRotate(target.transform, currentAngle);

    }

    

}

– (UIView*)touchNotClearColorView:(CGPoint)p0

{

    UIView *target = nil;

    BOOL end = NO;

    while (!end) {

        target = [self.view hitTest:p0 withEvent:nil];

        if (!target || target == self.view) {

            target = nil;

            end = YES;

        }

        CGPoint p = [target convertPoint:p0 fromView:target.superview];

        if ([self alphaOfPoint:p view:target] == 0) {

            target.userInteractionEnabled = NO;

        } else {

            end = YES;

        }

    }

    

    for (UIView *v in self.view.subviews) {

        v.userInteractionEnabled = YES;

    }

    return target;

}

– (float)alphaOfPoint:(CGPoint)point view:(UIView*)view

{

    unsigned char pixel[4] = {0};

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast);

    

    CGContextTranslateCTM(context, -point.x, -point.y);

    

    [view.layer renderInContext:context];

    CGContextRelease(context);

    CGColorSpaceRelease(colorSpace);

    float alpha = pixel[3]/255.0;

    

    return alpha;

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end