0 Replies
      Latest reply on Apr 9, 2019 3:43 PM by tiletechnology
      tiletechnology Level 1 Level 1 (0 points)

        I am attempting to create a UIScrollView with infinitely many horizontally scrolling pages. I plan to use instances of several reusable cached ViewControllers to fill the pages. To achieve this I am trying to create a similar effect to UIPageViewController by using a beforeViewController a currentViewController and an afterViewController to fill the the three positions as the user scrolls so there is always one viewController before and after the current one the user is looking at to scroll to.

        I would like to keep setting these instances equal to varying viewControllers from my cache but as I scroll back and forth eventually I am left with an empty ViewController. I believe it is caused by the passed by reference nature of the ViewController and setting one equal to the other.

        Below is the code I have created is there any way to improve the functionality. Thank you in advance for your help.

         

         

        import UIKit
           
            class ViewController: UIViewController,UIScrollViewDelegate {
           
                var previousOffset:CGFloat = 0
               
                var viewControllers:[UIViewController] = [UIViewController](){
                    didSet{
                        print("viewControllers.count: \(viewControllers.count)")
                    }
                }
               
                var beforeViewController:UIViewController?
                var currentViewController:UIViewController?
                var nextViewController:UIViewController?
               
                var scrollView:UIScrollView = {
                    let scrollView = UIScrollView()
                    scrollView.isPagingEnabled = true
                    scrollView.backgroundColor = UIColor.lightGray
                    scrollView.translatesAutoresizingMaskIntoConstraints = false
                    return scrollView
                }()
               
                override func viewDidLoad() {
                    super.viewDidLoad()
                    // Do any additional setup after loading the view.
                    scrollView.delegate = self
                    //3 Pages
                    scrollView.contentSize = CGSize(width: 3 * self.view.bounds.width, height: self.view.bounds.height)
                   
                    self.view.addSubview(scrollView)
                   
                    NSLayoutConstraint.activate([
                        scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
                        scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
                        scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0),
                        scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
                        ])
                   
                    currentViewController = getViewController()
                    addViewController(viewController: currentViewController!, index: 0) { (view) in
                        view.backgroundColor = UIColor.green
                    }
                   
                    nextViewController = getViewController()
                    addViewController(viewController: nextViewController!, index: 1) { (view) in
                        view.backgroundColor = UIColor.purple
                    }
                   
                }
               
                func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
                    let currentPage = scrollView.contentOffset.x/scrollView.bounds.width
                    print("current page: \(currentPage)")
                    if scrollView.contentOffset.x > previousOffset{
                        print("User scrolled Forward")
                        scrolledForwards(currentPage: Int(currentPage))
                    }else if scrollView.contentOffset.x < previousOffset{
                        print("User scrolled backwards")
                        scrolledBackwards(currentPage: Int(currentPage))
                    }
                    previousOffset = scrollView.contentOffset.x
                }
           
                func getViewController()->UIViewController{
                    let unusedViewControllers:[UIViewController] = self.viewControllers.filter({return $0.parent == nil})
                    if let unusedViewController = unusedViewControllers.first{
                        print("reusing viewController: \(viewControllers.count)")
                        print("reusing viewController: \(unusedViewController.description)")
                        return unusedViewController
                    }else{
                        let newViewController = UIViewController()
                        self.viewControllers.append(newViewController)
                        print("creating new viewController")
                        return newViewController
                    }
                }
               
                func addViewController(viewController:UIViewController,index:Int, completion: ((UIView)->Void)? = nil){
                    self.willMove(toParent: viewController)
                    self.addChild(viewController)
                    guard let view = viewController.view else{
                        removeViewController(viewController: viewController)
                        fatalError("view controller sent without a view")
                    }
                    self.scrollView.addSubview(view)
                    viewController.didMove(toParent: self)
                   
                    let offset = self.view.bounds.width * CGFloat(index)
                   
                    view.translatesAutoresizingMaskIntoConstraints = false
             
                    NSLayoutConstraint.activate([
                        view.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0),
                        view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: offset),
                        view.heightAnchor.constraint(equalTo: scrollView.heightAnchor, constant: 0),
                        view.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: 0)
                        ])
                   
                    if let completion = completion{
                     completion(view)
                    }
                   
                }
               
                func removeViewController(viewController:UIViewController?, completion: ((UIView)->Void)? = nil){
                    viewController?.willMove(toParent: nil)
                    viewController?.view.removeFromSuperview()
                    viewController?.removeFromParent()
                   
                   
                    if let completion = completion{
                        completion(view)
                    }
                   
                }
           
               
                func scrolledForwards(currentPage:Int = 0){
                    removeViewController(viewController: beforeViewController)
                    let index = currentPage + 1
                    self.beforeViewController = self.currentViewController
                    self.currentViewController = self.nextViewController
                    print("index: \(index)")
                    if index > 2{
                        print("There is no more forwards")
                        return
                    }
                    self.nextViewController = getViewController()
                   
                            if nextViewController?.parent == nil{
                                let index = currentPage + 1
                                self.addViewController(viewController: nextViewController!, index: index) { (view) in
                                    view.backgroundColor = .magenta
                                }
                            }
             
           
                }
               
                func scrolledBackwards(currentPage:Int = 0){
           
                    let index = currentPage - 1
                   
                    removeViewController(viewController: nextViewController)
                    nextViewController = currentViewController
                    currentViewController = beforeViewController
                    print("index: \(index)")
                    if index < 0{
                        print("There is no more backwards")
                        return
                    }
                    beforeViewController = getViewController()
           
                    currentViewController?.view.backgroundColor = UIColor.brown
                   
                    if beforeViewController?.parent == nil{
                        self.addViewController(viewController: beforeViewController!, index: index) { (view) in
                            view.backgroundColor = .cyan
                        }
                    }
                   
                }
               
               
           
            }