Swift ios not sure how to connect or use inline UIPageViewController

I decided to ditch my UIImageView and instead use a UIPageViewController so that users can scroll between images on the same page.

Now I have a regular UIViewController in which I have added a containerView in which I have embedded a UIPageViewController.

Thus, the UIPageViewController acts as a child of the viewcontroller.

I have an X product details page. And instead of only showing one image, I want the image to be "swipeable" so that users can see different images. But I don't want the PageControllerView to work as a full size view, the image must still be visible inside the view manager at all times.

And now my problem is that I am stuck. I don't know how to connect two controllers. So if anyone has a sample app / tutorial on inline segues / child view using swift please share.

I worked out a solution, getting the answer below:

In my storyboard, I have 3 VCs. VC1 with container view and inline container that has a segue for the UIPageControllerView, then I added a third VC content controller to display images for the UIPageControllerView.

ViewController.swift

import UIKit

class ViewController: UIViewController, UIPageViewControllerDataSource {

    var pageViewController: UIPageViewController!
    var pageTitles: NSArray!
    var pageImages: NSArray!

    override func viewDidLoad()
    {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func restartAction(sender: AnyObject)
    {
        var startVC = self.viewControllerAtIndex(0) as ContentViewController
        var viewControllers = NSArray(object: startVC)

        self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
    }

    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.imageFile = self.pageImages[index] as! String
        vc.titleText = self.pageTitles[index] as! String
        vc.pageIndex = index

        return vc


    }


    // MARK: - Page View Controller Data Source

    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
    }



    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "PageViewControllerFitta" {
            self.pageTitles = NSArray(objects: "Explore", "Today Widget")
            self.pageImages = NSArray(objects: "page1", "page2")

            let toView = segue.destinationViewController as! UIPageViewController
            toView.dataSource = self


            var startVC = self.viewControllerAtIndex(0) as ContentViewController
            var viewControllers = NSArray(object: startVC)

            toView.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)

            toView.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)

            self.addChildViewController(toView)
            self.view.addSubview(toView.view)
            toView.didMoveToParentViewController(self)
        }
    }


}

      

ContentViewController.Swift

import UIKit

class ContentViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    var pageIndex: Int!
    var imageFile: String!



    override func viewDidLoad()
    {
        super.viewDidLoad()

        self.imageView.image = UIImage(named: self.imageFile)


    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }



}

      

+3


source to share


1 answer


Embedding is, in fact, a kind of segue that takes place as soon as the created contained controller is created, so the way to do this is to implement prepareForSegue

on your containing controller and capture the reference to your child controller in this method.Here is an example adapted from the project, above which I'm currently working, it probably won't compile, but should give you an idea of โ€‹โ€‹how to proceed. Let me know if you need more information.

For this to work, you must provide a segue a property identifier

. Assuming you are using storyboards, this is just a property of the segment itself (the little circular transition icon found on the arrow connecting the parent and child UIViewControllers).

class ViewController: UIViewController { // <-- this is the container

    var myEmbeddedViewController: MyCustomUIViewController?

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        switch segue.identifier {
        case .Some("the-identifier-i-used-for-the-segue"):
            myEmbeddedViewController = segue.destinationViewController as? MyCustomUIViewController
        default:
            break
        }
    }

    // ... other UIViewController stuff...
}

      

Here's another answer that goes for some of the details and documentation, albeit presented in Objective-C rather than Swift: iOS Internally set view controllers in a UIViewController view?


Now that you have a link to yours UIPageControllerView

, it needs a way to answer some questions for display purposes: how many pages are there, which page we open, which page I will show when the user goes through the next and previous pages. It does this with a generic UIKit / Cocoa idiom dataSource

(many view controllers have this property).

In the code example you are linking to, the method viewDidLoad

contains a string self.pageViewController.dataSource = self

. This works because the author stated that his ViewController

, besides the extension UIViewController

, also conforms to the protocol UIPageViewControllerDataSource

. Then it implements the required methods:



func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int

      

See: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIPageViewControllerDataSourceProtocolRef/index.html

( dataSource

doesn't have to be yours ViewController

, you can create a separate class or struct. For simple examples, this is probably great.)

The specific challenge for you is to implement these methods based on your application requirements so that the built-in UIPageViewController

can figure out what to display from what you installed as it dataSource

.

Let's ProductImageViewController

say you want it to render , which just takes an image and a label. In theory, you could create storyboards for each product, but it really won't work unless you have a small number of products that never change. Chances are, you want to create new instances of your code based on your product list.

In the sample code, the author simply sets up a static array of labels and images, and then the methods pageViewController

simply grab the appropriate one from the array based on the current position and scroll direction. You want to set up a data source that knows about your product listing, which you supposedly get from some dynamic source like CoreData, CloudKit, or some other web service.

As a simple experiment to educate, try to change the demo variables self.pageTitles

and self.pageImages

to see how this changes the behavior of the embedded controller. Try changing this code so that instead of using two arrays, one of the text and one of the image file names, you use a single array containing the specified structure or class that contains both the shortcut text and the file name. After that, change so that instead of hardcoding the tag / image list, you download it from the product catalog. And that it works, you're done, post it :)

+3


source







All Articles