Mat*_*att 28 objective-c uikit ios ios7 uikit-dynamics
我正在尝试找出与Jelly的应用程序类似的工具UIKit Dynamics(特别是向下滑动以在屏幕外拖动视图).
看动画:http://vimeo.com/83478484(@ 1:17)
我理解UIKit Dynamics是如何工作的,但是没有很好的物理背景,因此无法梳理不同的行为以获得理想的结果!
Rob*_*Rob 86
这种拖动可以通过UIAttachmentBehavior创建附件行为的位置来完成UIGestureRecognizerStateBegan,更改锚点UIGestureRecognizerStateChanged.当用户进行平移手势时,这实现了旋转拖动.
一旦UIGestureRecognizerStateEnded你可以删除UIAttachmentBehavior,但随后应用一个UIDynamicItemBehavior让动画无缝继续使用相同的线性和角速度,用户在放开它时拖动它(不要忘记使用一个action块来确定何时视图不再与superview相交,因此你可以删除动态行为,也可以删除视图.或者,如果您的逻辑确定要将其返回到原始位置,则可以使用a UISnapBehavior来执行此操作.
坦率地说,在这个短片的基础上,确切地确定他们正在做什么有点困难,但这些是基本的构建块.
例如,假设您创建了一些想要拖离屏幕的视图:
UIView *viewToDrag = [[UIView alloc] initWithFrame:...];
viewToDrag.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:viewToDrag];
UIGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[viewToDrag addGestureRecognizer:pan];
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
Run Code Online (Sandbox Code Playgroud)
然后,您可以创建一个手势识别器将其拖离屏幕:
- (void)handlePan:(UIPanGestureRecognizer *)gesture {
static UIAttachmentBehavior *attachment;
static CGPoint startCenter;
// variables for calculating angular velocity
static CFAbsoluteTime lastTime;
static CGFloat lastAngle;
static CGFloat angularVelocity;
if (gesture.state == UIGestureRecognizerStateBegan) {
[self.animator removeAllBehaviors];
startCenter = gesture.view.center;
// calculate the center offset and anchor point
CGPoint pointWithinAnimatedView = [gesture locationInView:gesture.view];
UIOffset offset = UIOffsetMake(pointWithinAnimatedView.x - gesture.view.bounds.size.width / 2.0,
pointWithinAnimatedView.y - gesture.view.bounds.size.height / 2.0);
CGPoint anchor = [gesture locationInView:gesture.view.superview];
// create attachment behavior
attachment = [[UIAttachmentBehavior alloc] initWithItem:gesture.view
offsetFromCenter:offset
attachedToAnchor:anchor];
// code to calculate angular velocity (seems curious that I have to calculate this myself, but I can if I have to)
lastTime = CFAbsoluteTimeGetCurrent();
lastAngle = [self angleOfView:gesture.view];
typeof(self) __weak weakSelf = self;
attachment.action = ^{
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
CGFloat angle = [weakSelf angleOfView:gesture.view];
if (time > lastTime) {
angularVelocity = (angle - lastAngle) / (time - lastTime);
lastTime = time;
lastAngle = angle;
}
};
// add attachment behavior
[self.animator addBehavior:attachment];
} else if (gesture.state == UIGestureRecognizerStateChanged) {
// as user makes gesture, update attachment behavior's anchor point, achieving drag 'n' rotate
CGPoint anchor = [gesture locationInView:gesture.view.superview];
attachment.anchorPoint = anchor;
} else if (gesture.state == UIGestureRecognizerStateEnded) {
[self.animator removeAllBehaviors];
CGPoint velocity = [gesture velocityInView:gesture.view.superview];
// if we aren't dragging it down, just snap it back and quit
if (fabs(atan2(velocity.y, velocity.x) - M_PI_2) > M_PI_4) {
UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:gesture.view snapToPoint:startCenter];
[self.animator addBehavior:snap];
return;
}
// otherwise, create UIDynamicItemBehavior that carries on animation from where the gesture left off (notably linear and angular velocity)
UIDynamicItemBehavior *dynamic = [[UIDynamicItemBehavior alloc] initWithItems:@[gesture.view]];
[dynamic addLinearVelocity:velocity forItem:gesture.view];
[dynamic addAngularVelocity:angularVelocity forItem:gesture.view];
[dynamic setAngularResistance:1.25];
// when the view no longer intersects with its superview, go ahead and remove it
typeof(self) __weak weakSelf = self;
dynamic.action = ^{
if (!CGRectIntersectsRect(gesture.view.superview.bounds, gesture.view.frame)) {
[weakSelf.animator removeAllBehaviors];
[gesture.view removeFromSuperview];
[[[UIAlertView alloc] initWithTitle:nil message:@"View is gone!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
}
};
[self.animator addBehavior:dynamic];
// add a little gravity so it accelerates off the screen (in case user gesture was slow)
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[gesture.view]];
gravity.magnitude = 0.7;
[self.animator addBehavior:gravity];
}
}
- (CGFloat)angleOfView:(UIView *)view
{
// http://stackoverflow.com/a/2051861/1271826
return atan2(view.transform.b, view.transform.a);
}
Run Code Online (Sandbox Code Playgroud)
这会产生(如果不向下拖动则显示快照行为,如果成功将其向下拖动则显示动态行为):

这只是一个演示的外壳,但是它说明了UIAttachmentBehavior在平移手势期间使用a,UISnapBehavior如果你想要反转手势的动画,你可以使用a ,但是用它UIDynamicItemBehavior来完成拖动它的动画,离开屏幕,但UIAttachmentBehavior尽可能顺利地从最终动画转换到最终动画.我还在最后的同时添加了一点引力,UIDynamicItemBehavior以便它平滑地从屏幕加速(所以它不会花太长时间).
根据需要自定义.值得注意的是,该平移手势处理程序非常笨重,我可能会考虑创建一个自定义识别器来清理该代码.但希望这说明了使用UIKit Dynamics将视图拖离屏幕底部的基本概念.
@Rob 的回答很棒(点赞!),但我会删除手动角速度计算,让 UIDynamics 使用UIPushBehavior. 只需设置目标偏移量,UIPushBehaviorUIDynamics 就会为你做旋转计算工作。
从@Rob 的相同设置开始:
UIView *viewToDrag = [[UIView alloc] initWithFrame:...];
viewToDrag.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:viewToDrag];
UIGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[viewToDrag addGestureRecognizer:pan];
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
Run Code Online (Sandbox Code Playgroud)
但是调整手势识别器处理程序以使用 UIPushBehavior
- (void)handlePan:(UIPanGestureRecognizer *)gesture {
static UIAttachmentBehavior *attachment;
static CGPoint startCenter;
if (gesture.state == UIGestureRecognizerStateBegan) {
[self.animator removeAllBehaviors];
startCenter = gesture.view.center;
// calculate the center offset and anchor point
CGPoint pointWithinAnimatedView = [gesture locationInView:gesture.view];
UIOffset offset = UIOffsetMake(pointWithinAnimatedView.x - gesture.view.bounds.size.width / 2.0,
pointWithinAnimatedView.y - gesture.view.bounds.size.height / 2.0);
CGPoint anchor = [gesture locationInView:gesture.view.superview];
// create attachment behavior
attachment = [[UIAttachmentBehavior alloc] initWithItem:gesture.view
offsetFromCenter:offset
attachedToAnchor:anchor];
// add attachment behavior
[self.animator addBehavior:attachment];
} else if (gesture.state == UIGestureRecognizerStateChanged) {
// as user makes gesture, update attachment behavior's anchor point, achieving drag 'n' rotate
CGPoint anchor = [gesture locationInView:gesture.view.superview];
attachment.anchorPoint = anchor;
} else if (gesture.state == UIGestureRecognizerStateEnded) {
[self.animator removeAllBehaviors];
CGPoint velocity = [gesture velocityInView:gesture.view.superview];
// if we aren't dragging it down, just snap it back and quit
if (fabs(atan2(velocity.y, velocity.x) - M_PI_2) > M_PI_4) {
UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:gesture.view snapToPoint:startCenter];
[self.animator addBehavior:snap];
return;
}
// otherwise, create UIPushBehavior that carries on animation from where the gesture left off
CGFloat velocityMagnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[gesture.view] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.pushDirection = CGVectorMake((velocity.x / 10) , (velocity.y / 10));
// some constant to limit the speed of the animation
pushBehavior.magnitude = velocityMagnitude / 35.0;
CGPoint finalPoint = [gesture locationInView:gesture.view.superview];
CGPoint center = gesture.view.center;
[pushBehavior setTargetOffsetFromCenter:UIOffsetMake(finalPoint.x - center.x, finalPoint.y - center.y) forItem:gesture.view];
// when the view no longer intersects with its superview, go ahead and remove it
typeof(self) __weak weakSelf = self;
pushBehavior.action = ^{
if (!CGRectIntersectsRect(gesture.view.superview.bounds, gesture.view.frame)) {
[weakSelf.animator removeAllBehaviors];
[gesture.view removeFromSuperview];
[[[UIAlertView alloc] initWithTitle:nil message:@"View is gone!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
}
};
[self.animator addBehavior:pushBehavior];
// add a little gravity so it accelerates off the screen (in case user gesture was slow)
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[gesture.view]];
gravity.magnitude = 0.7;
[self.animator addBehavior:gravity];
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9024 次 |
| 最近记录: |