简体   繁体   English

基于滚动视图中平移手势的运动视图控制器

[英]Moving view controller based on pan gesture in scrollview

Right now I have a scrollView that takes up the entire view controller. 现在,我有一个scrollView占据了整个视图控制器。 The code below is able to move the scrollView around but I want to move the whole view controller around. 下面的代码能够移动scrollView,但是我想移动整个视图控制器。 How would I do that? 我该怎么办?

override func viewDidLoad() {
        pan = UIPanGestureRecognizer(target: self, action: "handlePan:")
        self.scrollview.addGestureRecognizer(pan)
}

func handlePan(recognizer:UIPanGestureRecognizer!) {

    switch recognizer.state {
    case .Changed:
        handlePanChanged(recognizer); break
    case .Ended:
        handlePanTerminated(recognizer); break
    case .Cancelled:
        handlePanTerminated(recognizer); break
    case .Failed:
        handlePanTerminated(recognizer); break
    default: break
    }
}

func handlePanChanged(recognizer:UIPanGestureRecognizer!) {
    if let view = recognizer.view {
        var translation = recognizer.translationInView(self.view)
        println("moving")
        view.center = CGPointMake(view.center.x, view.center.y + translation.y);
        recognizer.setTranslation(CGPointZero, inView: self.view)
    }
}

I've tried different variations of "self.view.center ...." "UIApplication.sharedApplication.rootViewController.view.center.." etc. 我尝试了“ self.view.center ....”,“ UIApplication.sharedApplication.rootViewController.view.center ..”等不同的变体。

I infer from your other question that you want to a gesture to dismiss this view controller. 我从您的另一个问题推断出,您想要手势以关闭该视图控制器。 Rather than manipulating the view yourself in the gesture, I'd suggest you use custom transition with a UIPercentDrivenInteractiveTransition interaction controller, and have the gesture just manipulate the interaction controller. 建议您UIPercentDrivenInteractiveTransition通过UIPercentDrivenInteractiveTransition交互控制器使用自定义过渡, UIPercentDrivenInteractiveTransition让手势仅操作交互控制器。 This achieves the same UX, but in a manner consistent with Apple's custom transitions paradigm. 这样可以实现相同的UX,但其方式与Apple的自定义转换范例一致。

The interesting question here is how do you want to delineate between the custom dismiss transition gesture and the scroll view gesture. 有趣的问题是您如何在自定义关闭过渡手势和滚动视图手势之间划界。 What you want is some gesture that is constrained in some fashion. 您想要的是以某种方式约束的某种手势。 There are tons of options here: 这里有很多选择:

  • If the scroll view is left-right only, have a custom pan gesture subclass that fails if you use it horizontally; 如果滚动视图仅是左右视图,则有一个自定义的平移手势子类,如果水平使用它,该子类将失败。

  • If the scroll view is up-down, too, then have a top "screen edge gesture recognizer" or add some visual element that is a "grab bar" to which you tie a pan gesture 如果滚动视图也是上下视图,则具有顶部的“屏幕边缘手势识别器”或添加一些与平移手势相关联的可视元素(即“抓取条”)

But however you design this gesture to work, have the scroll view's gestures require that your own gesture fails before they trigger. 但是,无论您将这个手势设计为可以工作,要让滚动视图的手势要求您自己的手势在触发之前失败。

For example, if you wanted a screen edge gesture recognizer, that would look like: 例如,如果您想要一个屏幕边缘手势识别器,它将类似于:

class SecondViewController: UIViewController, UIViewControllerTransitioningDelegate {

    @IBOutlet weak var scrollView: UIScrollView!

    var interactionController: UIPercentDrivenInteractiveTransition?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        modalPresentationStyle = .Custom
        transitioningDelegate = self
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // ...

        let edge = UIScreenEdgePanGestureRecognizer(target: self, action: "handleScreenEdgeGesture:")
        edge.edges = UIRectEdge.Top
        view.addGestureRecognizer(edge)
        for gesture in scrollView.gestureRecognizers! {
            gesture.requireGestureRecognizerToFail(edge)
        }
    }

    // because we're using top edge gesture, hide status bar

    override func prefersStatusBarHidden() -> Bool {
        return true
    }

    func handleScreenEdgeGesture(gesture: UIScreenEdgePanGestureRecognizer) {
        switch gesture.state {
        case .Began:
            interactionController = UIPercentDrivenInteractiveTransition()
            dismissViewControllerAnimated(true, completion: nil)
        case .Changed:
            let percent = gesture.translationInView(gesture.view).y / gesture.view!.frame.size.height
            interactionController?.updateInteractiveTransition(percent)
        case .Cancelled:
            fallthrough
        case .Ended:
            if gesture.velocityInView(gesture.view).y < 0 || gesture.state == .Cancelled || (gesture.velocityInView(gesture.view).y == 0 && gesture.translationInView(gesture.view).y < view.frame.size.height / 2.0) {
                interactionController?.cancelInteractiveTransition()
            } else {
                interactionController?.finishInteractiveTransition()
            }
            interactionController = nil
        default: ()
        }
    }

    @IBAction func didTapDismissButton(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
    }

    // MARK: UIViewControllerTransitioningDelegate

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DismissAnimation()
    }

    func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactionController
    }

}

class DismissAnimation: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return 0.25
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let from = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
        let container = transitionContext.containerView()!

        let height = container.bounds.size.height

        UIView.animateWithDuration(transitionDuration(transitionContext), animations:
            {
                from.view.transform = CGAffineTransformMakeTranslation(0, height)
            }, completion: { finished in
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            }
        )
    }

}

Personally, I find the notion of having top and bottom screen edge gestures to be a bad UX, so I'd personally change this modal presentation to slide in from the right, and then swiping from left edge to the right feels logical, and doesn't interfere with the built in top pull down (for iOS notifications). 就个人而言,我发现使用顶部和底部屏幕边缘手势是一种不好的用户体验,因此我个人将这种模式显示更改为从右侧滑入,然后从左侧边缘向右侧滑动感觉很合逻辑,并且不会干扰内置的顶部下拉菜单(适用于iOS通知)。 Or if the scroll view only scrolls horizontally, then you can just have your own vertical pan gesture that fails if it's not a vertical pan. 或者,如果滚动视图仅水平滚动,则您可以拥有自己的垂直平移手势,如果不是垂直平移,则手势会失败。

Or, if the scroll view only scrolls left and right, you can add your own pan gesture that is only recognized when you pull down by (a) using UIGestureRecognizerDelegate to recognize downward pans only; 或者,如果滚动视图仅向左和向右滚动,则可以添加自己的平移手势,只有当您使用(a)使用UIGestureRecognizerDelegate向下下拉时,才能识别该平移手势;仅识别向下平移; and (b) again setting the scroll view gestures to only recognize gestures if our pull-down gesture fails: (b)再次将滚动视图手势设置为仅在我们的下拉手势失败时才能识别手势:

override func viewDidLoad() {
    super.viewDidLoad()

    // ...

    let pan = UIPanGestureRecognizer(target: self, action: "handlePan:")
    pan.delegate = self
    view.addGestureRecognizer(pan)

    for gesture in scrollView.gestureRecognizers! {
        gesture.requireGestureRecognizerToFail(pan)
    }
}

func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
    if let gesture = gestureRecognizer as? UIPanGestureRecognizer {
        let translation = gesture.translationInView(gesture.view)
        let angle = atan2(translation.x, translation.y)
        return abs(angle) < CGFloat(M_PI_4 / 2.0)
    }
    return true
}

func handlePan(gesture: UIPanGestureRecognizer) {
    // the same as the `handleScreenEdgeGesture` above
}

Like I said, tons of options here. 就像我说的,这里有很多选择。 But you haven't shared enough of your design for us to advise you further on that. 但是您还没有分享足够的设计信息,无法让我们进一步建议您。

But the above illustrates the basic idea, that you shouldn't be moving the view around yourself, but rather use custom transition with your own animators and your own interactive controller. 但是上面的内容说明了基本思想,即您不应该在自己周围移动视图,而应将自定义过渡与自己的动画师和自己的交互式控制器一起使用。

For more information, see WWDC 2013 Custom Transitions Using View Controllers (and also WWDC 2014 A Look Inside Presentation Controllers , if you want a little more information on the evolution of custom transitions). 有关更多信息,请参阅使用视图控制器的 WWDC 2013 自定义过渡 (如果需要有关自定义过渡的更多信息,请参阅WWDC 2014 A Look Inside Presentation Controllers )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM