ios - 实现 UIKit Dynamics 以将 View 拖离屏幕

标签 ios objective-c ios7 uikit uikit-dynamics

我正在尝试找出与 Jelly 应用程序中的类似的 UIKit Dynamics 实现(特别是向下滑动以将 View 拖出屏幕)。

看动画:http://vimeo.com/83478484 (@1:17)

我了解 UIKit Dynamics 的工作原理,但没有很好的物理背景,因此无法组合不同的行为来获得所需的结果!

最佳答案

这种拖动可以通过 UIAttachmentBehavior 完成,您可以在 UIGestureRecognizerStateBegan 上创建附件行为,在 UIGestureRecognizerStateChanged 上更改 anchor 。这实现了用户进行平移手势时的旋转拖动。

UIGestureRecognizerStateEnded 上,您可以删除 UIAttachmentBehavior,但随后应用 UIDynamicItemBehavior 让动画以相同的线速度和角速度无缝继续当用户放开它时正在拖动它(不要忘记使用 action block 来确定 View 何时不再与父 View 相交,因此您可以删除动态行为,可能还有 View , 也)。或者,如果您的逻辑决定要将其返回到原始位置,您可以使用 UISnapBehavior 来执行此操作。

坦率地说,根据这个短片,很难准确地确定他们在做什么,但这些是基本的构建 block 。


例如,假设您创建了一些要拖出屏幕的 View :

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];

然后您可以创建一个手势识别器以将其拖出屏幕:

- (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);
}

这会产生(如果您不向下拖动则显示吸附行为,如果您成功向下拖动则显示动态行为):

UIDynamics demo

这只是一个演示的外壳,但它说明了在平移手势期间使用 UIAttachmentBehavior,如果你想在结束时将其捕捉回来,请使用 UISnapBehavior您想要反转手势的动画,但使用 UIDynamicItemBehavior 来完成将其向下拖动到屏幕外的动画,但要从 UIAttachmentBehavior 过渡到最终动画尽可能平滑。我还在最终 UIDynamicItemBehavior 的同时添加了一点重力,以便它平稳地加速离开屏幕(因此不会花费太长时间)。

根据您的需要自定义。值得注意的是,平移手势处理程序非常笨拙,我可能会考虑创建一个自定义识别器来清理该代码。但希望这能说明使用 UIKit Dynamics 将 View 拖离屏幕底部的基本概念。

关于ios - 实现 UIKit Dynamics 以将 View 拖离屏幕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21325057/

相关文章:

ios - 如何在 xcode 5 中运行 ios 6 模拟器?

ios - AddressBookUI View Controller 在 iOS 7 中的行为不符合预期

ios - 从左边缘滑动以关闭 UIViewController

ios - 为什么我的 Swift Playground 中什么都没有显示?

ios - 使用 iOS SDK 4 提取当前 facebook 用户名

ios - UIGraphicsBeginImageContext 不返回视网膜图像

objective-c - Cocoa Objective C - 如何在用户按下 Enter 键或单击侧面文本框时结束 NSTextField 的编辑

ios - 推荐 : RestKit or AFNetworking for Rest/Soap server

ios - 有没有办法在iOS中生成Int64类型的UUID?

ios - iOS7 是如何理解 Swift Code 的?操作系统是否支持 Swift?