jjatie
jjatie

Reputation: 5332

Creating UIPageViewController with Infinite Scrolling Through Days

My app has a journal in it that is controlled by a UIPageViewController. The view controller for the page works fine, but the UIPageViewController has some erratic behaviour. First, the delegate methods (The page view controller is it's own delegate):

private func journalPage(for date: Date) -> JournalViewController? {
    // Create a new view controller and pass suitable data.
    guard let journalPage = storyboard?.instantiateViewController(withIdentifier: JournalViewController.storyboardID) as? JournalViewController else {
        return nil
    }

    journalPage.date = date
    return journalPage
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
    guard let yesterday = calendar.date(byAdding: .day, value: -1, to: self.date) else {
        return nil
    }

    self.date = calendar.startOfDay(for: yesterday)        
    return journalPage(for: self.date)
}

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

    guard let tomorrow = calendar.date(byAdding: .day, value: 1, to: self.date) else {
        return nil
    }

    self.date = calendar.startOfDay(for: tomorrow)
    return journalPage(for: self.date)
}

I update the date of the UIPageViewController and then return a configured journal page. The problem is that the there is often duplicate pages (pages for the same date), or a page/date is skipped. I found this question, but don't see how I can implement something similar.

I've worked with UIPageViewController before and have always used an index to get around this, but in this case an index doesn't make sense as you can scroll to the beginning/end of time (literally).

How can I ensure that a new page is made at the correct time to avoid duplicate/missed dates?

Upvotes: 0

Views: 3876

Answers (3)

jjatie
jjatie

Reputation: 5332

I solved the problem without using an index (the date property on the pages is sufficient). This infinitely scrolls in either direction without creating duplicates or skipping pages.

extension JournalPageViewController: UIPageViewControllerDataSource {
    /// Creates and returns a JournalViewController if it can be found in the storyboard,
    /// and sets it's date to `date`
    private func journalPage(for date: Date) -> JournalViewController? {
        // Create a new view controller and pass suitable data.
        guard let journalPage = storyboard?.instantiateViewController(withIdentifier: JournalViewController.storyboardID) as? JournalViewController else {
            return nil
        }

        journalPage.date = date
        return journalPage
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        debugPrint("Before: ", separator: "", terminator: "")
        let today = (viewController as! JournalViewController).date
        guard var yesterday = calendar.date(byAdding: .day, value: -1, to: today) else {
            return nil
        }
        yesterday = calendar.startOfDay(for: yesterday)

        return journalPage(for: yesterday)
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        debugPrint("After: ", separator: "", terminator: "")
        let today = (viewController as! JournalViewController).date
        guard var tomorrow = calendar.date(byAdding: .day, value: 1, to: today) else {
            return nil
        }
        tomorrow = calendar.startOfDay(for: tomorrow)

        return journalPage(for: tomorrow)
    }
}

extension JournalPageViewController: UIPageViewControllerDelegate {
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        self.date = (self.viewControllers?.first as! JournalViewController).date
    }
}

Upvotes: 3

Duncan C
Duncan C

Reputation: 131461

Couldn't you use the Juilan Day Number (JDN) as an index number?

Upvotes: 0

ingconti
ingconti

Reputation: 11646

I think You must model your data in another way, so create a model containing an array with your data, and (for example) use an index to access dato to fill controller. in this way you uniquely access data.

I did so in a PageController-base app, starting from apple sample.

I have a class: (see apple template to compare..)

class ModelController: NSObject, UIPageViewControllerDataSource {

var myPages : [Page]?

.. }

where Page is the class describing you single page. I modified apple code in this way:

func viewControllerAtIndex(_ index: Int) -> BaseHWTestController? {
    // Return the data view controller for the given index.

    let count = selected.count
    if (count == 0) || (index >= count) {
        return nil
    }

    // Create a new view controller and pass suitable data.

    let myController = storyboard.instantiateViewController(withIdentifier: identifier) as! CustomPageController
    // set ref. to data inside the controller:
    BaseHWTestController.dataObject = myPages[index]
    return BaseHWTestController
} 

Upvotes: 0

Related Questions