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 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。