7

I am working on an iOS application which interacts with an API using alamofire and swiftyJSON. In this api I get a bunch of values returned and one of them is images. the Images returned comes in form of an array but i am able to iterate through the array and display a single image. To make the application more advanced, I decided to implement UIPageViewController class in my application. I called the API function and tried to pass my images into view controllers.

List of things in my storyboard

  1. A UIPageView
  2. A UiViewController with a UIImage embeded into it.

I have my codes fine to the best of my knowledge but when I run the application nothing happens on in the console. Below is my codes.

protocol ProductImagesPageViewControllerDelegate: class {
    func setupPageController(numberOfPages: Int)
    func turnPageController(to index: Int)
}

class ProductPageVC: UIPageViewController {

    weak var  pageViewControllerDelegate: ProductImagesPageViewControllerDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()

        automaticallyAdjustsScrollViewInsets = false
        dataSource = self
        delegate = self

    }


    func configureImg() {
        let productId = ProductServices.instance.selectedProduct?.id
        ProductServices.instance.findIndividualProducts(id: productId!, completion: { (success) in

            if success {
                ProductServices.instance.productDetails.forEach({ (productDetail) in

                    let images = productDetail.productImg.count

                    if images > 0 {

                        for image in 0..<(images) {

                            let imageVC = self.storyboard?.instantiateViewController(withIdentifier: PRODUCT_IMAGE_VC)
                            self.controllers.append(imageVC!)

                        }
                    }

                })
            }
        })
    }

    lazy var controllers: [UIViewController] = {

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        var controllers = [UIViewController]()


        let productId = ProductServices.instance.selectedProduct?.id
        ProductServices.instance.findIndividualProducts(id: productId!, completion: { (success) in

            if success {
                ProductServices.instance.productDetails.forEach({ (productDetail) in

                    let images = productDetail.productImg.count

                    if images > 0 {

                        for image in 0..<(images) {

                            let imageVC = storyboard.instantiateViewController(withIdentifier: PRODUCT_IMAGE_VC)
                            controllers.append(imageVC)

                        }
                    }

                })
            }
        })



        self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)

        return controllers
    }()



    func turnToPage(index: Int)
    {
        let controller = controllers[index]
        var direction = UIPageViewControllerNavigationDirection.forward

        if let currentVC = viewControllers?.first {
            let currentIndex = controllers.index(of: currentVC)!
            if currentIndex > index {
                direction = .reverse
            }
        }

        self.configureDisplaying(viewController: controller)

        setViewControllers([controller], direction: direction, animated: true, completion: nil)
    }

    func configureDisplaying(viewController: UIViewController)
    {
        for (index, vc) in controllers.enumerated() {
            if viewController === vc {
                if let imageVC = viewController as? ImagesVC {

                    imageVC.configureImage(productIndex: index)

                    self.pageViewControllerDelegate?.turnPageController(to: index)
                }
            }
        }
    }
}
// MARK: - UIPageViewControllerDataSource

extension ProductPageVC : UIPageViewControllerDataSource
{
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
    {
        if let index = controllers.index(of: viewController) {
            if index > 0 {
                return controllers[index-1]
            }
        }

        return controllers.last
    }

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

        if let index = controllers.index(of: viewController) {
            if index < controllers.count - 1 {
                return controllers[index + 1]
            }
        }

        return controllers.first
    }
}

extension ProductPageVC : UIPageViewControllerDelegate {

    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController])
    {
        self.configureDisplaying(viewController: pendingViewControllers.first as! ImagesVC)
    }

    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
    {
        if !completed {
            self.configureDisplaying(viewController: previousViewControllers.first as! ImagesVC)
        }
    }
}

This is my ImageVC code

class ImagesVC: UIViewController {

    @IBOutlet weak var imageView: UIImageView!


    func configureImage(productIndex : Int) -> Void {

        ProductServices.instance.productDetails.forEach { (productImg) in

            if productImg.productImg.count > 0 {
                imageView.sd_setImage(with: URL(string: productImg.productImg[productIndex]!) )
            } else {
                imageView.image = UIImage(named: "image_not_found")
            }
        }

    }}

This is my HeaderImageView code

class HeaderImageVC: UIView {

    @IBOutlet weak var pageControl: UIPageControl!
}

extension HeaderImageVC : ProductImagesPageViewControllerDelegate {
    func setupPageController(numberOfPages: Int)
    {
        pageControl.numberOfPages = numberOfPages
    }

    func turnPageController(to index: Int)
    {
        pageControl.currentPage = index
    }
}

I have no idea why the code is not working but further codes would be supplied on request. Thanks.

According to suggestion

var images = (ProductServices.instance.productDetails.first?.productImg) 

    lazy var controllers: [UIViewController] = {

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        var controllers = [UIViewController]()

        if let images = self.images {
            for image in images {
                let imageVC = storyboard.instantiateViewController(withIdentifier: PRODUCT_IMAGE_VC)
                controllers.append(imageVC)
            }
        }



        self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)

        return controllers
    }()

this still returns empty

King
  • 1,885
  • 3
  • 27
  • 84

1 Answers1

1

problem

lazy var controllers: [UIViewController] = {

    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    var controllers = [UIViewController]()

    // IT WILL NOT WAIT TO FINISH IT
    let productId = ProductServices.instance.selectedProduct?.id
    ProductServices.instance.findIndividualProducts(id: productId!, completion: { (success) in

        if success {
            ProductServices.instance.productDetails.forEach({ (productDetail) in

               // NO USE OF productDetail as it is already returned 

                let images = productDetail.productImg.count

                if images > 0 {

                    for image in 0..<(images) {

                        let imageVC = storyboard.instantiateViewController(withIdentifier: PRODUCT_IMAGE_VC)
                        controllers.append(imageVC)

                    }
                }

            })
        }
    })


    // controllers.count is 0

    self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)

    return controllers
}()

if you observe above code and you should debug it, then you will find that controllers is empty Because it is async task and compiler will not wait for finish findIndividualProducts task to complete it will return the empty array which you have define so outcome is your controller has nothing to show !! -> Make sense

Solution is that simple you can create method that will set the call the API findIndividualProducts and assign it to the controllers now the controller is simple plain property.

Hope it is helpful

EDIT

First create one method and call it from view did load or view will appear whatever suits you

 private func getAllControllers () {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    var controllers = [UIViewController]()

    // IT WILL NOT WAIT TO FINISH IT
    let productId = ProductServices.instance.selectedProduct?.id
    ProductServices.instance.findIndividualProducts(id: productId!, completion: { (success) in

        if success {
            ProductServices.instance.productDetails.forEach({ (productDetail) in

               // NO USE OF productDetail as it is already returned 

                let images = productDetail.productImg.count

                if images > 0 {

                    for image in 0..<(images) {

                        let imageVC = storyboard.instantiateViewController(withIdentifier: PRODUCT_IMAGE_VC)
                        controllers.append(imageVC)

                    }


  self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)
  self.controllers = controllers
                }

            })
        }
    })

}

and remove lazy block from your controller property

var controllers: [UIViewController] = []

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
  • sorry can I dont quite understand. can you explain further? – King May 31 '18 at 20:57
  • @King in simple words your lazy `var controllers: [UIViewController] = {` has API Call now before the api response comes , the `return controllers` statement executes so your view controller is empty and you won't able to see anything Hope it is clear – Prashant Tukadiya Jun 01 '18 at 05:17
  • yes. but how do I now pass the images into the controller – King Jun 01 '18 at 09:19
  • @King You have to create one method, And put that code inside it and call it from viewDidLoad or viewwillappear whatever suits you!! , So when the api call completed you can call `self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)` and also assign `controllers` – Prashant Tukadiya Jun 01 '18 at 09:29
  • I have a function there saying `configureImg()` I deleted the API call int he controller but calling the `configureImg()` in viewDidLoad does nothing. returns blank pages too – King Jun 01 '18 at 09:44
  • @King after that change your code with `var controllers: [UIViewController] = []` – Prashant Tukadiya Jun 01 '18 at 09:45
  • If you don’t mind please edit your answer to reflect what you are saying. Thank you – King Jun 01 '18 at 09:46