Flip between two ViewControllers under the same NavigationController
Asked Answered
F

6

14

I had referred to this question and several question about view / view controller transition but still couldn't find an satisfied answer. Most of the solutions suggest to flip the views instead of view controllers. However, the two view controllers in my app have totally different operation and implementation logic, thus I'm avoiding to mix them together.

In my app, I have a modal view controller FrontViewController which is embedded in a NavigationController. After pushing one button on the view, the modal view controller should flip to BackViewController, and vice versa. I had ever tried the following in FrontViewController:

let navi = UINavigationController(rootViewController: backController)
navi.modalPresentationStyle = .CurrentContext
navi.modalTransitionStyle = .FlipHorizontal
self.presentViewController(backController, animated: true, completion: nil)

This works almost as what I want, except that it flips navigation bar as well. Moreover, if I dismiss the modal view, only the view controller on the top of the stack is dismissed, while I failed to get the right parent/presenting controller to dismiss all the other stack controllers in the modal view.

Thus I further tried to prevent viewcontroller stack and use transitionFromViewController in FrontViewController using the same navigation controller instead:

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)
}))

Then I got this run time error on executiing: Parent view controller is using legacy containment in call to -[UIViewController transitionFromViewController:toViewController: duration:options:animations:completion:]

So, how to transition between two view controllers while preventing them remain in the view controller stack?

Fortyfour answered 29/12, 2015 at 4:13 Comment(0)
H
20

You can add custom transition to the navigation controllers layer just before pushing the view controller.

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)

Note that the animated parameter should be false. Otherwise the default sliding animation will takes place

Herbartian answered 29/12, 2015 at 4:59 Comment(1)
This solution is simple and works like a charm! Thanks a lot!Fortyfour
L
11

See this demo for you :

Swift code for 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()

FlipViewController Animation

Output :

enter image description here

Litchfield answered 29/12, 2015 at 5:6 Comment(1)
sorry i meant in nav back button?Cellulous
S
6

Swift 5 - I personally like using this approach.

//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)
}
Schuler answered 27/2, 2020 at 14:8 Comment(0)
T
4

Swift 3.0 + Should use UIView's animation block, make sure your viewController in UINavigationController stacks:

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)
Tow answered 8/10, 2017 at 10:37 Comment(0)
W
2
     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)

    }
Worsted answered 25/11, 2017 at 13:25 Comment(0)
T
0

here Student is NSObjectClass

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()

        }
    }
Tearing answered 14/10, 2017 at 17:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.