I have created top design in MainSegPageViewController
in storyboard like this and i have 2 other viewcontrollers as well. now i need same upper design in my all three viewcontrollers. so how to show that 2 viewcontrollers in this wight area only? is that possible? if yes please explain me
code: with this code initially i am getting ViewController1
why? how to show MainSegPageViewController
initially? and how to show ViewController1
in white area when i click STEP 2
. please guide me.
import UIKit
class MainSegPageViewController: UIPageViewController {
private(set) lazy var subViewcontrollers: [UIViewController] = {
return [
UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "ViewController1"),
UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "ViewController2")
]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
modalPresentationStyle = .formSheet
if let firstViewController = subViewcontrollers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
setUpNavigationBar(with: "Pagination vc", isBackNeed: true)
self.navigationController?.navigationBar.isHidden = false
}
}
// MARK: UIPageViewControllerDataSource
extension MainSegPageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = subViewcontrollers.firstIndex(of:viewController)
else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard subViewcontrollers.count > previousIndex else {
return nil
}
if previousIndex == 2 {
return nil
}
return subViewcontrollers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = subViewcontrollers.firstIndex(of:viewController)
else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = subViewcontrollers.count
guard orderedViewControllersCount != nextIndex else {
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
if nextIndex == 3 {
return nil
}
return subViewcontrollers[nextIndex]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return subViewcontrollers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = subViewcontrollers.firstIndex(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
And i am reaching to MainSegPageViewController
through side menu like this
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "MainSegPageViewController") as? MainSegPageViewController
self.navigationController?.pushViewController(vc!, animated: true)
o/p: with the code initially i am getting ViewController1
screen but i need MainSegPageViewController
to show first.
To achieve your layout, one approach is to create a "Profile" view controller containing your "Welcome to Profile" label and UISegmentedControl
, and then embed the UIPageViewController
in a Container View.
So, since your post says you are pushing to this layout in a navigation controller, we start with this in Storyboard:
The button will be connected to this code:
@IBAction func profileButtonTapped(_ sender: Any) {
if let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "ProfileViewController") as? ProfileViewController {
navigationController?.pushViewController(vc, animated: true)
}
}
Then, layout ProfileViewController
in Storyboard like this:
The large view is a UIContainerView
:
and we've embedded a typical UIPageViewController
in it.
Assuming you have working code for your page view controller, this can be run and you'll be able to swipe back and forth through the pages.
Next, we need to add some code so tapping on a Segment will change the page.
We can setup the ProfileViewController
like this:
class ProfileViewController: UIViewController {
@IBOutlet var stepsSegCtrl: UISegmentedControl!
// we will want a reference to the embedded MainSegPageViewController
// so we can tell it to change page when the segmented control is tapped
var mainSegPageVC: MainSegPageViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Pagination VC"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// this is called because we've embedded the controller in a container view
if let pgVC = segue.destination as? MainSegPageViewController {
// save reference so we can access it when the segmented control is tapped
mainSegPageVC = pgVC
}
}
@IBAction func segCtrlChanged(_ sender: UISegmentedControl) {
// safely unwrap
if let pgVC = mainSegPageVC {
pgVC.gotoPage(pg: sender.selectedSegmentIndex)
}
}
}
When embedding a controller in a Container View, we automatically get a segue
where we can grab a reference to the embedded page view controller.
If we add this func to our MainSegPageViewController
class:
public func gotoPage(pg: Int) {
let curPg = presentationIndex(for: self)
if pg > curPg {
setViewControllers([subViewcontrollers[pg]],
direction: .forward,
animated: true,
completion: nil)
} else {
setViewControllers([subViewcontrollers[pg]],
direction: .reverse,
animated: true,
completion: nil)
}
}
we can now change the page from the segmented control.
Finally, we need to add a closure
so the page view controller can inform the "parent" controller when the user drags to a new page.
So, in MainSegPageViewController
class, we add this property:
var pageChanged: ((Int) -> ())?
and implement:
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
// user dragged to a new page, so inform the parent controller
pageChanged?(presentationIndex(for: self))
}
We set that closure back in prepare(for segue:...)
back in ProfileViewController
:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// this is called because we've embedded the controller in a container view
if let pgVC = segue.destination as? MainSegPageViewController {
// set the closure
pgVC.pageChanged = { [weak self] pg in
guard let self = self else { return }
// user dragged to a new page, so update the segmented control
self.stepsSegCtrl.selectedSegmentIndex = pg
}
// save reference so we can access it when the segmented control is tapped
mainSegPageVC = pgVC
}
}
I've put up a complete project here - https://github.com/DonMag/ContainerPageViewController so you can inspect the code and Storyboard -- and see it in action -- while building your own.