3

我正在研究类似于 Mail.app 的打开草稿行为的 UIPresentationController 子类。当呈现视图控制器时,它不会一直到顶部,呈现的视图控制器会缩小,就好像它正在回落一样。

它的基本要点如下:

class CustomPresentationController : UIPresentationController {

    // Create a 40pt space above the view.
    override func frameOfPresentedViewInContainerView() -> CGRect {
        let frame = super.frameOfPresentedViewInContainerView()
        let insets = UIEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)
        return UIEdgeInsetsInsetRect(frame, insets)
    }

    // Scale down when expanded is true, otherwise identity.
    private func setScale(expanded expanded: Bool) {

        if expanded {
            let fromMeasurement = presentingViewController.view.bounds.width
            let fromScale = (fromMeasurement - 30) / fromMeasurement
            presentingViewController.view.transform = CGAffineTransformMakeScale(fromScale, fromScale)
        } else {
            presentingViewController.view.transform = CGAffineTransformIdentity
        }

    }

    // Scale down alongside the presentation.
    override func presentationTransitionWillBegin() {
        presentingViewController.transitionCoordinator()?.animateAlongsideTransition({ context in
            self.setScale(expanded: true)
        }, completion: { context in
            self.setScale(expanded: !context.isCancelled())
        })
    }

    // Scale up alongside the dismissal.
    override func dismissalTransitionWillBegin() {
        presentingViewController.transitionCoordinator()?.animateAlongsideTransition({ context in
            self.setScale(expanded: false)
        }, completion: { context in
            self.setScale(expanded: context.isCancelled())
        })
    }

    // Fix the scaled view's frame on orientation change.
    override func containerViewWillLayoutSubviews() {
        super.containerViewWillLayoutSubviews()
        guard let bounds = containerView?.bounds else { return }
        presentingViewController.view.bounds = bounds
    }
}

这适用于非交互式演示或解雇。但是,在执行交互式解除时,所有动画都以presentingViewController.view非交互方式运行。也就是说,缩放将在通常需要关闭的约 300 毫秒内发生,而不是在 3% 关闭时保持在 3% 完成。

您可以在 GitHub 上的示例项目中看到这一点。并且该问题的视频在 YouTube 上

我尝试了以下方法,但它们都产生相同的结果:

  • 如上所示的并行动画。
  • UIViewControllerAnimatedTransitioning 中的动画。
  • 使用 CABasicAnimation 手动调整容器视图层的时间。
4

2 回答 2

4

问题是它presentingViewController不是演示文稿的containerView. UIPercentDrivenInteractiveTransition通过设置containerView.layer.speed为零并设置containerView.layer.timeOffset以反映完成百分比来工作。由于有问题的视图不是层次结构的一部分,因此它的速度保持在 1 并且正常完成。

这在以下文档中明确说明animateAlongsideTransition(_:,completion:)

使用此方法可以执行不由 animator 对象本身处理的动画。您指定的所有动画都必须出现在动画上下文的容器视图(或其后代之一)内。使用上下文对象的 containerView 属性来获取容器视图。要在不从容器视图下降的视图中执行动画,请改用该animateAlongsideTransitionInView:animation:completion:方法。

如文档所示,切换到animateAlongsideTransitionInView(_:,animation:,completion:)解决问题:

// Scale up alongside the dismissal.
override func dismissalTransitionWillBegin() {
    presentingViewController.transitionCoordinator()?.animateAlongsideTransitionInView(presentingViewController.view, animation: { context in
        self.setScale(expanded: false)
    }, completion: { context in
        self.setScale(expanded: context.isCancelled())
    })
}

标题中对该方法的评论比文档更直接:

// 如果视图不是容器视图的后代并且您需要此动画由 UIPercentDrivenInteractiveTransition 交互控制器驱动,则需要此替代 API。

于 2016-04-21T16:14:26.860 回答
-1

感谢您的回答,在 iOS 9.x 和 10.x 设备上遇到了这个问题 - 使用animateAlongsideTransition(in:animation:completion:) 完成了这项工作。

有趣的是,在 iOS 11.x 上animate(alongsideTransition:completion:)也可以正常工作来展示ViewController(无需使用animateAlongsideTransition(in:animation:completion:)) - 看起来 Apple 在最新的 iOS 中提升了一些东西。

于 2017-12-07T07:23:38.057 回答