在功能性 UIScrollView 中使用 UIPanGestureRecognizer 平移视图 [英] Pan view using UIPanGestureRecognizer within a functional UIScrollView

查看:38
本文介绍了在功能性 UIScrollView 中使用 UIPanGestureRecognizer 平移视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

我有一个 UIScrollView 包含一个 UIView,我希望允许用户使用 UIPanGestureRecognizer 进行平移.

I have a UIScrollView containing a UIView that I wish to allow the user to pan using a UIPanGestureRecognizer.

为了使其按预期工作,用户应该能够用一根手指平移视图,但可以用另一根手指平移滚动视图 - 在同时(每个手指用一根手指).

In order for this to work as desired, users should be able to pan the view with one finger, but also be able to pan the scroll view with another finger - doing both at the same time (using one finger for each).

但是,当用户平移其中包含的视图时,滚动视图将停止工作.在视图的平移手势结束之前无法平移.

However, the scroll view ceases to work when the user is panning a view contained within it. It cannot be panned until the view's pan gesture ends.

尝试的解决方法

我试图通过覆盖以下 UIGestureRecognizerDelegate 方法启用平移视图和包含它的 UIScrollView 同时滚动来解决此问题:

I tried to work around this by enabling simultaneous scrolling of both the pan view and the UIScrollView that contains it by overriding the following UIGestureRecognizerDelegate method:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

然而,这使得平移视图移动滚动视图.每个元素的平移手势应该相互独立,而不是相互关联.

However, this makes it so that panning the view also moves the scroll view. Each element's panning gesture should be independent of the other, not linked.

演示项目

我已经创建了一个简单的演示项目来演示这一点,在这里:

I have created a simple demo project that should demonstrate this, here:

https://github.com/jeffc-dev/ScrollViewPannerTest

该项目包含一个带有方形视图的滚动视图,应该能够独立于其包含的滚动视图进行平移,但不能.

This project contains a scroll view with a square view that should be able to be panned independently of its containing scroll view, but can not.

我为什么要这样做

这样做的目的是让用户更容易/更快地找到要平移视图的目的地.有点类似于在 Springboard 中重新排列图标:您可以用一根手指平移应用程序图标,同时用另一根手指在页面之间平移,快速找到放置它的位置.我没有使用分页滚动视图 - 只是一个普通的 - 我希望它是一个无缝的平移手势(我不需要/希望用户必须进入摆动模式")但基本原则是一样.

The point of this is to make it easier/quicker for a user to find a destination to pan the view to. The is somewhat analogous to rearranging icons in Springboard: You can use one finger to pan an app icon while simultaneously panning between pages with another finger, quickly finding a place to drop it. I'm not using a paged scroll view - just a normal one - and I want it to be a seamless panning gesture (I don't need/want the user to have to enter a 'wiggle mode') but the basic principle is the same.

更新:DonMag 提出了使用 UILongPressGestureRecognizer 将视图移出滚动视图以进行平移的想法,这看起来很有希望.但是,如果我走那条路,我想我需要在这样做之后无缝过渡到使用 UIPanGestureRecognizer(因为我确实使用了一些平移手势识别器特定的功能).

UPDATE: DonMag helpfully came up with the idea of using a UILongPressGestureRecognizer to move the view out of the scroll view for panning, which does seem promising. However, if I went that route I think I'd need to seamlessly transition to using a UIPanGestureRecognizer after doing so (as I do use some pan gesture recognizer-specific functionality).

推荐答案

我确信有不同的方法可以做到这一点,但这里是一种方法...

I'm sure there are different ways to do this, but here is one approach...

我没有使用 UIPanGesture,而是使用了 UILongPressGesture.

Instead of using a UIPanGesture I used a UILongPressGesture.

当手势开始时,我们移动视图从scrollView到它的superview.当我们继续按下视图并拖动它时,它现在独立于滚动视图.当我们结束手势(抬起手指)时,我们将视图添加回滚动视图.

When the gesture begins, we move the view from the scrollView to its superview. While we continue to press the view and drag it around, it is now independent of the scrollView. When we end the gesture (lift the finger), we add the view back to the scrollView.

在拖动时,我们可以使用第二根手指滚动滚动视图的内容.

While dragging, we can use a second finger to scroll the content of the scroll view.

代码的主要部分如下所示:

The main portion of the code looks like this:

@objc func handleLongPress(_ g: UILongPressGestureRecognizer) -> Void {
    
    switch g.state {
    
    case .began:
        
        // get our superview and its superview
        guard let sv = superview as? UIScrollView,
              let ssv = sv.superview
        else {
            return
        }
        theScrollView = sv
        theRootView = ssv
        
        // convert center coords
        let cvtCenter = theScrollView.convert(self.center, to: theRootView)
        self.center = cvtCenter
        curCenter = self.center
        
        // add self to ssv (removes self from sv)
        ssv.addSubview(self)
        
        // start wiggling anim
        startAnim()
        
        // inform the controller
        startCallback?(self)
        
    case .changed:
        
        guard let thisView = g.view else {
            return
        }
        
        // get the gesture point
        let point = g.location(in: thisView.superview)
        
        // Calculate new center position
        var newCenter = thisView.center;
        newCenter.x += point.x - curCenter.x;
        newCenter.y += point.y - curCenter.y;
        
        // Update view center
        thisView.center = newCenter
        curCenter = newCenter
        
        // inform the controller
        movedCallback?(self)
        
    default:
        
        // stop wiggle anim
        stopAnim()
        
        // convert center to scroll view (original superview) coords
        let cvtCenter = theRootView.convert(curCenter, to: theScrollView)
        
        // update center
        self.center = cvtCenter
        
        // add self back to scroll view
        theScrollView.addSubview(self)
        
        // inform the controller
        endedCallback?(self)
        
    }
    
}

我分叉了你的 GitHub 存储库并添加了一个新的控制器来演示:https://github.com/DonMag/ScrollViewPannerTest

I forked your GitHub repo and added a new controller to demonstrate: https://github.com/DonMag/ScrollViewPannerTest

您会发现它只是此方法的起点.被拖动的视图(实际上,在这个演示中,你可以用两根手指同时拖动两个视图)使用闭包通知控制器拖动...

You'll see that it is just a Starting Point for this approach. The view being dragged (actually, in this demo, you can use two fingers to drag two views at the same time) uses closures to inform the controller about the dragging...

目前,拖/放"不会影响 scrollView 中的任何其他子视图.唯一能做任何事情的闭包是结束的"关闭,此时控制器重新计算 scrollView 的 contentSize.感动"闭包可用于重新定位视图——但这是另一项任务.

Currently, "drag/drop" does not affect any other subviews in the scrollView. The only closure that does anything is the "ended" closure, at which point the controller re-calcs the scrollView's contentSize. The "moved" closure could be used to re-position views -- but that's another task.

这篇关于在功能性 UIScrollView 中使用 UIPanGestureRecognizer 平移视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆