タイル状のUIViewを動かしてみる


[Letterpress](https://itunes.apple.com/jp/app/letterpress-word-game/id526619424?mt=8)という英単語陣取りゲームがおもしろいです。
こんな風な「タイルのドラッグ移動」をやったこと無かったので試し書きしてみました。

```objective-c:ZEViewController

#import "ZEViewController.h"
#import <QuartzCore/QuartzCore.h> // layer.shadowXXXを使うために。

@interface ZEViewController ()

@end

@implementation ZEViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 25個のUILabelを作る
    NSMutableArray *tiles = [NSMutableArray array];
    for( int i=0; i<25; i++ ){
        float tileSize = self.view.bounds.size.width/5;
        int indexX = i%5;      // 横方向 0-4
        int indexY = (int)i/5; // 縦方向 0-4
        float centerX = tileSize*indexX+(tileSize/2);
        float centerY = tileSize*indexY+(tileSize/2);
        
        UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0,0, tileSize,tileSize)];
        label.center=CGPointMake(centerX,centerY);
        label.textAlignment=NSTextAlignmentCenter;
        label.text=[NSString stringWithFormat:@"%d",i];
        if( i%2 == 0 ){
            label.backgroundColor = [UIColor whiteColor];
        }else{
            label.backgroundColor = [UIColor grayColor];
        }
        [tiles addObject:label];
        [self.view addSubview:label];
        
        // ホールドした際の挙動
        label.userInteractionEnabled = YES;
        UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
        [label addGestureRecognizer:panGesture];
        
    }
    //
}
- (void) handlePanGesture:(UIPanGestureRecognizer*) sender {
    UIPanGestureRecognizer* pan = (UIPanGestureRecognizer*) sender;
    UILabel *label = (UILabel *)pan.view;
    CGPoint location = [pan translationInView:label];
    NSLog(@"pan x=%f, y=%f", location.x, location.y);
    if ([sender state] == UIGestureRecognizerStateEnded){
        // 指を放したとき
        CGContextRef context = UIGraphicsGetCurrentContext();
        [UIView beginAnimations:nil context:context];
        // 元の場所に移動
        int i = [label.text intValue];
        float tileSize = self.view.bounds.size.width/5;
        int indexX = i%5;      // 横方向 0-4
        int indexY = (int)i/5; // 縦方向 0-4
        float centerX = tileSize*indexX+(tileSize/2);
        float centerY = tileSize*indexY+(tileSize/2);
        label.center = CGPointMake(centerX,centerY);
        // 影を隠す
        label.clipsToBounds=YES;
        // 元のサイズに戻す
        label.transform = CGAffineTransformIdentity;
        [UIView commitAnimations];

    }else{
        // 前面に持ってくる
        [self.view bringSubviewToFront:label];
        // ちょっと大きくする&傾ける
        label.transform = CGAffineTransformMakeScale(1.3, 1.3);
//        label.transform = CGAffineTransformMakeRotation(M_PI*10.0/360.0);
        // ↑ここをコメントアウトすると、拡大しなくなる
        // 影を付ける
        label.clipsToBounds=NO;
        CALayer *layer = label.layer;
        layer.shadowRadius = 3;
        layer.shadowColor = [UIColor blackColor].CGColor;
        layer.shadowOpacity = 0.5;
        layer.shadowOffset = CGSizeMake(10, 10);
        // めちゃくちゃ速くなるらしい方法
        // http://7gano.tumblr.com/post/4379956470/calayer-shadow
//        layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
        // UIViewを移動
        double x = sender.view.center.x + location.x;
        double y = sender.view.center.y + location.y;
        sender.view.center=CGPointMake(x, y);
        [pan setTranslation:CGPointZero inView:label];
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
```
- layer.shadowXXXをそのまま使うと遅いらしい。このサンプル程度だと違いは分からない。
- -label.transformに複数の値を渡していて、後の値で上書きしているように見えてるけど、setterの中で値を個別に持っていれば問題ないのだろう、けど紛らわしい。- ここ間違い。後の値で上書きされている。

[youtube](http://www.youtube.com/watch?v=Ria_ngeK1qs)