UIKit Dynamicsを使う簡単なチュートリアルを書いてみました。
箱に重力を追加する
まずは箱をひとつ作って、それを重力をつけて下に落としてみます。
Xcodeで「Single View Application」なプロジェクトを新規作成します。
灰色で、64x64の大きさのUIViewの箱を上のほうにつくってOutletに接続します。
続いて、重力を付けるように実装します。
UDSViewController.mにあるビューコントローラの無名カテゴリにanimator
とgravity
を追加します。
1 2 3 4 | @interface UDSViewController () @property UIDynamicAnimator* animator; @property UIGravityBehavior* gravity; @end |
そして、viewDidLoad
でこれらを初期化します。
1 2 3 4 5 6 7 8 9 | - ( 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
を追加します。
1 2 3 4 5 | @interface UDSViewController () @property UIDynamicAnimator* animator; @property UIGravityBehavior* gravity; @property UICollisionBehavior* collision; @end |
そして、viewDidLoad
に初期化コードを追加します。
1 2 3 4 5 6 7 8 9 10 11 | - ( 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
の下に次のコードを追加します。
1 2 3 4 5 6 7 | 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
にします。
そして、それに対するタップ動作を次のように定義します。
そして、その実装では次のように書きます。
1 2 3 4 5 6 7 8 9 10 11 | - ( 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 , 4 8 , 4 8 )]; box .layer .backgroundColor = UIColor .grayColor .CGColor ; [ self .view addSubview :box]; [_gravity addItem :box]; [_collision addItem :box]; } |
追加した箱に重力と衝突の両方の挙動をさせたいので_gravity
と_collision
の両方でaddItem:
をしています。
つるつるにする
物体の跳ねかたや表面のつるつる具合を変更するにはUIDynamicItemBehavior
を使います。
まず、無名カテゴリにdynamicProperties
を追加します。
1 2 3 4 5 6 | @interface UDSViewController () @property UIDynamicAnimator* animator; @property UIGravityBehavior* gravity; @property UICollisionBehavior* collision; @property UIDynamicItemBehavior* dynamicProperties; @end |
そして、viewDidLoad
で初期化します。
1 2 3 4 5 6 7 8 | _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
にも追加するようにします。
1 2 3 4 | [ self .view addSubview :box]; [_gravity addItem :box]; [_collision addItem :box]; [_dynamicProperties addItem :box]; |
横から落ちたら消す
今のところ、横から落ちたら見えなくなりますが、オブジェクトを削除しているわけではないのでメモリの無駄です。
そこで、横から落ちたオブジェクトは削除するようにします。
まず、無名カテゴリをUICollisionBehaviorDelegate
プロトコル準拠にします。
1 | @interface UDSViewController () <UICollisionBehaviorDelegate> |
そして、viewDidLoad
でcollisionDelegate
と別のバウンダリを追加します。これは、横から落ちたらひっかかるように、@"bottom"
よりもちょっと下で、幅は左右広めに取っています。
1 2 3 4 5 | _collision .collisionDelegate = self ; [_collision addBoundaryWithIdentifier : @"outer-edge" fromPoint :CGPointMake(- 9 9 9 9 , h + 1 0 ) toPoint :CGPointMake( 9 9 9 9 +w, h + 1 0 )]; |
そして、@"outer-edge"
に降れたら消すように、次のコードを追加します。
collisionBehavior:beganContactForItem:
はアイテムとボーダとの衝突時に呼ばれるメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | - ( 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:
で色を適当につけます。
1 2 3 | UIView* box = [UIView .alloc initWithFrame :CGRectMake(p .x , p .y , 4 8 , 4 8 )]; NSArray* colors = @[UIColor .grayColor , UIColor .redColor , UIColor .blueColor , UIColor .greenColor ]; box .layer .backgroundColor = [colors[rand() % colors .count ] CGColor ]; |
そして、次のコードを追加します。collisionBehavior:beganContactForItem:withItem:atPoint:
はアイテム同士の衝突時に呼ばれるメソッドです。
1 2 3 4 5 6 7 8 | - ( void )collisionBehavior:( UICollisionBehavior *)behavior beganContactForItem :( id <UIDynamicItem>)item 1 withItem :( id <UIDynamicItem>)item 2 atPoint :(CGPoint)p { CGColorRef color 1 = [[(UIView*)item 1 layer ] backgroundColor ]; CGColorRef color 2 = [[(UIView*)item 2 layer ] backgroundColor ]; if (CGColorEqualToColor(color 1 , color 2 )) { [ self removeItem :(UIView*) item1 ]; [ self removeItem :(UIView*) item2 ]; } } |
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。