在同一个 NavigationController 下的两个 ViewController 之间翻转
Flip between two ViewControllers under the same NavigationController
我参考了 this question 和几个关于视图/视图控制器转换的问题,但仍然找不到满意的答案。大多数解决方案建议翻转视图而不是视图控制器。但是,我的应用程序中的两个视图控制器具有完全不同的操作和实现逻辑,因此我避免将它们混合在一起。
在我的应用程序中,我有一个模态视图控制器 FrontViewController
,它嵌入在 NavigationController 中。在视图上按下一个按钮后,模态视图控制器应翻转为 BackViewController
,反之亦然。我曾在 FrontViewController
中尝试过以下操作:
let navi = UINavigationController(rootViewController: backController)
navi.modalPresentationStyle = .CurrentContext
navi.modalTransitionStyle = .FlipHorizontal
self.presentViewController(backController, animated: true, completion: nil)
这几乎和我想要的一样,只是它还翻转了导航栏。此外,如果我关闭模态视图,只会关闭堆栈顶部的视图控制器,而我无法获得正确的 parent/presenting 控制器来关闭模态视图中的所有其他堆栈控制器。
因此,我进一步尝试使用相同的导航控制器来防止 viewcontroller 堆栈并在 FrontViewController
中使用 transitionFromViewController
:
self.navigationController!.addChildViewController(backController)
self.willMoveToParentViewController(nil)
self.navigationController!.transitionFromViewController(self, toViewController: backViewController, duration: 1, options: .TransitionFlipFromLeft, animations: {}, completion: ({Bool -> Void in
self.removeFromParentController()
c.didMoveToParentViewController(self)
}))
然后我在执行时得到这个 运行 时间错误:Parent view controller is using legacy containment in call to -[UIViewController transitionFromViewController:toViewController: duration:options:animations:completion:]
那么,如何在两个视图控制器之间进行转换,同时防止它们保留在视图控制器堆栈中?
您可以在推送视图控制器之前向导航控制器层添加自定义过渡。
let transition = CATransition()
transition.duration = 0.3
transition.type = "flip"
transition.subtype = kCATransitionFromLeft
self.navigationController?.view.layer.addAnimation(transition, forKey: kCATransition)
self.navigationController?.pushViewController(viewController!, animated: false)
注意animated
参数应该是false
。否则会出现默认的滑动动画
为您观看此演示:
Swift flipAnimation 代码:
let mainStory = UIStoryboard(name: "Main", bundle: nil)
let search = mainStory.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController
UIView.beginAnimations("animation", context: nil)
UIView.setAnimationDuration(1.0)
self.navigationController!.pushViewController(search, animated: false)
UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false)
UIView.commitAnimations()
输出:
Swift 3.0 +
应使用 UIView
的动画块,确保 viewController
在 UINavigationController
堆栈中:
let viewController = ViewController(nibName: "xxx", bundle: nil)
UIView.transition(with: self.navigationController!.view, duration: 1.0, options: .transitionFlipFromLeft, animations: {
self.navigationController?.pushViewController(viewController, animated: false)
}, completion: nil)
这里Student是NSObject类
var Arraydata:[Student] = []
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let arr = Arraydata[indexPath.row]
if indexPath.row == arr
{
let story = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
UIView.beginAnimations("", context: nil)
UIView.setAnimationDuration(1.0)
UIView.setAnimationCurve(UIViewAnimationCurve.easeInOut)
UIView.setAnimationTransition(UIViewAnimationTransition.flipFromRight, for: (self.navigationController?.view)!, cache: false)
self.navigationController?.pushViewController(story, animated: true)
UIView.commitAnimations()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
// for back button
changeTransition()
}
//btnMap.addTarget(self, action: #selector(searchHotelsResultVC.goToMap), for: .touchUpInside)
//btnMap.addTarget(self, action: #selector(MapViewController.backToList), for: .touchUpInside)
func goToMap() {
// for pushing
changeTransition()
navigationController?.pushViewController(settingsVC, animated: false)
}
func backToList() {
// for dismiss
changeTransition()
navigationController?.popViewController(animated: false)
dismiss(animated: true, completion: nil)
}
func changeTransition() {
let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
//transition.type = kCATransitionPush
transition.type = "flip"
transition.subtype = kCATransitionFromLeft
navigationController?.view.layer.add(transition, forKey: kCATransition)
}
Swift 5 - 我个人喜欢使用这种方法。
//PUSH
let secondVC = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "secondVC") as! SecondVC
self.navigationController?.pushViewController(secondVC, animated: false)
UIView.transition(from: self.view, to: secondVC.view, duration: 0.85, options: [.transitionFlipFromLeft])
//POP
let firstVC = self.navigationController?.viewControllers[(self.navigationController?.viewControllers.count ?? 2) - 2] as? FirstVC
if let firstView = firstVC?.view{
self.navigationController?.popViewController(animated: false)
UIView.transition(from: self.view, to: firstView, duration: 0.85, options: [.transitionFlipFromRight])
} else {
self.navigationController?.popViewController(animated: true)
}
我参考了 this question 和几个关于视图/视图控制器转换的问题,但仍然找不到满意的答案。大多数解决方案建议翻转视图而不是视图控制器。但是,我的应用程序中的两个视图控制器具有完全不同的操作和实现逻辑,因此我避免将它们混合在一起。
在我的应用程序中,我有一个模态视图控制器 FrontViewController
,它嵌入在 NavigationController 中。在视图上按下一个按钮后,模态视图控制器应翻转为 BackViewController
,反之亦然。我曾在 FrontViewController
中尝试过以下操作:
let navi = UINavigationController(rootViewController: backController)
navi.modalPresentationStyle = .CurrentContext
navi.modalTransitionStyle = .FlipHorizontal
self.presentViewController(backController, animated: true, completion: nil)
这几乎和我想要的一样,只是它还翻转了导航栏。此外,如果我关闭模态视图,只会关闭堆栈顶部的视图控制器,而我无法获得正确的 parent/presenting 控制器来关闭模态视图中的所有其他堆栈控制器。
因此,我进一步尝试使用相同的导航控制器来防止 viewcontroller 堆栈并在 FrontViewController
中使用 transitionFromViewController
:
self.navigationController!.addChildViewController(backController)
self.willMoveToParentViewController(nil)
self.navigationController!.transitionFromViewController(self, toViewController: backViewController, duration: 1, options: .TransitionFlipFromLeft, animations: {}, completion: ({Bool -> Void in
self.removeFromParentController()
c.didMoveToParentViewController(self)
}))
然后我在执行时得到这个 运行 时间错误:Parent view controller is using legacy containment in call to -[UIViewController transitionFromViewController:toViewController: duration:options:animations:completion:]
那么,如何在两个视图控制器之间进行转换,同时防止它们保留在视图控制器堆栈中?
您可以在推送视图控制器之前向导航控制器层添加自定义过渡。
let transition = CATransition()
transition.duration = 0.3
transition.type = "flip"
transition.subtype = kCATransitionFromLeft
self.navigationController?.view.layer.addAnimation(transition, forKey: kCATransition)
self.navigationController?.pushViewController(viewController!, animated: false)
注意animated
参数应该是false
。否则会出现默认的滑动动画
为您观看此演示:
Swift flipAnimation 代码:
let mainStory = UIStoryboard(name: "Main", bundle: nil)
let search = mainStory.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController
UIView.beginAnimations("animation", context: nil)
UIView.setAnimationDuration(1.0)
self.navigationController!.pushViewController(search, animated: false)
UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false)
UIView.commitAnimations()
输出:
Swift 3.0 +
应使用 UIView
的动画块,确保 viewController
在 UINavigationController
堆栈中:
let viewController = ViewController(nibName: "xxx", bundle: nil)
UIView.transition(with: self.navigationController!.view, duration: 1.0, options: .transitionFlipFromLeft, animations: {
self.navigationController?.pushViewController(viewController, animated: false)
}, completion: nil)
这里Student是NSObject类
var Arraydata:[Student] = []
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let arr = Arraydata[indexPath.row]
if indexPath.row == arr
{
let story = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
UIView.beginAnimations("", context: nil)
UIView.setAnimationDuration(1.0)
UIView.setAnimationCurve(UIViewAnimationCurve.easeInOut)
UIView.setAnimationTransition(UIViewAnimationTransition.flipFromRight, for: (self.navigationController?.view)!, cache: false)
self.navigationController?.pushViewController(story, animated: true)
UIView.commitAnimations()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
// for back button
changeTransition()
}
//btnMap.addTarget(self, action: #selector(searchHotelsResultVC.goToMap), for: .touchUpInside)
//btnMap.addTarget(self, action: #selector(MapViewController.backToList), for: .touchUpInside)
func goToMap() {
// for pushing
changeTransition()
navigationController?.pushViewController(settingsVC, animated: false)
}
func backToList() {
// for dismiss
changeTransition()
navigationController?.popViewController(animated: false)
dismiss(animated: true, completion: nil)
}
func changeTransition() {
let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
//transition.type = kCATransitionPush
transition.type = "flip"
transition.subtype = kCATransitionFromLeft
navigationController?.view.layer.add(transition, forKey: kCATransition)
}
Swift 5 - 我个人喜欢使用这种方法。
//PUSH
let secondVC = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "secondVC") as! SecondVC
self.navigationController?.pushViewController(secondVC, animated: false)
UIView.transition(from: self.view, to: secondVC.view, duration: 0.85, options: [.transitionFlipFromLeft])
//POP
let firstVC = self.navigationController?.viewControllers[(self.navigationController?.viewControllers.count ?? 2) - 2] as? FirstVC
if let firstView = firstVC?.view{
self.navigationController?.popViewController(animated: false)
UIView.transition(from: self.view, to: firstView, duration: 0.85, options: [.transitionFlipFromRight])
} else {
self.navigationController?.popViewController(animated: true)
}