在一个包含PageViewController的TableView中,如何实现滑动删除?

13

我在一个pageViewController中放置了一个tableView,当滑动单元格以显示删除选项时,手势只在某些情况下被识别,比如你非常快速和激进地滑动。

我想这是因为它不确定滑动手势是针对pageView还是tableView。是否有一种特定的方式来确定滑动手势发生的位置,以启用漂亮而平滑地显示删除按钮的效果?


1
你可以添加一个编辑按钮。然后在编辑模式下,你可以通过将代理设置为nil来禁用页面控制器的滑动手势。 - user1199624
5个回答

19

理论:

UIPageViewControllerUITableView都使用UIScrollView实现,其中UIPageViewController嵌入了UIScrollView,而UITableViewUIScrollView的子类。

UITableView还使用了一些UIPanGestureRecognizer来实现一些功能。其中之一是UISwipeActionPanGestureRecognizer,用于处理滑动删除操作。

这个问题是由于UIPageViewControllerUIPanGestureRecognizer在与UITableViewUISwipeActionPanGestureRecognizer冲突时获胜所导致的。

因此,我们必须想办法告诉UIPageViewController如果UITableViewUISwipeActionPanGestureRecognizer正在执行,则应忽略手势。

幸运的是,UIGestureRecognizer已经提供了一些方法。

UIGestureRecognizerrequire(toFail otherGestureRecognizer:UIGestureRecognizer)创建了两个手势识别器之间的关系,以防止手势动作被调用,直到另一个手势识别器失败为止。

所以我们需要做的就是在UIPageViewControllers嵌入UIScrollview时,当触发UITableViewUISwipeActionPanGestureRecognizer时,将其panGestureRecognizer设置为失败。

有两种方法可以实现这个目标。

解决方案1:向表视图添加一个新的手势识别器,并模仿UISwipeActionPanGestureRecognizer。并使UIPageViewController panGesture需要失败此新的gestureRecognizer

解决方案2(有点不太好):对UITableViewUISwipeActionPanGestureRecognizer进行字符串比较,并使UIPageViewController panGesture需要失败此新的gestureRecognizer

代码:

解决方案1 一个有用的工具来获取UIPageViewController嵌入的UIScrollView

extension UIPageViewController {

    var scrollView: UIScrollView? {
        return view.subviews.first { $0 is UIScrollView } as? UIScrollView
    }

}

将以下代码添加到包含UITableViewUIViewController中,并从viewDidLoad()调用它。
func handleSwipeDelete() {
    if let pageController = parent?.parent as? UIPageViewController {
        let gestureRecognizer = UIPanGestureRecognizer(target: self, action: nil)
        gestureRecognizer.delaysTouchesBegan = true
        gestureRecognizer.cancelsTouchesInView = false
        gestureRecognizer.delegate = self
        tableView.addGestureRecognizer(gestureRecognizer)

        pageController.scrollView?.canCancelContentTouches = false
        pageController.scrollView?.panGestureRecognizer.require(toFail: gestureRecognizer)
    }
}

最后是委托方法

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer else {
        return false
    }

    let translation = panGesture.translation(in: tableView)
    // In my case I have only trailing actions, so I used below condition.
    return translation.x < 0
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                       shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return otherGestureRecognizer.view == tableView
}
解决方案2(有点不太干净) 一个有用的工具,可以获取嵌入了UIScrollViewUIPageViewController
extension UIPageViewController {

    var scrollView: UIScrollView? {
        return view.subviews.first { $0 is UIScrollView } as? UIScrollView
    }

}

将以下代码添加到包含UITableViewUIViewController中,并从viewDidLoad()调用它。
func handleSwipeDelete() {
    guard let pageController = parent as? UIPageViewController else {
        return
    }

    pageController.scrollView?.canCancelContentTouches = false
    tableView.gestureRecognizers?.forEach { recognizer in
        let name = String(describing: type(of: recognizer))
        guard name == "_UISwipeActionPanGestureRecognizer" else {
            return
        }
        pageController.scrollView?
            .panGestureRecognizer
            .require(toFail: recognizer)
    }
}

不错,它完美地适用于我的tableview,即使它在一个containerview中,而这个containerview又在另一个scrollview中,后者是UIPageViewcontroller的子视图控制器。 - Vasco
@Vasco 谢谢。请随意点赞,这样答案就能在堆栈中上升,让人们快速找到它。 - Srinivasan
完美运行。 - Ángel B.

6

我有同样的问题。我找到了一个有效的解决方案。

将以下内容放入您的UIPageViewControllerviewDidLoad函数中。

if let myView = view?.subviews.first as? UIScrollView {
    myView.canCancelContentTouches = false
}

PageViewControllers有一个自动生成的子视图来处理手势。我们可以防止这些子视图取消内容触摸。表格视图将能够捕获删除按钮的滑动,同时解释未能满足表格视图手势要求的滑动为页面滑动。在你按住并滑动或"积极地"滑动时,删除按钮将显示。


我把这段代码添加到了我的UIPageViewController的viewDidLoad方法中,结果有时候在tableView上会显示出滑动删除的选项,但大部分时间它只是切换到下一页... - raklos
1
谢谢Carter。我已经尝试了你的解决方案。然而,有时pageViewController仍会从子tableView中夺取水平滑动操作。我该如何确保当用户在子tableView上滑动时,始终优先考虑该表视图获取手势? - David Liu

0
如果你的UIPageViewController中第一个或最后一个viewController内有tableView,也许可以先禁用UIPageViewController末尾处的弹跳,然后再实现 这个解决方案
如果不知道如何禁用UIPageViewController的弹跳效果,请查看我的答案这里

0

我已经找到了一个可行的解决方案,通过将UIScrollViewpanGestureRecognizerdelegate重新分配给我的类,并在检测到从右到左的手势时忽略原始代理。 我使用方法交换实现了这一点。

请检查我的示例项目:https://github.com/kambala-decapitator/SwipeToDeleteInsidePageVC


0

你也可以在tableView本身上将delaysContentTouches设置为false。这个解决方案对我的集合视图中的UISlider元素有效。

请参见下面的Swift 4.0代码:

yourTableView.delaysContentTouches = false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接