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が非インタラクティブに実行されます。つまり、スケーリングは、3% が却下されたときに 3% の完了を維持するのではなく、通常は却下するのにかかる約 300 ミリ秒で発生します。

これは、GitHub で入手できるサンプル プロジェクトで確認できます。この問題のビデオは YouTube にあります。

次のアプローチを試しましたが、すべて同じ結果が得られます。

  • 上記の並列アニメーション。
  • UIViewControllerAnimatedTransitioning でのアニメーション化。
  • CABasicAnimation を使用して、コンテナー ビューのレイヤーのタイミングを手動で調整します。
4

2 に答える 2

4

問題は、それpresentingViewControllerがプレゼンテーションの の子孫ではないことでしたcontainerView。 ゼロに設定し、完了率を反映するように設定することでUIPercentDrivenInteractiveTransition機能します。問題のビューは階層の一部ではないため、速度は 1 のままで、通常どおり完了しました。containerView.layer.speedcontainerView.layer.timeOffset

これは、次のドキュメントに明示的に記載されていますanimateAlongsideTransition(_:,completion:)

このメソッドを使用して、アニメーター オブジェクト自体によって処理されないアニメーションを実行します。指定するすべてのアニメーションは、アニメーション コンテキストのコンテナー ビュー (またはその子孫の 1 つ) 内で発生する必要があります。コンテナ ビューを取得するには、コンテキスト オブジェクトの 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())
    })
}

ヘッダーのそのメソッドに関するコメントは、ドキュメントよりもはるかに直接的です。

// この代替 API は、ビューがコンテナー ビューの子孫ではなく、このアニメーションを UIPercentDrivenInteractiveTransition インタラクション コントローラーによって駆動する必要がある場合に必要です。

于 2016-04-21T16:14:26.860 に答える