问题描述
我正在尝试找出与Jelly的应用程序类似的工具UIKit Dynamics(特别是向下滑动以在屏幕外拖动视图)。
I'm trying to figure out implement UIKit Dynamics that are similar to those in Jelly's app (specifically swiping down to drag view off-screen).
查看动画:(@ 1:17)
See the animation: http://vimeo.com/83478484 (@1:17)
我理解UIKit Dynamics是如何工作的,但没有很好的物理背景,因此无法梳理不同的行为以获得理想的结果!
I understand how UIKit Dynamics work, but don't have a great physics background and therefore am having trouble combing the different behaviors to get the desired result!
推荐答案
这种拖动可以用 UIAttachmentBehavior
完成,你可以在 UIGestureRecognizerStateBegan $ c $上创建附件行为c>,在
UIGestureRecognizerStateChanged
时更改锚点。这可以在用户进行平移手势时实现旋转拖动。
This sort of dragging can be accomplished with an UIAttachmentBehavior
where you create the attachment behavior upon UIGestureRecognizerStateBegan
, change the anchor upon UIGestureRecognizerStateChanged
. This achieves the dragging with rotation as the user conducts the pan gesture.
在 UIGestureRecognizerStateEnded
后,您可以删除 UIAttachmentBehavior
,但随后应用 UIDynamicItemBehavior
让动画无缝地继续使用用户拖动时相同的线性和角速度他们放开它(不要忘记使用 action
块来确定视图何时不再与superview相交,这样你就可以删除动态行为,可能还有视图,也)。或者,如果您的逻辑确定要将其返回到原始位置,则可以使用 UISnapBehavior
来执行此操作。
Upon UIGestureRecognizerStateEnded
you can remove the UIAttachmentBehavior
, but then apply a UIDynamicItemBehavior
to have the animation seamlessly continue with the same linear and angular velocities the user was dragging it when they let go of it (don't forget to use an action
block to determine when the view no longer intersects the superview, so you can remove the dynamic behavior and probably the view, too). Or, if your logic determines that you want to return it back to the original location, you can use a UISnapBehavior
to do so.
坦率地说,基于这个短片,确切地确定他们正在做什么有点困难,但这些是基本的构建块。
Frankly, on the basis of this short clip, it's a little tough to determine precisely what they're doing, but these are the basic building blocks.
例如,我们假设您要创建一些想要拖出屏幕的视图:
For example, let's assume you create some view you want to drag off the screen:
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];
然后你可以创建一个手势识别器将其拖离屏幕:
You can then create a gesture recognizer to drag it off the screen:
- (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);
}
产生(如果你没有向下拖动,则显示快照行为,以及成功将其向下拖动时的动态行为):
That yields (showing both the snap behavior if you don't drag down, as well as the dynamic behavior if you successfully drag it down):
这只是演示的一个shell,但它说明了使用 UIAttachmentBehavior
在平移手势期间,使用 UISnapBehavior
如果您想要将其反馈,如果您想要反转手势的动画,但使用 UIDynamicItemBehavior
完成将其拖放到屏幕外的动画,但是尽可能顺利地从 UIAttachmentBehavior
转换到最终动画。我还在最后的 UIDynamicItemBehavior
的同时添加了一点引力,以便它平滑地加速离开屏幕(所以它不会花太长时间)。
This is only a shell of a demonstration, but it illustrates using a UIAttachmentBehavior
during the pan gesture, using a UISnapBehavior
if you want to snap it back if you conclude you want to reverse the gesture's animation, but using UIDynamicItemBehavior
to finish the animation of dragging it down, off the screen, but making the transition from the the UIAttachmentBehavior
to the final animation as smooth as possible. I also added a little gravity at the same time as that final UIDynamicItemBehavior
so that it smoothly accelerates off the screen (so it doesn't take too long).
根据需要自定义此项。值得注意的是,该平移手势处理程序非常笨重,我可能会考虑创建一个自定义识别器来清理该代码。但希望这说明了使用UIKit Dynamics将视图拖离屏幕底部的基本概念。
Customize this as you see fit. Notably, that pan gesture handler is unwieldy enough that I might contemplate creating a custom recognizer to clean up that code. But hopefully this illustrates the basic concepts in using UIKit Dynamics to drag a view off the bottom of the screen.
这篇关于实现UIKitDynamics以将视图拖离屏幕的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!