UIKit Dynamicsを使う簡単なチュートリアルを書いてみました。
箱に重力を追加する
まずは箱をひとつ作って、それを重力をつけて下に落としてみます。
Xcodeで「Single View Application」なプロジェクトを新規作成します。
灰色で、64x64の大きさのUIViewの箱を上のほうにつくってOutletに接続します。
続いて、重力を付けるように実装します。
UDSViewController.mにあるビューコントローラの無名カテゴリにanimator
とgravity
を追加します。
@interface UDSViewController () @property UIDynamicAnimator* animator; @property UIGravityBehavior* gravity; @end
そして、viewDidLoad
でこれらを初期化します。
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; _gravity = [[UIGravityBehavior alloc] initWithItems:@[self.firstBox]]; [_animator addBehavior:_gravity]; }
これだけで、実行すると上のほうにある箱が下に落ちていきます。
床を作る
今のままだと、下に床がないので箱がそのまま消えてしまいます。
なので、床を足してみましょう。
まず、無名カテゴリにcollision
を追加します。
@interface UDSViewController () @property UIDynamicAnimator* animator; @property UIGravityBehavior* gravity; @property UICollisionBehavior* collision; @end
そして、viewDidLoad
に初期化コードを追加します。
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; _gravity = [[UIGravityBehavior alloc] initWithItems:@[self.firstBox]]; _collision = [[UICollisionBehavior alloc] initWithItems:@[self.firstBox]]; [_animator addBehavior:_gravity]; [_animator addBehavior:_collision]; }
さらに、viewDidLoad
の下に次のコードを追加します。
CGSize s = self.view.frame.size; CGFloat w = s.width; CGFloat h = s.height; [_collision addBoundaryWithIdentifier:@"bottom" fromPoint:CGPointMake(0, h) toPoint:CGPointMake(w, h)];
これで、下に床が追加されました。
クラス同士の関係
この時点でのクラス同士の関係を次に示します。
UIDynamicAnimator
は、「重力」や「衝突」といった挙動をプラグイン化したUIDynamicBehavior
のサブクラスを持つことができ、このサブクラスは、その挙動をするアイテムを持っています。このアイテムはUIDynamicItem
プロトコルに従う必要があります。
他のオブジェクトを作る
続いて、タップで新規オブジェクトを追加するようにしてみます。
まず、ストーリボード上のトップレベルのUIViewのCustom ClassをUIButton
にします。
そして、それに対するタップ動作を次のように定義します。
そして、その実装では次のように書きます。
- (IBAction)onViewTapped:(UIButton *)sender forEvent:(UIEvent *)event { NSSet* set = [event touchesForView:sender]; CGPoint p = [set.allObjects[0] locationInView:sender]; UIView* box = [UIView.alloc initWithFrame:CGRectMake(p.x, p.y, 48, 48)]; box.layer.backgroundColor = UIColor.grayColor.CGColor; [self.view addSubview:box]; [_gravity addItem:box]; [_collision addItem:box]; }
追加した箱に重力と衝突の両方の挙動をさせたいので_gravity
と_collision
の両方でaddItem:
をしています。
つるつるにする
物体の跳ねかたや表面のつるつる具合を変更するにはUIDynamicItemBehavior
を使います。
まず、無名カテゴリにdynamicProperties
を追加します。
@interface UDSViewController () @property UIDynamicAnimator* animator; @property UIGravityBehavior* gravity; @property UICollisionBehavior* collision; @property UIDynamicItemBehavior* dynamicProperties; @end
そして、viewDidLoad
で初期化します。
_dynamicProperties = [[UIDynamicItemBehavior alloc] initWithItems:@[self.firstBox]]; _dynamicProperties.density = 1; // 物体の密度 _dynamicProperties.elasticity = 0.5; // 物体の跳ね具合 _dynamicProperties.friction = 0.05; // 表面の滑らかさ [_animator addBehavior:_gravity]; [_animator addBehavior:_collision]; [_animator addBehavior:_dynamicProperties];
また、onViewTapped:forEvent:
でオブジェクト追加時に_dynamicProperties
にも追加するようにします。
[self.view addSubview:box]; [_gravity addItem:box]; [_collision addItem:box]; [_dynamicProperties addItem:box];
横から落ちたら消す
今のところ、横から落ちたら見えなくなりますが、オブジェクトを削除しているわけではないのでメモリの無駄です。
そこで、横から落ちたオブジェクトは削除するようにします。
まず、無名カテゴリをUICollisionBehaviorDelegate
プロトコル準拠にします。
@interface UDSViewController () <UICollisionBehaviorDelegate>
そして、viewDidLoad
でcollisionDelegate
と別のバウンダリを追加します。これは、横から落ちたらひっかかるように、@"bottom"
よりもちょっと下で、幅は左右広めに取っています。
_collision.collisionDelegate = self; [_collision addBoundaryWithIdentifier:@"outer-edge" fromPoint:CGPointMake(-9999, h + 10) toPoint:CGPointMake(9999+w, h + 10)];
そして、@"outer-edge"
に降れたら消すように、次のコードを追加します。
collisionBehavior:beganContactForItem:
はアイテムとボーダとの衝突時に呼ばれるメソッドです。
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p { if ([(NSString*) identifier isEqualToString:@"outer-edge"]) { [self removeItem:(UIView*)item]; } } - (void)removeItem:(UIView*)item { NSLog(@"%@", item); [item removeFromSuperview]; [_gravity removeItem:item]; [_collision removeItem:item]; [_dynamicProperties removeItem:item]; }
同じ色なら消す
衝突時にbackgroundColor
が同じなら、アイテムを消すようにしてみます。
それにはまず、onViewTapped:forEvent:
で色を適当につけます。
UIView* box = [UIView.alloc initWithFrame:CGRectMake(p.x, p.y, 48, 48)]; NSArray* colors = @[UIColor.grayColor, UIColor.redColor, UIColor.blueColor, UIColor.greenColor]; box.layer.backgroundColor = [colors[rand() % colors.count] CGColor];
そして、次のコードを追加します。collisionBehavior:beganContactForItem:withItem:atPoint:
はアイテム同士の衝突時に呼ばれるメソッドです。
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item1 withItem:(id<UIDynamicItem>)item2 atPoint:(CGPoint)p { CGColorRef color1 = [[(UIView*)item1 layer] backgroundColor]; CGColorRef color2 = [[(UIView*)item2 layer] backgroundColor]; if (CGColorEqualToColor(color1, color2)) { [self removeItem:(UIView*) item1]; [self removeItem:(UIView*) item2]; } }
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。