I've just started programming in Swift, what I'm trying to accomplish is a very simple app with an initial UIViewController
, a UIPageViewController
that shows some book pages and a destination UIViewController
.
My approach so far is this:
UIViewController1
is loaded and has a showPage button that simply shows UIPageViewController
present(walkthroughViewController, animated: true, completion: nil)
When the user reaches the last page of the UIPageViewController
, I show the destination UIViewController2
, addressing the segue from the start UIViewController
override func onUIPageViewControllerRigthClosing(){
let pvc = self.presentingViewController as! StartPageController
dismiss(animated: true){
pvc.performSegue(withIdentifier: "startTest", sender: nil)
}
}
Everything works correctly, but the problem is that when UIPageViewController
is dismissed, the Starting UIViewController
is showed and then is showed the second with the animated segue.
What I am trying to achieve is to directly display the target UiViewController
to the user on the dismiss of the UIPageViewController
, without showing the transition with animation from start View to the destination View.
I'm completely wrong approaching or there is a way to do the segue before dismissing the UIPageViewController
?
Here I created a gif that shows the problem, when I close the UIPageViewController
I see the previous view in transition: GIF demo
I suggest you using this approach: for these screens transitions use childViewControllers instead of presenting them modally and dismissing with default UIKit functions.
You have problems with naming, so let me rename view controllers. Say, you have:
RootViewController
(the first screen, user see after
app launch). OnboardingViewController
(your pageViewController or other container)AppContentViewController
(actually app main screen)I suggest you using this approach: for screens transitions on RootViewController
use childViewControllers instead of presenting them modally and dismissing with default UIKit functions.
Here is sample code that works with childViewControllers
extension UIViewController {
func displayChildController(_ content: UIViewController, duration: TimeInterval = 0.4, animation: (() -> ())? = nil, completion: @escaping () -> () = {}) {
content.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
view.addSubview(content.view)
addChildViewController(content)
UIView.animate(withDuration: animation != nil ? duration : 0, animations: {() -> Void in
animation?()
}, completion: {(_ finished: Bool) -> Void in
content.didMove(toParentViewController: self)
completion()
})
}
func hideChildController(_ content: UIViewController, duration: TimeInterval = 0.4, animation: (() -> ())? = nil, completion: @escaping () -> () = {}) {
UIView.animate(withDuration: animation != nil ? duration : 0, animations: {() -> Void in
animation?()
}, completion: {(_ finished: Bool) -> Void in
content.willMove(toParentViewController: nil)
content.view.removeFromSuperview()
content.removeFromParentViewController()
completion()
})
}
}
Here is "algorithm":
I assuming that you are using single storyboard with all these view controllers.
On OnBoardingViewController
declare onDoneCallback
:
class OnBoardingViewController: ... {
var onDoneCallback = {}
...
}
On RootViewController
when you need present OnboardingViewController
:
func presentOnboardingScreen() {
let onboardingVC = self.storyboard?.instantiateViewController(withIdentifier: "OnboardingViewController") as! OnboardingViewController
onboardingVC.transform = .init(translationX: 0, y: self.view.frame.height)
onboardingVC.onDoneCallback = {
self.presentAppContentAfterOnboarding() // see below
}
displayChildController(onboardingVC, duration: 0.3, animation: {
vc.view.transform = .identity
})
}
When you need call onDoneCallback
closure on OnboardingViewController
presentAppContentAfterOnboarding
method on RootViewController
could look like:
func presentAppContentAfterOnboarding() {
let onboardingVC = self.childViewControllers.last as! OnboardingViewController
let appContentVC = self.storyboard?.instantiateViewController(withIdentifier: "AppContentViewController") as! AppContentViewController
displayChildController(appContentVC)
view.insertSubview(appContentVC.view, belowSubview: onboardingVC.view)
hideChildController(childVC, duration: duration, animation: {
onboardingVC.view.transform = .init(translationX: 0, y: self.view.frame.height)
})
}
Note. Don't forget to set Storyboard ID of OnboardingViewController
and AppContentViewController
in your storyboard.
Here is the sample project