交互式关闭模态时出现故障

Glitch when interactively dismissing modal

在通过 UIPercentDrivenInteractiveTransition 实现模态视图控制器的交互式关闭(向下拖动模态应该关闭它)时,我们已经 运行 解决了这个问题。

设置:

  1. 设置 UIViewController 嵌入 UINavigationController 至少有一个按钮 UINavigationBar
  2. 模态呈现 UINavigationController 中嵌入的另一个 UIViewController,至少有一个 UINavigationBar
  3. 中的按钮
  4. 设置 UIPanGestureRecognizer 模态呈现 UINavigationController 以驱动 UIPercentDrivenInteractiveTransition
  5. 将模态向下拖动 "holding" 按 UINavigationBar
  6. 上的点

问题:

可以从 github repo 下载最小示例。

有人遇到过这样的问题吗?有什么解决方法吗?我们的设置有什么缺陷吗?

更新

问题已在 运行 上面的 iPhone 5 模拟器上用 iOS 9.3OSX 10.11.4、Xcode [=26= 编译的项目进行了模拟].

更新 2

进一步调查表明,该问题可能不在动画中:出于某种原因,在给定设置中 pan.translationInView(view) 返回意外值,导致动画跳转。

部分解决方法

根据 Vladimir 的想法,我们通过覆盖 UINavigationBarhitTest 方法部分解决了这个问题:

class DraggableNavigationBar: UINavigationBar {

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        guard let view = super.hitTest(point, withEvent: event) else { return nil }

        if view is UIControl || pointIsInsideNavigationButton(point) {
            return view
        } else {
            return nil
        }
    }

    private func pointIsInsideNavigationButton(point: CGPoint) -> Bool {
        return subviews
            .filter { [=10=].frame.contains(point) }
            .filter { String([=10=].dynamicType) == "UINavigationItemButtonView" }
            .isEmpty == false
    }
}

我没有完整的解决方案,但我能够将故障减少一定程度。我可以在 iPhone 5 秒内使用 iOS 9.3.2 [通过按住导航栏向下拖动屏幕]

重现此问题

问题似乎出在 DismissalAnimatorUIView.animateWithDuration 块中。通过注释掉延迟和选项,即将它们保持为默认值,您可以减少视图的跳跃。您也可以尝试检查获得最小跳跃的不同 UIViewAnimationOptions

    UIView.animateWithDuration(0.3,
        animations: {
            dismissedView.frame = finalFrame
        },
        completion: { _ in
            let didComplete = !transitionContext.transitionWasCancelled()
            transitionContext.completeTransition(didComplete)
        }
   )

a question 似乎正在处理您面临的相同问题。并且响应从禁用自动布局,将 layoutIfNeeded 放入动画块 [尝试过,都没有用]。

非常有趣的故障。几天前我找到了这个问题的部分解决方案,但由于没有人找到完整的解决方案,我将 post 这个,也许会有帮助。

如果您覆盖 UINavigationBarhitTest 方法,您可以通过按住 UINavigationBar:

拖动模式来解决此问题
extension UINavigationBar {

    override public func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

        guard let view = super.hitTest(point, withEvent: event) else { return nil }

        if view.isKindOfClass(UIControl) {
            return super.hitTest(point, withEvent: event)
        } else {
            return nil
        }
    }
}

不幸的是,如果您在 UINavigationBar 上按住 UIBarButtonItem 拖动模态框,故障仍然存在。

您也可以尝试另一种方法。

如您所见,pan.translationInView(view) returns 不正确的值导致动画跳转。 在拖动过程中,您需要将此值与模态视图的 y 坐标进行比较。您可以通过检查模态视图控制器的表示层来获取此值:

...

let translation = pan.translationInView(view)

if let layer = view.layer.presentationLayer() {
            print(layer.frame.origin.y)
}

...

你可以看到当pan.translationInView(view)开始显示错误值时,layer.frame.origin.y在那一刻仍然是正确的。您可以比较这两个值并在值不正确时找到模式,并通过向 translation.y 值添加几个点来更改它以更正。