Pass data in UIPageViewController between different swipe views - ios

I am working on a project with 3 views that are accessed by swiping.
The following code implements the PageViewController I am using to handle the swiping motion:
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
lazy var orderedViewControllers : [UIViewController] = {
return [self.newVC(viewController : "ClimaVC"),
self.newVC(viewController : "WeatherVC"),
self.newVC(viewController : "ClosetVC")]
}()
var myViewControllers = [UIViewController]()
var currentIndex : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.dataSource = self
setViewControllers([orderedViewControllers[1]],
direction: .forward, animated: true, completion: nil)
self.delegate = self
}
func newVC(viewController : String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1;
guard previousIndex >= 0 else {
// return orderedViewControllers.last
//Return nil to avoid swiping forever.
return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
currentIndex = previousIndex
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1;
guard orderedViewControllers.count != nextIndex else {
//return orderedViewControllers.first
//Return nil to avoid swiping forever.
return nil
}
guard orderedViewControllers.count > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}
The main View Controller is WeatherVC. In this view, time data is retrieved and stored in an integer variable named timeOfDay.
Depending on the value of timeOfDay, the background displayed will differ. While I was able to use this variable in WeatherVC, I need to pass this variable timeOfDay to the two other view controllers ClimaVC and ClosetVC.
I tried setting up a delegate protocol but failed, explored using the different functions provided by UIPageViewControllerDelegate and it did not get me anywhere.
I would really appreciate some help on this,
Thank you very much!

It's not complicated, just assign the values at the pageviewcontroller datasources.
import UIKit
class ClimaViewController: UIViewController{
var timeOfDayClima: Int = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print (timeOfDayClima)
}
}
class WeatherViewController: UIViewController{
var timeOfDayClima: Int = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
timeOfDayClima += 1
}
}
class ClosetViewController: UIViewController{
var timeOfDayClima: Int = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print (timeOfDayClima)
}
}
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
lazy var orderedViewControllers : [UIViewController] = {
return [self.newVC(viewController : "ClimaVC"),
self.newVC(viewController : "WeatherVC"),
self.newVC(viewController : "ClosetVC")]
}()
var myViewControllers = [UIViewController]()
var currentIndex : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.dataSource = self
setViewControllers([orderedViewControllers[1]],
direction: .forward, animated: true, completion: nil)
self.delegate = self
}
func newVC(viewController : String) -> UIViewController {
return UIStoryboard(name: "Second", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
//Guard = some kind of if statement.
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1;
guard previousIndex >= 0 else {
// return orderedViewControllers.last
//Return nil to avoid swiping forever.
return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
currentIndex = previousIndex
if(currentIndex == 0) {
(orderedViewControllers.first as! ClimaViewController).timeOfDayClima = (orderedViewControllers[1] as! WeatherViewController).timeOfDayClima
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
//Guard = some kind of if statement.
guard let viewControllerIndex = orderedViewControllers.index(of: viewController)
else {
return nil
}
let nextIndex = viewControllerIndex + 1;
guard orderedViewControllers.count != nextIndex else {
//return orderedViewControllers.first
//Return nil to avoid swiping forever.
return nil
}
guard orderedViewControllers.count > nextIndex else {
return nil
}
currentIndex = nextIndex
if(currentIndex == orderedViewControllers.count - 1) {
(orderedViewControllers.last as! ClosetViewController).timeOfDayClima = (orderedViewControllers[1] as! WeatherViewController).timeOfDayClima
}
if(nextIndex == 0) {
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "ClimaVC") as! ClimaViewController
secondVC.timeOfDayClima = 1
}
return orderedViewControllers[nextIndex]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Maybe you should try explaining what has failed in setting up your delegate protocol as well because it seems like it is likely the solution you are looking for. I'm writing a protocol here for reference but you have to change it according to your needs.
protocol WeatherVCDelegate {
func changedTime(timeOfDay: Int)
}
Here comes the tricky part. When you declare a delegate in WeatherVC, you need to use the delegate to call the function to relay the timeOfDay to other weatherVC. How this works is that anything that conforms to the delegate protocol will be triggered to use the function.
class WeatherVC {
weak var delegate: WeatherVCDelegate?
func functionName() {
delegate?.changedTime(timeOfDay: value)
}
Now, when you initialize weatherVC in your PageViewController, you need PageViewController to conform to weatherVC delegate.
In PageViewController...
weatherVC.delegate = self
extension PageViewController: WeatherVCDelegate {
...
}
Here is more read up about delegates if my answer is not clear enough
https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef

Related

UIPageViewControllerDataSource is not called

The UIPageViewControllerDataSource is not being called from ViewDidLoad, hence only the first page is displayed and swipe doesn't do anything.
I have followed this tutorial:
https://spin.atomicobject.com/2015/12/23/swift-uipageviewcontroller-tutorial/
The answers in questions similar to this do not work. I have viewed 5 similar questions but none of them work for me.
class SetupViewController: UIPageViewController {
private(set) lazy var pages : [UIViewController] = {
return [self.newPage(1), self.newPage(2), self.newPage(3), self.newPage(4)]
}()
private func newPage(_ page: Int) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Page\(page)")
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstPage = pages.first {
print(#function, "if let firstPage", firstPage, pages) //viewDidLoad() if let firstPage page1name [page1, page2, page3, page4]
setViewControllers([firstPage], direction: .forward, animated: true, completion: nil)
}
}
}
extension SetupViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
print(#function) //It doesn't print
guard let pageIndex = pages.firstIndex(of: viewController) else { return nil }
let nextIndex = pageIndex + 1
let pageCount = pages.count
guard nextIndex != pageCount else { return nil }
guard pageCount > nextIndex else { return nil }
return pages[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
print(#function) //It doesn't print
guard let pageIndex = pages.firstIndex(of: viewController) else { return nil }
let previousIndex = pageIndex - 1
guard previousIndex >= 0 else { return nil }
guard pages.count > previousIndex else { return nil }
return pages[previousIndex]
}
}
Storyboard IDs are: Page1, Page2, Page3 & Page4
What am I doing wrong?
May be you have a configuration problem i have created a demo here
https://github.com/ShKhan9/PagerShow
let wind = (UIApplication.shared.delegate as! AppDelegate).window!
wind.rootViewController = SetupViewController()

Pageview scrollview is not workout

This is my code for pageView . It does not scroll. I am using swift2 because of unavailability of swift4 in my setup. Can anyone help me I am Fresher!
import UIKit
class TutorialPageViewController:UIPageViewController,UIPageViewControllerDataSource,UIPageViewControllerDelegate
{
//Storyboard class and id -
//ViewController1,ViewController2,ViewController3
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newColoredViewController("ViewController1"),
self.newColoredViewController("ViewController2"),
self.newColoredViewController("ViewController3")]
}()
private func newColoredViewController(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("name")
}
//code of pageview
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let ViewController1 = orderedViewControllers.first {
setViewControllers([ViewController1],
direction: .Forward,
animated: true,
completion: nil)
}
}
You need to implement UIPageViewControllerDataSource too :
extension Yourcontroller: UIPageViewControllerDataSource{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0 else {
return orderedViewControllers.last
// Uncommment the line below, remove the line above if you don't want the page control to loop.
//return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
// Uncommment the line below, remove the line above if you don't want the page control to loop.
//return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}

page view controller hide indicator dots and stop in the last page (can't come back)

I have a page control in my project and I want to do two things :
1. I want to hide indicator dots in the last page
2. I want to lock the last page in the (page VC(my class)) that I can't come back to the first page
here is my code
class pageVC : UIPageViewController , UIPageViewControllerDataSource , UIPageViewControllerDelegate {
lazy var VCArr : [UIViewController] = {
return [self.VCInstance(name : "FirtsVC"),
self.VCInstance(name : "SecondVC"),
self.VCInstance(name :"ThirdVC"),
self.VCInstance(name :"FourthVC"),
self.VCInstance(name :"FivethVC")]
}()
private func VCInstance(name : String) -> UIViewController {
return UIStoryboard(name : "Main" , bundle : nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if VCArr.last != nil {
print("first Page Reached!")
}
if let firstVC = VCArr.first {
setViewControllers([firstVC] , direction: .forward , animated: true, completion: nil)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let pageVC = UIPageControl()
for view in self.view.subviews {
if view is UIScrollView {
view.frame = UIScreen.main.bounds
}else if view is UIPageControl{
view.backgroundColor = UIColor.clear
pageVC.numberOfPages = 5
pageVC.center = self.view.center
pageVC.layer.position.y = self.view.frame.height - 180 ;
}
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?{
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex-1
guard previousIndex >= 0 else {
return VCArr.last
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?{
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex+1
guard nextIndex < VCArr.count
else {
return VCArr.first
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int{
return VCArr.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int{
guard let firstViewController = viewControllers?.first , let firstViewControllerIndex = VCArr.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if (VCArr.last!.isViewLoaded)
{
print("It is Done!!!")
}
}
Change this, which is making the page view to return to the first one when it is in the last one:
let nextIndex = viewControllerIndex+1
guard nextIndex < VCArr.count
else {
return nil //was VCArr.first
}
And to make it hidden, first move the let pageVC = UIPageControl() below the class pageVC : UIPageViewController , UIPageViewControllerDataSource , UIPageViewControllerDelegate { to make it public within the class. Then add:
if nextIndex == VCArr.count{
pageVC.isHidden = true
}

present walkthrough scenes only once ios swift

I created a PageViewController and added three view controllers as a walkthrough for my app, but i want them to be displayed only once, and i can't figure out how.
here is my code for the pageViewController:
import UIKit
class MyPageViewController: UIPageViewController{
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
// Do any additional setup after loading the view.
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//array for the ordered view controllers
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newColoredViewController("First"),
self.newColoredViewController("Second"),
self.newColoredViewController("Third")]
}()
private func newColoredViewController(color: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewControllerWithIdentifier("\(color)ViewController")
}
}
extension MyPageViewController: UIPageViewControllerDataSource {
//returning index of previous view controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
//returning index of the next view controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
guard orderedViewControllersCount != nextIndex else {
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}
how do i edit the code to display it only once?
From what you've said I'm guessing that you only want it to show the first time the user opens the app and then not appear again?
Theres a few ways of doing this, the simplest would likely be to set a property on NSUserDefaults, this will remember the setting in between app launches. try something like:
// this should be your initial view controller
let defaults = NSUserDefaults.standardUserDefaults()
override func viewDidLoad() {
super.viewDidLoad()
if !defaults.boolForKey("walkthroughSeen") {
// create page view controller and display
let walkthroughVC = UIStoryboard(name: "Main", bundle: nil).
instantiateViewControllerWithIdentifier("MyPageViewController")
self.presentViewController(walkthroughVC, animated: true)
defaults.setBool(true, forKey: "walkthroughSeen")
}
}

Setting up NSTimer with PageViewController in Swift

I recently set up a ContainerView with a PageViewController that cycles through views (images) as a sort of banner on top of a TableView that I have. Now, I'm having difficulty hooking that up to an NSTimer (so that it will cycle through the views/images automatically). Everything works, except for the timer piece that I've tried to hook up. I've tried to convert some Objective-C tutorials/SO questions but am still having difficulty.
I have looked at this: Set timer to UIPageViewController, but I can't get that provided code to work for me at all (nor any variation of it).
See below for my code:
class ImageSwapViewController: UIPageViewController {
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newColoredViewController("First"),
self.newColoredViewController("Second"),
self.newColoredViewController("Third")]
}()
private func newColoredViewController(order: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewControllerWithIdentifier("\(order)ViewController")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(ImageSwapViewController.animation), userInfo: nil, repeats: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func animation() {
[NEED HELP WITH WHAT GOES HERE]
}
}
extension ImageSwapViewController : UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return orderedViewControllers.last
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}
Any help is greatly appreciated.
A couple of notes:
1) The general idea is that you need to set up a timer in some reasonable place--viewWillAppear is a common location, and remember to invalidate it when the view is no longer displaying (as in viewWillDisappear). Failing to do so may result in memory growth because the run loop holds a strong reference to the target--in this case, self. I'd recommend moving your NSTimer.scheduledTimer... invocation to viewWillAppear.
2) The animation code above is pretty simple. Basically, you need to get the current "page", find out what the next "page" should be, and then ask your UIPageViewController to display that next "page".
func animation() {
guard let currentViewController = viewControllers?.first else {
return
}
// Use delegate method to retrieve the next view controller
guard let nextViewController = pageViewController(self, viewControllerAfterViewController: currentViewController) else {
return
}
setViewControllers([nextViewController], direction: .Forward, animated: true, completion: nil)
}
You could also simplify your before and after methods slightly:
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let i = orderedViewControllers.indexOf(viewController) else {
return nil
}
let nextIndex = (i + 1) % AutoAnimatingPageViewController.colors.count
return orderedViewControllers[nextIndex]
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let i = orderedViewControllers.indexOf(viewController) else {
return nil
}
let prevIndex = (i - 1) < 0 ? AutoAnimatingPageViewController.colors.count - 1 : (i - 1)
return orderedViewControllers[prevIndex]
}

Resources