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)
}
}
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
}
}
I am using XLPagerTabStrip to create a category based reading app in Swift 4. I learnt static number of ViewControllers can be easily created using the following function.
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { }
However, the number of categories in my case depends on server response, which may change as per the need. I tried to create dynamic number of tabs by creating view controllers based on the name of categories I parsed from the json response. This is the method I did a hit and trial.
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
var childrenVC = [UIViewController]()
for eachCategory in postCategories {
print(eachCategory)
let newVC = self.storyboard?.instantiateViewController(withIdentifier: "FirstTVC") as? FirstTVC
newVC?.childName = eachCategory.name
childrenVC.append(newVC!)
self.reloadPagerTabStripView()
self.reloadInputViews()
}
return childrenVC
}
Yes, it failed. How can I achieve dynamic number of tabs in this case? If not I am also open to any other alternative. I am done with json response thing but stuck in this step. This SO answer and Github Issue didn't help as well.
I had the exact same situation. If you fetch results from the server asynchronously, as you should, you will have a crash since xlpagertabstrip needs at least one child viewcontroller.
The solution i found is to return one dummy viewcontroller at the start of the application, and after fetching data from the server to reloadPagerTabStripView.
In your parent viewcontroller you should make an empty array of your objects which you fetch from the server, for example:
var menuItems = [MenuObject]()
Next, viewControllers(for pagerTabStripController ... ) method should look like this:
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
var children = [UIViewController]()
if menuItems.count == 0 {
let child = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DummyVC") as! DummyVC
children.append(child)
return children
} else {
let menuItemsUrls = menuItems.map{$0.url}
for menuItem in menuItems {
let child = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainVC") as! TelegrafMainVC
child.name = menuItem.name.uppercased
child.url = menuItem.url
children.append(child)
}
return children
}
}
And after you fetch data from the server, whether using URLSession, or Alamofire, or whatever else, in the function or a closure you should put something like:
self.menuItems = menuItems
DispatchQueue.main.async {
self.reloadPagerTabStripView()
}
Tell me if i need to clarify something, but i believe this will be sufficient to help you.
Cheers
Step 1 : Do this in your initial viewcontroller,
class initialViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
self.loadMainView(viewControllerArray: self.setMainViewTabParameters())
}
func setMainViewTabParameters() -> NSMutableArray {
let viewControllerArray = NSMutableArray()
var tabArray = ["Tab1", "Tab2", "Tab3", "Tab4", "Tab5", "Tab6", "Tab7"]
var tabIndex = NSInteger()
for item in tabArray {
let tabString = tabArray[tabIndex]
let tabview = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "YourTabControllerIdentifier") as! YourTabController
tabview.tabHeadingTitle = tabString as NSString
viewControllerArray.add(tabview)
tabIndex = tabIndex + 1
}
return viewControllerArray
}
func loadMainView(viewControllerArray : NSMutableArray) -> Void {
let mainView = self.storyboard?.instantiateViewController(withIdentifier: "YourMainControllerIdentifier") as! YourMainController
mainView.viewControllerList = viewControllerArray
self.navigationController?.pushViewController(mainView, animated: false)
}
}
Step 2:
class YourMainController: ButtonBarPagerTabStripViewController {
var viewControllerList = NSArray()
var isReload = false
//MARK: - PagerTabStripDataSource
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
guard isReload else {
return viewControllerList as! [UIViewController]
}
var childViewControllers = viewControllerList as! [UIViewController]
for (index, _) in childViewControllers.enumerated(){
let nElements = childViewControllers.count - index
let n = (Int(arc4random()) % nElements) + index
if n != index{
swap(&childViewControllers[index], &childViewControllers[n])
}
}
let nItems = 1 + (arc4random() % 8)
return Array(childViewControllers.prefix(Int(nItems)))
}
override func reloadPagerTabStripView() {
isReload = true
if arc4random() % 2 == 0 {
pagerBehaviour = .progressive(skipIntermediateViewControllers: arc4random() % 2 == 0, elasticIndicatorLimit: arc4random() % 2 == 0 )
} else {
pagerBehaviour = .common(skipIntermediateViewControllers: arc4random() % 2 == 0)
}
super.reloadPagerTabStripView()
}
}
Step 3: Your tab view controller:
class YourTabController: UIViewController, IndicatorInfoProvider {
var tabHeadingTitle = NSString()
// MARK: - Top Tab Bar Method - IndicatorInfoProvider
func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
return IndicatorInfo(title: tabHeadingTitle as String)
}
}
Try this
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
var childrenVC: [UIViewController] = []
for eachCategory in postCategories {
let newVC = UIStoryboard(name: "Main (YOUR-STORYBOARD-NAME)", bundle: nil).instantiateViewController(withIdentifier: "FirstTVC") as? FirstTVC
newVC?.childName = eachCategory.name
childrenVC.append(newVC!)
}
return childrenVC
}
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 have a 2 dimensional UIPageViewController (Two PageViewController, one horizontal, and one vertical).
On the first level (X = 0), I display full screen videos with autoplay. When i'm using transition style Page Curl, i have no problems, but with transition style Scroll, the fact that the "after view controller" is preloaded, it will execute the code and autoplay the video (hosted on youtube) before, therefore, it will stop the video which on the view controller i'm actually seeing, and also, i will hear that video in the background.
I can't figure how to make sure that the video that is playing is one i'm really looking at.
this is how I set my view:
import UIKit
class YViewController: UIViewController {
var yPageIndex: Int!
var xPageIndex: Int!
var yLabelText: String!
override func viewDidLoad() {
super.viewDidLoad()
if (xPageIndex == 0) { // if xPageIndex == 0 then you are on the tag page. Else, you are browsing
// HOME SCREEN
let homeLabel = UILabel(frame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))
// homeLabel.backgroundColor = UIColor.blueColor()
// homeLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2)
// homeLabel.textAlignment = NSTextAlignment.Center
homeLabel.numberOfLines = 0
let htmlString = "<center><h1>Welcome John Smith</h1> <h2> Your Tags: </h2> <i> #unity #iOS #php #XCODE</i><br><br><br><br><br><br> <i>Swipe left to start browsing job offers</i></center>"
let encodedData = htmlString.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
do {
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
homeLabel.attributedText = attributedString
} catch _ {
print("Cannot create attributed String")
}
self.view.addSubview(homeLabel)
} else {
if yPageIndex == 0 { // if yPageIndex == 0 then you are on video page. Else, you are on the job description page
var webView = UIWebView(frame: self.view.frame)
view.addSubview(webView)
view.bringSubviewToFront(webView)
webView.allowsInlineMediaPlayback = true
webView.mediaPlaybackRequiresUserAction = false
webView.scrollView.bounces = false
let videoID = mainInstance.jobInfoArray[xPageIndex].videoId
let embededHTML = "<html><body style='margin:0px;padding:0px;'><script type='text/javascript' src='http://www.youtube.com/iframe_api'></script><script type='text/javascript'>function onYouTubeIframeAPIReady(){ytplayer=new YT.Player('playerId',{events:{onReady:onPlayerReady}})}function onPlayerReady(a){a.target.playVideo();}</script><iframe id='playerId' type='text/html' width='\(self.view.frame.size.width)' height='\(self.view.frame.size.height)' src='http://www.youtube.com/embed/\(videoID)?enablejsapi=1&rel=0&playsinline=1&autoplay=0&controls=0' frameborder='0'></body></html>"
webView.loadHTMLString(embededHTML, baseURL: NSBundle.mainBundle().resourceURL)
print("width='\(self.view.frame.size.width)' height='\(self.view.frame.size.height)'")
} else {
print("Job " + mainInstance.jobInfoArray[xPageIndex].jobTitle)
print("width='\(self.view.frame.size.width)' height='\(self.view.frame.size.height)'")
var homeLabel = UILabel(frame: CGRectMake(0, 30, self.view.frame.size.width, self.view.frame.size.height))
homeLabel.numberOfLines = 0
let htmlString = "<h1>\(mainInstance.jobInfoArray[xPageIndex].jobTitle)</h1> <h2>Company: \(mainInstance.jobInfoArray[xPageIndex].jobCompany) </h2> <i> tags: \(mainInstance.jobInfoArray[xPageIndex].jobKeyWords)</i><br>Salary: \(mainInstance.jobInfoArray[xPageIndex].jobSalary)<br> <br> \(mainInstance.jobInfoArray[xPageIndex].jobDescription)"
let encodedData = htmlString.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
do {
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
homeLabel.attributedText = attributedString
} catch _ {
print("Cannot create attributed String")
}
homeLabel.sizeToFit()
self.view.addSubview(homeLabel)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
and this how I configured my UIPageView
class XViewController: UIViewController, UIPageViewControllerDataSource {
// to init y display
var yPageViewController: UIPageViewController!
var yLabel: NSArray!
// To remove ?
var xPageIndex: Int!
var videoTitleText: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// self.videoLabel.text = self.videoTitleText
self.yLabel = NSArray(objects: "Video", "Job Description")
self.yPageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("yPageViewController") as! UIPageViewController
self.yPageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as YViewController
let viewControllers = NSArray(object: startVC)
self.yPageViewController.setViewControllers(viewControllers as! [UIViewController], direction: .Forward, animated: false, completion: nil)
self.yPageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width , self.view.frame.size.height)
self.addChildViewController(self.yPageViewController)
self.view.addSubview(self.yPageViewController.view)
self.yPageViewController.didMoveToParentViewController(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(yIndex: Int) -> YViewController
{
if ( (self.yLabel.count == 0) || (yIndex >= self.yLabel.count) ) {
return YViewController()
}
let vc: YViewController = self.storyboard?.instantiateViewControllerWithIdentifier("yContentViewController") as! YViewController
vc.yPageIndex = yIndex
vc.xPageIndex = xPageIndex
vc.yLabelText = self.yLabel[yIndex] as! String
return vc
}
// MARK: Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! YViewController
var yIndex = vc.yPageIndex as Int
if ( (yIndex == 0) || (yIndex == NSNotFound) ) {
return nil
}
yIndex--
return self.viewControllerAtIndex(yIndex)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! YViewController
var yIndex = vc.yPageIndex as Int
if (yIndex == NSNotFound || xPageIndex == 0) {
return nil
}
yIndex++
if (yIndex == self.yLabel.count)
{
return nil
}
return self.viewControllerAtIndex(yIndex)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
and just in case, here the ViewConytoller (the fact that it goes horizontal and vertical might makes it a bit hard to figure out.
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
var xPageViewController: UIPageViewController!
var videoId: NSArray!
// var yPageViewController: UIPageViewController!
// var jobId: NSArray!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.videoId = NSArray(objects: "Video #1", "Video #2", "Video #3", "Video #4", "Video #5")
self.xPageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("xPageViewController") as! UIPageViewController
self.xPageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as XViewController
let viewControllers = NSArray(object: startVC)
self.xPageViewController.setViewControllers(viewControllers as! [UIViewController], direction: .Forward, animated: false, completion: nil)
self.xPageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width , self.view.frame.size.height)
self.addChildViewController(self.xPageViewController)
self.view.addSubview(self.xPageViewController.view)
self.xPageViewController.didMoveToParentViewController(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(xIndex: Int) -> XViewController
{
if ( (self.videoId.count == 0) || (xIndex >= self.videoId.count) ) {
return XViewController()
}
let vc: XViewController = self.storyboard?.instantiateViewControllerWithIdentifier("xContentViewController") as! XViewController
vc.xPageIndex = xIndex
vc.videoTitleText = self.videoId[xIndex] as! String
return vc
}
// MARK: Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! XViewController
var xIndex = vc.xPageIndex as Int
if ( (xIndex == 0) || (xIndex == NSNotFound) ) {
return nil
}
xIndex--
return self.viewControllerAtIndex(xIndex)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! XViewController
var xIndex = vc.xPageIndex as Int
if (xIndex == NSNotFound) {
return nil
}
xIndex++
if (xIndex == self.videoId.count)
{
return nil
}
return self.viewControllerAtIndex(xIndex)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
I'm new to this, so i'm sorry if i'm not clear or if I'm not following the "posting rules" for this site.
If anyone as any ideas on how i could achieve that, that would be brilliant.
Thanks a lot,
Julien
I'm learning how to use a PageViewController and in the app I'm building I need to be able to refresh the content on the screen from the containing ViewController.
So in my storyboard I have:
a view controller, class RoomPageListViewController.
a view controller, class RoomContentViewController which has a number of labels which are update using CoreData.
a PageViewController
The setup is very simple, I can put in the entire code if needed but what I wanted to do is from RoomPageListViewController to be able to call a function within RoomContentViewController to update the labels and keep the user on the page that they are.
Whatever I tried has resulted in error, for example tried:
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("RoomContentViewController") as! RoomContentViewController
pageContentViewController.updateScreen()
But no luck... how can I accomplish this or am I doing it the 'wrong' way?
Thanks!
EDIT v3: With a protocol implementation now working fully!
This is the code for the RoomPageListViewController:
class RoomPageListViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var roomContentVCAccess: RoomContentVCAccess!
var roomsList: Array<String> = ["Entire Home"]
var roomButtonClicked: String = ""
let activityInd: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.WhiteLarge)
var showInd: Bool = true
let shadowLabel: UILabel = UILabel(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
var viewBySelection: Int = 1
var roomDeviceGroupID: Int = 0
var redrawBool: Bool = true
var displayRoom: String = ""
var pageViewController : UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Devices By Room"
var backBtn : UIBarButtonItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: self, action: nil)
self.navigationItem.leftBarButtonItem = backBtn
self.navigationItem.leftBarButtonItem?.enabled = false
var settingsBtn : UIBarButtonItem = UIBarButtonItem(title: "Settings", style: UIBarButtonItemStyle.Plain, target: self, action: "goSettings")
self.navigationItem.rightBarButtonItem = settingsBtn
activityInd.stopAnimating()
if showInd == true {
startInd()
}
}
func startInd() {
shadowLabel.backgroundColor = UIColor.lightGrayColor()
shadowLabel.text = "Please Wait... Loading Data...\n\n\n\n\n"
shadowLabel.numberOfLines = 6
shadowLabel.textAlignment = NSTextAlignment.Center
let screenSize: CGRect = UIScreen.mainScreen().bounds
shadowLabel.center = CGPoint (x: screenSize.width/2 , y: screenSize.height/2)
shadowLabel.alpha = 0.5
shadowLabel.hidden = false
activityInd.center = CGPoint (x: screenSize.width/2 , y: screenSize.height/2)
activityInd.color = UIColor.blueColor()
activityInd.startAnimating()
activityInd.hidden = false
self.view.addSubview( shadowLabel )
self.view.addSubview( activityInd )
}
func stopInd() {
shadowLabel.hidden = true
activityInd.stopAnimating()
activityInd.hidden = true
showRooms()
if (redrawBool == true) {
//showRooms()
reset()
} else {
self.roomContentVCAccess.updateScreen()
}
redrawBool = false
}
func showRooms() {
roomsList = ["Entire Home"]
var serverSettings:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var managedContext: NSManagedObjectContext = serverSettings.managedObjectContext!
var request = NSFetchRequest(entityName: "Devices")
request.propertiesToFetch = NSArray(objects: "room") as [AnyObject]
request.resultType = NSFetchRequestResultType.DictionaryResultType
request.returnsDistinctResults = true
let deviceFilter = NSPredicate (format: "room <> %#", "Unknown")
request.predicate = deviceFilter
var roomsResults: Array<AnyObject> = managedContext.executeFetchRequest(request, error: nil)!
println("count: \(roomsResults.count)")
if roomsResults.count > 0 {
for room in roomsResults {
var theroom = room["room"] as! String
if (theroom != "Alarm") {
roomsList.append(theroom)
}
}
}
println(roomsList)
}
func reset() {
/* Getting the page View controller */
pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
let pageContentViewController = self.viewControllerAtIndex(0)
self.pageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
self.addChildViewController(pageViewController)
self.view.addSubview(pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
//stopInd()
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! RoomContentViewController).pageIndex!
index++
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! RoomContentViewController).pageIndex!
if (index <= 0) {
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func viewControllerAtIndex(index : Int) -> UIViewController? {
if ((self.roomsList.count == 0) || (index >= self.roomsList.count)) {
return nil
}
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("RoomContentViewController") as! RoomContentViewController
self.roomContentVCAccess = pageContentViewController
pageContentViewController.room = self.roomsList[index]
pageContentViewController.pageIndex = index
displayRoom = self.roomsList[index]
return pageContentViewController
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return roomsList.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
and the code for RoomContentViewController:
protocol RoomContentVCAccess {
func updateScreen()
}
class RoomContentViewController: UIViewController, RoomContentVCAccess {
var pageIndex: Int?
var room : String!
#IBOutlet weak var screenScrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
screenScrollView.contentSize = CGSizeMake(screenScrollView.frame.size.width, 650)
roomName.text = room
btnViewAllRoomSensors.layer.cornerRadius = 10
btnViewAllRoomSensors.layer.masksToBounds = true
updateScreen()
}
override func viewDidAppear(animated: Bool) {
updateScreen()
}
func updateScreen() {
println(room)
let roomValues = getLabelValues(room)
println(roomValues)
var roomDevicesCount: Array<Int> = roomValues[0] as! Array<Int>
// more code here....
}
func getLabelValues(roomName: String) -> (Array<AnyObject>) {
var serverSettings:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var managedContext: NSManagedObjectContext = serverSettings.managedObjectContext!
var request = NSFetchRequest(entityName: "Devices")
let deviceFilter = NSPredicate (format: "room = %#", roomName)
// more CoreData code...
}
The overall picture is that the app, once it receives data calls the stopInd() within RoomPageListViewController. From within stopInd() I need to be able to call updateScreen() that is in RoomContentViewController.
You could create a protocol that the ViewController owning the labels conforms to. For example:
protocol RoomContentVCAccess
{
func updateLabels()
}
Then in your RoomContentViewController's class declaration:
class RoomContentViewController: UIViewController, RoomContentVCAccess
{
// ...
// MARK: - RoomContentVCAccess
func updateLabels()
{
// update your labels
}
}
Your RoomPageListViewController also has to know who his roomContentVCAccess is. For that, just create an instance variable in RoomPageListViewController: var roomContentVCAccess: RoomContentVCAccess! and then say self.roomContentVCAccess = viewController as! RoomContentViewController in your viewControllerAtIndex-function.
And then when stopInd() is called in RoomPageListViewController, say self.roomContentVCAccess.updateLabels().