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

现在我有一个scrollView,占用整个视图控制器。 下面的代码能够移动scrollView左右,但我想移动整个视图控制器。 我该怎么做?

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

我已经尝试了“self.view.center ….”,“UIApplication.sharedApplication.rootViewController.view.center ..”等不同的变体。

我从你的另一个问题推断,你想要一个手势来解雇这个视图控制器。 而不是在手势中自己操纵视图,我build议您使用UIPercentDrivenInteractiveTransition交互控制器的自定义转换,并让手势只是操纵交互控制器。 这实现了相同的用户体验,但符合苹果的定制转换范例。

这里有趣的问题是如何在自定义closures转换手势和滚动视图手势之间进行划分。 你想要的是某种被某种方式约束的姿态。 这里有很多的select:

  • 如果滚动视图只是左右移动,则有一个自定义平移手势子类,如果水平使用它,则会失败;

  • 如果滚动视图也是上下的,那么有一个顶部的“屏幕边缘手势识别器”或添加一些视觉元素,这是一个“抓手”,你把平底锅手势

但是,无论你如何devise这个手势,滚动视图的手势都需要你自己的手势在触发之前失败。

例如,如果你想要一个屏幕边缘手势识别器,那看起来像:

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

就我个人而言,我发现屏幕边缘的顶部和底部手势的概念是一个糟糕的用户体验,所以我亲自改变这种模式的演示文稿,从右侧滑入,然后从左侧边缘滑到右边感觉合乎逻辑,不会干涉内置的顶部拉下(iOS通知)。 或者,如果滚动视图只能水平滚动,那么您可以拥有自己的垂直平移手势,如果不是垂直平移,则会失败。

或者,如果滚动视图只向左和向右滚动,则可以添加自己的平移手势,只有在(a)使用UIGestureRecognizerDelegate才能识别向下平移时,才能识别平移手势。 (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 } 

就像我说的,这里有很多select。 但是你们还没有分享足够的devise给我们,就此进一步提供build议。

但是上面的例子说明了基本思想,你不应该把自己的观点转移到自己身上,而是用你自己的animation师和你自己的交互控制器来进行自定义的转换。

有关更多信息,请参阅使用视图控制器的 WWDC 2013 自定义转换 (以及WWDC 2014 A Look Inside Presentation控制器 ,如果您想了解更多关于自定义转换演变的信息)。