I have set up a CollectionViewController, that will create a CollectionView from a datasource. Now I want to use a PageViewController to swipe trough the CollectionViews but I don't know how I to create an array that contains the CollectionViews.
So far I only presented one of the CollectionViews the following way:
let CollectionVC = storyboard?.instantiateViewController(withIdentifier: "CollectionView") as! CollectionViewController
CollectionVC.itemStrings = matrixArray[0].makeString()
CollectionVC.columns = problem.columns
CollectionVC.rows = problem.rows
self.present(CollectionVC, animated: true, completion: nil)
And that worked just fine to present one View.
But now I need to create an array that can be read by the PageViewController to generate the PageView, how do I do that? Im not even sure about the format it needs to be for the PageViewController Datasource, as far as I can see from other tutorials, its supposed to be an array of UIViewController and not of Type UICollectionViewController as I planned to do, if thats the case, how can I make that work with the CollectionViews?
Any advice on how to create such an Array that can be used as datasource for the PageViewController?
For more Clarification
Here is a picture of how it is supposed to look at the end:
Every Screen is basically created by the CollectionViewController and the PageViewController is organize them and make it possible to Swipe trough these screens as indicated by the pagination.
//Edit:
Thanks Puneet Sharma for the answer. Im sure this is already very close to the solution I was looking for. But since I am a total self-taught noob in programming, Im not completely sure where to fit in all this information.
So far I have 2 Controllers, one ViewController:
class ViewController: UIViewController {
//Initialize Variables:
var matrixArray = Array<Matrix>()
var basicArray = Array<Array<Int>>()
var maxArray = Array<Double>()
var currentSolutionArray = Array<Array<Double>>()
var isOptimal = false
var isCyceling = false
var CollectionViewArray: [UICollectionView] = []
//The outlets are for later
@IBOutlet weak var myObjectionFunction: UITextField!
@IBOutlet weak var myConstraint1: UITextField!
@IBOutlet weak var myConstraint1RightSide: UITextField!
@IBOutlet weak var myConstraint2: UITextField!
@IBOutlet weak var myConstraint2RightSide: UITextField!
@IBOutlet weak var myConstraint3: UITextField!
@IBOutlet weak var myConstraint3RightSide: UITextField!
//StartButton
@IBAction func startButton(_ sender: Any) {
//Initializing Objection Function
let objFunction = [-40.0,-30.0,0.0,0.0,0.0,0.0]
// Initializing Nr. of Rows and Columns of the Matrix
let rows = 4
let columns = 6
// Initializing constraints
let constraint0 = [1.0,1.0,1.0,0.0,0.0,8.0]
let constraint1 = [2.0,1.0,0.0,1.0,0.0,12.0]
let constraint2 = [2.0,3.0,0.0,0.0,1.0,18.0]
//Initalize an Array containing all constraints
let constraints = [constraint0, constraint1, constraint2]
//Alternative for use of UserData -> Delete objFuntion, rows, columns, constraint0..2, constraintsArray
//let problem = getData().0
//let constraints = getData.1
let problem = LinearProblem.createLP(rows: rows, columns: columns, objFunction: objFunction, constraints: constraints)
// Initializing Current solution (No need to change anything)
let currentSolution = Array(repeating: 0.0, count: constraints.count)
print("Initial Problem: ")
print(problem)
print("\n")
let solution = PrimalSimplex(problem: problem, currentSolution: currentSolution)
matrixArray = solution.0
basicArray = solution.1
maxArray = solution.2
currentSolutionArray = solution.3
isOptimal = solution.4
isCyceling = solution.5
let CollectionVC = storyboard?.instantiateViewController(withIdentifier: "CollectionView") as! CollectionViewController
CollectionVC.itemStrings = matrixArray[0].makeString()
CollectionVC.columns = problem.columns
CollectionVC.rows = problem.rows
self.present(CollectionVC, animated: true, completion: nil)
}
}
It is basically a screen with a start button, that starts the algorithm when its pushed and presents the next View.
And then this CollectionViewController:
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
@IBOutlet weak var myCollectionView: UICollectionView!
//Is also set in Storyboard
let reuseIdentifier = "cell"
var itemStrings: Array<String?> = []
var rows: Int = 0
var columns: Int = 0
//
//Setup CollectionView: Table to display LPs including Datasource
//
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
let newPath = indexPath.item + 1
print("You selected cell #\(newPath)")
}
// calculate height and width of cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.bounds.width
let gaps = columns-1
let tmpWidth = width - CGFloat(gaps)
let itemWidth = tmpWidth / CGFloat(columns)
let height = collectionView.bounds.height
let tmpHeight = height - CGFloat(gaps)
let itemHeight = tmpHeight / CGFloat(rows)
return CGSize(width: itemWidth, height: itemHeight)
}
// CellSpacing vertical
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(3)
}
// CellSpacing horizontal
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(1)
}
// change background color when user touches cell
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
//Change color
if cell?.backgroundColor == UIColor(red:0.96, green:0.40, blue:0.40, alpha:1.0) {
cell?.backgroundColor = UIColor(red:0.94, green:0.94, blue:0.94, alpha:1.0)
}
else{
cell?.backgroundColor = UIColor(red:0.96, green:0.40, blue:0.40, alpha:1.0)
}
}
// MARK: - UICollectionViewDataSource protocol
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let numberOfItems = rows*columns
return numberOfItems
}
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cel
cell.myLabel.text = itemStrings[indexPath.item]
cell.backgroundColor = UIColor(red:0.94, green:0.94, blue:0.94, alpha:1.0)
// Change shape of cells
cell.layer.cornerRadius = 8
return cell
}
}
These are currently also the only elements in my Storyboard.
I suppose I need to add the PageViewController in the Storyboard and fill it with the code form you of the HomeViewController. Then change the segue in the storyboard from the start button to the HomeViewController.
The parts I didnt understand are the ChildVCs that will be created. Do I need to add that part to my ViewController? And what about the IndexedCollectionViewController class, do I need to create an extra file for that or can I fit that into my collectionView somewhere?
And what about this function
private func viewController(atIndex index:Int) -> IndexedCollectionViewController
where does that go? I think in my case that would be in the ViewController class and Id just call the method in the start button ?
Sorry for all these pretty simple questions, but as a self taught coder its sometimes hard to grasp the concepts behind things ^^
In my view, UIPageViewController documentation is a little vague. The point of contention is the existence of a method:
func setViewControllers(_ viewControllers: [UIViewController]?,
direction: UIPageViewControllerNavigationDirection,
animated: Bool,
completion: ((Bool) -> Void)? = nil)
All the tutorials pass one one view controller in this array and they are right of-course. But because of the existence of this method, I assumed I could give all view controllers, that needed to be shown, in an array to pageviewcontroller.
But, this method actually is needed to pass the view controllers that needs to be shown after the animation. If we need to show more than one view(view controllers) on screen, we can pass those views inside the array. But that is seldom the case, hence all tutorials show passing of a single VC inside the array.
UIPageViewControllerDatasource is used to provide additional view controllers.
You can implement your viewcontroller that hold pageviewcontroller in tis way.
// Extension on UIViewController to insert child view controller
extension UIViewController {
func insertChildController(_ childController: UIViewController, intoParentView parentView: UIView) {
childController.willMove(toParentViewController: self)
self.addChildViewController(childController)
childController.view.frame = parentView.bounds
parentView.addSubview(childController.view)
childController.didMove(toParentViewController: self)
}
}
// Subclass of UICollectionViewController to create IndexedCollectionViewController. This is done to introduce an integer property to index collectionview controllers
class IndexedCollectionViewController:UICollectionViewController {
var index:Int = 0
}
class HomeViewController: UIViewController {
var pageViewController:UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
addPageViewController()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
// MARK:- PageView
extension HomeViewController:UIPageViewControllerDataSource, UIPageViewControllerDelegate {
fileprivate func addPageViewController() {
if !self.isViewLoaded {
return
}
self.pageViewController = UIPageViewController(transitionStyle: UIPageViewControllerTransitionStyle.scroll, navigationOrientation: UIPageViewControllerNavigationOrientation.horizontal, options: nil)
let firstVC = self.viewController(atIndex: 0)
pageViewController.setViewControllers([firstVC], direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
pageViewController.dataSource = self
pageViewController.delegate = self
self.insertChildController(pageViewController, intoParentView: self.view)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
var index = (viewController as! IndexedCollectionViewController).index
index = index+1
return self.viewController(atIndex: index)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
var index = (viewController as! IndexedCollectionViewController).index
if(index == 0){return nil}
index = index-1
return self.viewController(atIndex: index)
}
// Method that creates viewcontroller
private func viewController(atIndex index:Int) -> IndexedCollectionViewController {
// This method will work only if you change the Class of CollectionViewController to IndexedCollectionViewController in your storyboard
let vc = storyboard?.instantiateViewController(withIdentifier: "CollectionView") as! IndexedCollectionViewController
vc.itemStrings = matrixArray[index].makeString()
vc.index = index
vc.columns = problem.columns
vc.rows = problem.rows
return vc
}
}
In my view, your view layouts should look something like this.
VC1 :-> view controller which has start button, and will present vc2.
VC2 :-> view controller which shows pageview.
It can either be a PageViewController itself(as UIPageViewController is subclass of UIViewController) or a simple ViewController with PageViewController added as a child VC.
PageViewController, essentially shows other ViewControllers content, in form of pages. These other viecontrollers in your case is CollectionViewController.