检测iOS 3D Touch的浏览结束(不弹出)。

7
我刚开始为我的应用程序添加基本的3D Touch功能,第一次尝试添加似乎很顺利。
然而,我在想是否有一种方法可以检测到峰值已经完成,而没有进入弹出窗口。
UIViewControllerPreviewingDelegate方法可以告诉您请求峰值或弹出窗口,但我没有看到一种方法可以告诉您峰值已经结束并且没有进入弹出窗口。
峰值视图控制器是否有一种方式知道它目前处于峰值并且正在消失,因为我猜这就足够了。基本上,我有一个转场通常会在进入视图时创建一些东西,如果我窥视它,则需要撤消用户选择仅结束窥视而不弹出的内容。目前,我似乎无法找到检测此情况以执行所需清理的好方法。
干杯

我有同样的问题。希望苹果能够为UIViewControllerPreviewingDelegate添加另一个函数。 - HMHero
1
暂时,您可以设置一个标志来告诉您vc是否正在查看,并在previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint)中进行设置,在previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController)中重置为false。 - HMHero
谢谢,我有类似的想法,甚至为我认为在被窥视的视图上接收的所有窥视操作制定了一个协议。不幸的是,在我们被调用弹出视图之前,预览视图就会消失,因此我仍然无法区分窥视消失和窥视转换为弹出。 - jimbobuk
在 viewWillDisappear 中,我们会在正确弹出之前调用。 - jimbobuk
@HMHero 只有在你跟进并弹出时才有效。如果你窥视而不弹出,那么标志永远不会正确重置。 - R.P. Carson
2个回答

15

当您使用registerForPreviewingWithDelegate()注册预览时,会返回一个符合UIViewControllerPreviewing协议的上下文。该协议包含一个引用被用于浏览/弹出的手势识别器,称为previewingGestureRecognizerForFailureRelationship。它被设计用来在其他手势识别器可能同时被识别时使用,但您也可以将自己的对象添加为目标来观察更改。

现在,在进行浏览时,此手势识别器的状态将为.Changed。当您在不弹出的情况下释放时,状态将变为.Ended。当您弹出时,状态将变为.Cancelled(我实际上期望这是相反的,但至少我们可以区分)。重要的是,在调用您查看的视图控制器的viewDidDisappear之前,此状态会发生变化,因此您可以及时调整标志。


谢谢,听起来很有前途。我会看一下,并在它能够运行后接受答案!谢谢! - jimbobuk
1
这个程序可以运行,但如果用户滚动到previewActionItems,即使我们仍然处于预览模式,它也会显示.Ended。 - CiNN
3
我用 Swift 写了样例代码:ShingoFukuyama/3DTouchDetectStateOfPeekPopCancel - Shingo Fukuyama
6
看起来在iOS 11上情况有所改变。在手势仍在进行中时,我立即得到了“.ended”状态。 - Tim
@ShingoFukuyama:你的代码中不需要使用KVO。你可以直接使用目标动作(target action)。 - clemens
1
@Tim:你说得对,Tim。看起来iOS11完全破坏了这个功能。我也会立即收到".ended"的信号,这破坏了我之前根据clemens原始答案编写的代码。可惜现在我们无法知道视图是被peek和pop出来的,还是只是peek然后离开了。我猜我们一直都在滥用这个手势识别器,因为似乎没有其他的识别器参与,它就会立即结束。由于这个问题,我将不得不在我的应用程序中取消对peeking的支持。 - jimbobuk

1

我在我的应用程序中遇到了同样的问题,我需要知道一个视图控制器何时开始和停止被窥视,并得出以下结论。

为了监控预览的生命周期,您可以跟踪被窥视的视图控制器的生命周期,从在 previewingContext(_ previewingContext:UIViewControllerPreviewing,viewControllerForLocation location:CGPoint) -> UIViewController 中创建视图控制器开始,到其 viewDidDisappear() 结束。

我在被窥视的视图控制器 PeekingViewController 中创建了回调处理程序:

var viewDidDisappearHandler:(() - >())?= nil

并将其放置在 PeekingViewControllerviewDidDisappear 中:

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    viewDidDisappearHandler?()
}

OriginalViewcontroller中,我们正在查看PeekingViewController,需要保留对被查看视图控制器实例的弱引用,如下所示:weak var peekingViewController: PeekingViewController?
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
    self.peekingViewController = PeekingViewController()
    return peekingViewController
}

然后,您可以通过在peekingViewController实例中填写didSet来观察对被窥视的视图控制器的弱引用的更改:
weak private var peekingViewController: PeekingViewController? {
    didSet {
        peekingViewController?.viewDidDisappearHandler = { [weak self] in
            self?.peekingViewController = nil
        }
        if peekingViewController == nil { // Peek ended
            handlePeekEnded()
        } else { // Peek began
            handlePeekBegan()
        }
    }
}

注意: 如果执行了峰值并取消,那么这个逻辑将被触发,但是如果执行了峰值,完成了segue,然后新呈现的PeekingViewController被弹出,也会触发此逻辑。
如果您需要关于取消峰值的逻辑仅在未完成峰值时触发,而不是在完全峰值和解除峰值之后触发,可以通过以下方式实现:
在OriginalViewController中包含一个新的布尔变量来跟踪视图控制器是否已经完全推送(OriginalViewController的viewDidDisappear将在完全推送时触发,但不会在峰值上触发),并在peekingViewController的didSet中检查该布尔变量,以确定是否应该采取任何操作,并在OriginalViewController的viewWillAppear中将peekingViewController设置为nil。

感谢您的详细回复,很抱歉我回复得这么晚。最近我没有经常在这个应用程序上工作,但最近遇到了一个崩溃问题,我发现这是由于iOS11中peek breakages引起的更多问题。不幸的是,您建议的工作流程对我的用例不起作用,因为我想在viewDidDisappear中执行不同的拆除行为,当peeked view在弹出后消失时与在peek后消失时不同。ViewDidDisappear在弹出之前和取消时调用peeked view,所以无法区分两者:( - jimbobuk

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