ScrollView中的可重用ViewController通过引用传递

时间:2019-04-09 21:53:18

标签: ios swift memory-management uiviewcontroller uiscrollview

我正在尝试创建一个UIScrollView,其中包含无限多个水平滚动页面。我计划使用几个可重复使用的缓存ViewController的实例来填充页面。为了实现这一点,我试图通过在用户滚动时使用beforeViewControllercurrentViewControllerafterViewController来填充这三个位置,来创建与UIPageViewController类似的效果,因此总会有一个viewController用户正在滚动到当前滚动之前和之后。

我想继续将这些实例设置为与从缓存中更改的viewControllers相等,但是随着我来回滚动,最终我留下一个空的ViewController。我相信这是由ViewController的引用继承和设置一个等于另一个引起的。

下面是我创建的代码,有没有什么方法可以改善功能。预先感谢您的帮助。

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
            }
        }

    }



}

0 个答案:

没有答案