好的,除了我的问题中的 UINavigationController 子类之外,这是我必须做的。我必须使用UIViewControllerAnimatedTransitioning delegate 来实现我想要的。
我创建了两个子类NSObject 并符合上述协议。
第一个是 present 转换-
import UIKit
class ComposePresentTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: .from)!
let toViewController = transitionContext.viewController(forKey: .to)!
let containerView = transitionContext.containerView
let screenBounds = UIScreen.main.bounds
let topOffset: CGFloat = UIApplication.shared.statusBarFrame.height
var finalFrame = transitionContext.finalFrame(for: toViewController)
finalFrame.origin.y += topOffset
finalFrame.size.height -= topOffset
toViewController.view.frame = CGRect(x: 0.0, y: screenBounds.size.height,
width: finalFrame.size.width,
height: finalFrame.size.height)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 1.0, options: .curveEaseOut, animations: {
toViewController.view.frame = finalFrame
fromViewController.view.alpha = 0.3
}) { (finished) in
transitionContext.completeTransition(finished)
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: .curveEaseOut,
animations: {
}) { (finished) in
}
}
}
dismiss 转换第二-
import UIKit
class ComposeDismissTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: .from)!
let toViewController = transitionContext.viewController(forKey: .to)!
let screenBounds = UIScreen.main.bounds
var finalFrame = fromViewController.view.frame
finalFrame.origin.y = screenBounds.size.height
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, options: .curveEaseIn,
animations: {
fromViewController.view.frame = finalFrame
toViewController.view.alpha = 1.0
}) { (finished) in
fromViewController.view.removeFromSuperview()
transitionContext.completeTransition(finished)
}
}
}
然后,从将要呈现此视图控制器的视图控制器中,该视图控制器需要符合UIViewControllerTransitioningDelegate 协议并实现animationController:forPresented 和animationController:forDismissed 这样的方法-
extension PresentingVC: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ComposePresentTransitionController()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ComposeDismissTransitionController()
}
}
完成后,PresentingVC 需要将UIViewControllerTransitioningDelegate 设置为self,这在prepareForSegue 中完成-
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
segue.destination.transitioningDelegate = self
}
如果您像我一样讨厌 segue,那么您也可以通过代码中的按钮的 IBAction 执行此操作。
@IBAction func composeTap(_ sender: Any) {
let composeVC = self.storyboard?.instantiateViewController(withIdentifier: "compose") as! ComposeNavigController //this should be the navigation controller itself not the embedded viewcontroller
composeVC.transitioningDelegate = self
self.present(composeVC, animated: true, completion: nil)
}
就是这样...现在,只要点击按钮以模态显示撰写屏幕,它就会以我想要的效果模态动画。我添加了一点弹跳效果只是为了好玩:)