Hello I'm trying to access my constraint from my second view controller to my initial view controller, but it always give an error, Im accessing my constraint from a pageviewontroller class, to automatically adjust the height of my second page.
found nil while unwrapping optional value
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MainNeedPage") as! NeedDetailsController;
vc.bottomContainerHeight.constant = CGFloat(50)
vc.containerHeight.constant = CGFloat(30)
UIView.animate(withDuration: 0.5, animations: {
vc.view.layoutIfNeeded()
})
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
guard orderedViewControllersCount != nextIndex else {
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
Sh_Khan Answer worked, but when I want to get back to the first page of the pageviewcontroller it doesn't get back to it's original size. Here's what i've tried
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
let pageContentViewController = pageViewController.viewControllers![0]
var page = orderedViewControllers.index(of: pageContentViewController)!
if(page == 1) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MainNeedPage") as! NeedDetailsController
NeedDetailsController.status.viewstat = "true"
NeedDetailsController.status.bottomContainerHeightCon = CGFloat(50)
NeedDetailsController.status.containerHeightCon = CGFloat(30)
vc.view.layoutIfNeeded()
} else if(page == 0) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MainNeedPage") as! NeedDetailsController;
NeedDetailsController.status.viewstat = "false"
NeedDetailsController.status.bottomContainerHeightCon = CGFloat(50)
NeedDetailsController.status.containerHeightCon = CGFloat(30)
vc.view.layoutSubviews()
}
}
and on my initial controller
override func viewDidLayoutSubviews() {
print("statusx: \(NeedDetailsController.status.viewstat)")
if(NeedDetailsController.status.viewstat == "true"){
print("statusy: \(NeedDetailsController.status.viewstat)")
NeedDetailsController.status.viewstat = "false"
self.bottomContainerHeight.constant = 0
self.containerHeight.constant = 0
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
} else if(NeedDetailsController.status.viewstat == "false") {
print("statusz: \(NeedDetailsController.status.viewstat)")
setupview()
NeedDetailsController.status.viewstat = "old"
self.bottomContainerHeight.constant = 50
self.containerHeight.constant = 30
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
}
}
You can't access a property whose view controller is not loaded
vc.bottomContainerHeight.constant = CGFloat(50)
vc.containerHeight.constant = CGFloat(30)
try to add two variables to that class
var bottomContainerHeightCon:CGFloat!
var containerHeightCon:CGFloat!
set them like this
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MainNeedPage") as! NeedDetailsController;
vc.bottomContainerHeightCon = CGFloat(50)
vc.containerHeightCon = CGFloat(30)
and in viewDidLayoutSubviews
apply the variables to the constraints
override func viewDidLayoutSubviews
{
if(once){
once = false
self.bottomContainerHeight = bottomContainerHeightCon
self.containerHeight = containerHeightCon
self.view.layoutIfNeeded()
}
}
Related
I have a problem with big memory leaks when I use UIPageViewController. I have a view and I scroll it. I see that memory grows very fast.
i have an array of ViewController (about 350+ vc), the UIPageViewcontroller shows the every VC in a page, the problem is :
to append 350+ Viewcontroller in array is cause memory to grow up fast
so is there a way to append VC in array by viewController Before and viewController After method when scrolling ?
so the array will always be 2 index for example
arraypage1 = [vc1,vc2,vc3]
when scrolling to the next page arraypage1 well be [vc2,vc3,vc4]
so the index 0 remove but a new page will add at index 2
here is what i did so far :
at TestVC :
var TodayDate: String?
var data1 : String? , data2: String?
var data3: String? , data4: String?
var data5: String? , data6: String?
var imagee: UIImage?
at the UIpageViewcontroller :
let vc1 = self.storyboard?.instantiateViewController(withIdentifier: "page1")as! TestPageVC
vc1.TodayDate = "06.09.2022"
vc1.data1 = "welcome"
vc1.data2 = "The first data..."
vc1.data3 = "The first data..."
vc1.data4 = "The first data..."
vc1.data5 = "The first data..."
vc1.data6 = "The first data..."
vc1.imagee = UIImage(named: "0001")
let vc2 = self.storyboard?.instantiateViewController(withIdentifier: "page1")as! TestPageVC
vc2.TodayDate = "07.09.2022"
vc2.data1 = "welcome"
vc2.data2 = "The second data..."
vc2.data3 = "The second data..."
vc2.data4 = "The second data..."
vc2.data5 = "The second data..."
vc2.data6 = "The second data..."
vc2.imagee = UIImage(named: "0002")
let vc3 = self.storyboard?.instantiateViewController(withIdentifier: "page1")as! TestPageVC
vc3.TodayDate = "08.09.2022"
vc3.data1 = "welcome"
vc3.data2 = "The third data..."
vc3.data3 = "The third data..."
vc3.data4 = "The third data..."
vc3.data5 = "The third data..."
vc3.data6 = "The third data..."
vc3.imagee = UIImage(named: "0003")
let vc4 = self.storyboard?.instantiateViewController(withIdentifier: "page1")as! TestPageVC
vc4.TodayDate = "08.09.2022"
vc4.data1 = "welcome"
vc4.data2 = "The fourth data..."
vc4.data3 = "The fourth data..."
vc4.data4 = "The fourth data..."
vc4.data5 = "The fourth data..."
vc4.data6 = "The fourth data..."
vc4.imagee = UIImage(named: "0004")
let vc5 = self.storyboard?.instantiateViewController(withIdentifier: "page1")as! TestPageVC
vc3.TodayDate = "08.09.2022"
vc3.data1 = "welcome"
vc3.data2 = "The fifth data..."
vc3.data3 = "The fifth data..."
vc3.data4 = "The fifth data..."
vc3.data5 = "The fifth data..."
vc3.data6 = "The fifth data..."
vc3.imagee = UIImage(named: "0005")
arraypage1.append(contentsOf:[vc1, vc2, vc3])
// here i want add vc4 and remove vc1 when go to next page
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let currentIndex = arraypage2.firstIndex(of: viewController) else {
return nil
}
UserDefaults.standard.removeObject(forKey: "emptyView1")
let previousIndex = currentIndex - 1
guard previousIndex >= 0 else {
return nil
}
return arraypage2[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let currentIndex = arraypage2.firstIndex(of: viewController) else {
return nil
}
let afterIndex = currentIndex + 1
let afterIndex3 = currentIndex + 2
if afterIndex3 == arraypage2.count {
UserDefaults.standard.set("next", forKey: "emptyView1")
}
guard afterIndex < arraypage2.count else {
return nil
}
return arraypage2[afterIndex]
}
As you've seen, if you are using a UIPageViewController with many pages, you'll run into memory issues rather quickly if you keep an array of instantiated view controllers.
To avoid that, we want to dynamically instantiate the controllers when the Page View Controller requests them.
Here's a quick example...
First, we'll define a data structure (you'll likely have more properties):
struct MyPageData {
var title: String = "Title"
var subtitle: String = "Subtitle"
var imgName: String = "0.circle.fill"
var bkgColor: UIColor = .white
}
Next we'll lay out a "Page" view controller in Storyboard, so it looks something like this:
And set its Custom Class to TestPageVC and give it an Identifier of "page1":
class TestPageVC: UIViewController {
public var pageIndex: Int = 0
#IBOutlet var titleLabel: UILabel!
#IBOutlet var subtitleLabel: UILabel!
#IBOutlet var imageView: UIImageView!
func fillData(_ mpd: MyPageData) {
titleLabel.text = mpd.title
subtitleLabel.text = mpd.subtitle
if let img = UIImage(systemName: mpd.imgName) {
imageView.image = img
}
view.backgroundColor = mpd.bkgColor
}
}
Note that we added a pageIndex property, which we'll use to track the "pages" as they're displayed.
In our UIPageViewController, instead of an array of view controllers, we'll use an array of data structures:
// array of data -- NOT view controllers
var pageData: [MyPageData] = []
and in viewDidLoad() we'll generate 350 of those:
class MyDynamicViewController: UIPageViewController {
// array of data -- NOT view controllers
var pageData: [MyPageData] = []
override func viewDidLoad() {
super.viewDidLoad()
let colors: [UIColor] = [
.red, .green, .blue,
.cyan, .magenta, .yellow,
.systemRed, .systemGreen, .systemYellow,
]
// let's create 350 "pages"
var mpd: MyPageData
for i in 0..<350 {
mpd = MyPageData()
mpd.title = "Page \(i)"
mpd.subtitle = "Subtitle for page \(i)"
mpd.imgName = "\(i % 20).circle.fill"
mpd.bkgColor = colors[i % colors.count]
pageData.append(mpd)
}
dataSource = self
delegate = nil
// instantiate the first "page"
guard let sb = storyboard,
let vc = sb.instantiateViewController(withIdentifier: "page1") as? TestPageVC
else {
fatalError("Could not instantiate view controller!")
}
vc.loadViewIfNeeded()
vc.fillData(pageData[0])
vc.pageIndex = 0
setViewControllers([vc], direction: .forward, animated: false, completion: nil)
}
}
As we see at the end of viewDidLoad(), we instantiated the first "page" view controller and called setViewControllers([vc], ...)
Now we write the UIPageViewControllerDataSource to instantiate and return new instances of "page" view controllers, rather than keeping them in a giant array:
// typical Page View Controller Data Source
extension MyDynamicViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? TestPageVC else { return nil }
if vc.pageIndex == 0 { return nil }
guard let sb = storyboard,
let newVC = sb.instantiateViewController(withIdentifier: "page1") as? TestPageVC
else {
fatalError("Could not instantiate view controller!")
}
let n = vc.pageIndex - 1
newVC.loadViewIfNeeded()
newVC.fillData(pageData[n])
newVC.pageIndex = n
return newVC
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? TestPageVC else { return nil }
if vc.pageIndex >= pageData.count - 1 { return nil }
guard let sb = storyboard,
let newVC = sb.instantiateViewController(withIdentifier: "page1") as? TestPageVC
else {
fatalError("Could not instantiate view controller!")
}
let n = vc.pageIndex + 1
newVC.loadViewIfNeeded()
newVC.fillData(pageData[n])
newVC.pageIndex = n
return newVC
}
}
// typical Page View Controller Delegate
extension MyDynamicViewController: UIPageViewControllerDelegate {
// if you do NOT want the built-in PageControl (the "dots"), comment-out these funcs
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return pageData.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstVC = pageViewController.viewControllers?.first as? TestPageVC else {
return 0
}
return firstVC.pageIndex
}
}
In my case I have UITableView and have View all button for the listing of all the items in separate screens. So I added target for UIButton action method in cellForRowAt. Now what I am doing in action method:
#IBAction func btnViewAllOffer(_ sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tblOfferView)
let indexPath = self.tblOfferView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
if let type = self.homeData[indexPath!.section].type {
if type == HomeDataType.SponserProduct.rawValue {
let vc1 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController1") as! ViewController1
if let title = self.homeData[indexPath!.section].title {
vc1.title = title
}
self.navigationController?.pushViewController(vc1, animated: true)
} else if type == HomeDataType.Offer.rawValue {
let vc2 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
if let title = self.homeData[indexPath!.section].title {
vc2.title = title
}
self.navigationController?.pushViewController(vc2, animated: true)
} else if type == HomeDataType.BestSeller.rawValue {
let vc3 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as! ViewController3
if let title = self.homeData[indexPath!.section].title {
vc3.title = title
}
self.navigationController?.pushViewController(vc3, animated: true)
}
}
}
}
What I need, is there any way I can minimize the code and assign viewcontrollers dynamically so there is no need to instantiate each view controller and push them everytime?
Something like:
var vc = UIViewController()
if let type = self.homeData[indexPath!.section].type {
if type == HomeDataType.SponserProduct.rawValue {
vc = ViewController1()
}
else if type == HomeDataType.Offer.rawValue {
vc = ViewController2()
} else if type == HomeDataType.BestSeller.rawValue {
vc = ViewController3()
}
}
self.navigationController?.pushViewController(vc, animated: true)
Use a protocol (SimilarViewController) to define the common properties like title:
protocol SimilarViewController {
var title: String? { get set }
}
class ViewController1: UIViewController, SimilarViewController {
var title: String?
}
class ViewController2: UIViewController, SimilarViewController {
var title: String?
}
class ViewController3: UIViewController, SimilarViewController {
var title: String?
}
#IBAction func btnViewAllOffer(_ sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tblOfferView)
let indexPath = self.tblOfferView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
if let type = self.homeData[indexPath!.section].type {
var vcGeneric: SimilarViewController?
if type == HomeDataType.SponserProduct.rawValue {
vcGeneric = self.storyboard?.instantiateViewController(withIdentifier: "ViewController1") as! ViewController1
} else if type == HomeDataType.Offer.rawValue {
vcGeneric = self.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
} else if type == HomeDataType.BestSeller.rawValue {
vcGeneric = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as! ViewController3
}
if let title = self.homeData[indexPath!.section].title {
vcGeneric?.title = title
}
if let vcGeneric = vcGeneric as? UIViewController {
self.navigationController?.pushViewController(vcGeneric, animated: true)
}
}
}
}
1: create a struct and assign the value to it.
struct TitleDetails {
static var title : String = ""
}
2: create an extension of viewController and use it to avoid code repetition.
extension UIViewController {
func pushVC(_ vcName : String) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
self.navigationController?.pushViewController(vc, animated: true)
}
}
3: now you can call it directly as,
TitleDetails.title = yourTitleValue
self.pushVC("ViewController1")
and in your ViewDidLoad() method of your destination view controller,
self.title = TitleDetails.title
Create BaseViewController and derived other ViewController from BaseViewController
class BaseViewController: UIViewController {
var viewTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func pushVC(_ vcName : String) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
self.navigationController?.pushViewController(vc, animated: true)
}
}
And use below code on ViewController you need like:
#IBAction func btnViewAllOffer(_ sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tblOfferView)
let indexPath = self.tblOfferView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
if let type = self.homeData[indexPath!.section].type {
self.viewTitle = self.homeData[indexPath!.section].title
if type == HomeDataType.SponserProduct.rawValue {
self.pushVC("ViewController1")
} else if type == HomeDataType.Offer.rawValue {
self.pushVC("ViewController2")
} else if type == HomeDataType.BestSeller.rawValue {
self.pushVC("ViewController3")
}
}
}
}
This is my first post, so I am open to feedback if I can improve my question.
I have been studying Swift for about a year now and I am trying to build an app that uses UIPageViewController to make an app that mimics the experience of a book. The user is able to create as many pages as they want. For now, I have a template UIViewController with an imageView and a textView. The user presses a "+" button to add the next page, which should appear as a pre-set blank imageView (that they can set) and a blank textView.
Specs:
I'm persisting user information with CoreData.
The problem:
I create a new set of pages. Then I add an image and some text. I tap "+" and do the same on the next page. When I tap "+" after that, the next page and each page after that copies the image and text from the previous page rather than being an empty page.
Here is the logic for how my pages should be run.
extension BookPageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = storyPageViewControllers.index(of: viewController as! TemplateViewController) else { return nil }
if viewControllerIndex == 0 { return nil } // Page won't scroll below first page.
let previousIndex = viewControllerIndex - 1
guard storyPageViewControllers.count > previousIndex else { return nil }
currentPage = Double(previousIndex)
return storyPageViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = storyPageViewControllers.index(of: viewController as! TemplateViewController) else { return nil }
let nextIndex = viewControllerIndex + 1
let storyPageViewControllersCount = storyPageViewControllers.count
guard storyPageViewControllersCount != nextIndex else { return nil }
guard storyPageViewControllersCount > nextIndex else { return nil }
currentPage = Double(nextIndex)
return storyPageViewControllers[nextIndex]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return storyPageViewControllers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = storyPageViewControllers.index(of: firstViewController as! TemplateViewController) else
{ return 0 }
return firstViewControllerIndex
}
}
This is the IBAction for "+" button:
guard let book = book else { return }
PageController.shared.createPageWith(text: "", pageNumber: currentPage + 1.0, image: #imageLiteral(resourceName: "ImageViewPlaceHolder"), book: book)
updateStoryPageViewControllers()
setViewControllers([storyPageViewControllers[Int(currentPage) + 1]], direction: .forward, animated: true, completion: nil)
UPDATE:
As requested, here are the functions commenters asked for.
func updateStoryPageViewControllers() {
// Mark: - RETURN HERE TO TALK TO SPENCER ABOUT FIXING THE VC UPDATING.
// Make a check to see which pages already have view controllers, and only make a new view controller for the pages that don't.
guard let book = book else { return }
// Create an array of view controllers to store the page VCs being instantiated
var temporaryViewControllers: [TemplateViewController] = []
// Loop through the books pages, and make a view controller for each
if let pages = book.pages?.array as? [Page], pages.count > 0 {
for page in pages {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let pageVC = storyboard.instantiateViewController(withIdentifier: "TemplateViewController") as? TemplateViewController else { break }
pageVC.page = page
temporaryViewControllers.append(pageVC)
}
} else {
//FIXME: - Default image set here.
guard let page = PageController.shared.createPageWith(text: "", pageNumber: 0, image: #imageLiteral(resourceName: "ImageViewPlaceHolder"), book: book) else { return }
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let pageVC = storyboard.instantiateViewController(withIdentifier: "TemplateViewController") as? TemplateViewController else { return }
pageVC.page = page
temporaryViewControllers.append(pageVC)
}
// Set the self.storyPageViewControllers array to the array you just made and filled.
self.storyPageViewControllers = temporaryViewControllers
}
Here is the code for the createPageWith method:
#discardableResult func createPageWith(text: String, pageNumber: Double, image: UIImage, book: Book) -> Page? {
//Turns the UIImage into data to be saved in CoreData.
guard let data = UIImageJPEGRepresentation(image, 0.8) else { return nil }
let page = Page(text: text, pageNumber: pageNumber, photoData: data, book: book)
BookController.shared.saveToPersistentStore()
return page
}
i wanted to change the login screen userName Outlet but its not working i can not find our the solution
let loginVC = storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
if self.emailTextField.text == "" {
loginVC.userName?.text = "Hello"
} else {
loginVC.userName?.text = self.emailTextField.text!
}
navigationController?.pushViewController(loginVC, animated: true)
Try this:
let loginVC = storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
if self.emailTextField.text == ""
{
loginVC.str = "Hello"
}
else
{
loginVC.str = self.emailTextField.text!
}
navigationController?.pushViewController(loginVC, animated: true)
LoginViewController
class LoginViewController: UIViewController
{
var str:String! = nil
override func viewDidLoad()
{
super.viewDidLoad()
self.userName.text = str
}
}
I have a working page view controller system with this ( just going to paste absolutely everything, pretty sure the issue just lies in my firebase observation near the top) :
class IndividualPeopleVC: UIViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles : [String]!
var imageURLS: NSArray!
let currentUser = FIRAuth.auth()?.currentUser?.uid
override func viewDidLoad()
{
super.viewDidLoad()
self.pageTitles = []
self.pageTitles = ["1", "2", "3", "4'"]
DataService.ds.REF_INTERESTS.child(passedInterest).child("rooms").child(passedRoom).child("users").observeSingleEventOfType(.Value) { (snapshot: FIRDataSnapshot) in
print(snapshot.value)
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
let name = snap.key
let uid = snap.value as! String
let person = Person(name: name, bio: "", UID: uid)
if person.UID != self.currentUser {
self.pageTitles.append(uid)
}
}
}
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as ContentViewController
let viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.titleText = self.pageTitles[index] as! String
vc.imageFile = self.pageImages[index] as! String
vc.pageIndex = index
return vc
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound) {
return nil
}
index++
if (index == self.pageTitles.count){
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int{
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int{
return 0
}
}
So basically most of that is just copy-pasted from another source and it works. It creates 4 pages from the pageTitles array that scroll and on another VC I am passing the 1,2,3 and 4 and a label is displaying everything in the array as the scrollview should.
The problem comes when I try to not hard-code the pageTitles and instead pull it from firebase.
When I comment out where I'm hard-coding the pageTitles array, I get an unexpected nil value on my ContentViewController where I'm sending the titles and I do like titleLabel.text = (what was sent over).
I figured maybe guarding it with an if/let to allow firebase time to load stuff in would help, so I did a
if let title = (what was sent over) {
titleLabel.text = title
}
Nothing shows up on the screen when this happens, and then if I scroll left or right I get an error with indexes down in my pagebefore/pageafters.
I feel like this should be super simple and very similar to a tableview? Load in data from firebase, fill an array with the info, use if/lets to allow firebase some time to load in, then update the UI based on the array.
I think your DataService is asynchronous. You have to create your pageviewcontroller once your data is ready. Try this modified viewDidLoad method.
override func viewDidLoad()
{
super.viewDidLoad()
self.pageTitles = []
// self.pageTitles = ["1", "2", "3", "4'"]
DataService.ds.REF_INTERESTS.child(passedInterest).child("rooms").child(passedRoom).child("users").observeSingleEventOfType(.Value) { (snapshot: FIRDataSnapshot) in
print(snapshot.value)
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
let name = snap.key
let uid = snap.value as! String
let person = Person(name: name, bio: "", UID: uid)
if person.UID != self.currentUser {
self.pageTitles.append(uid)
}
}
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as ContentViewController
let viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
}