Modularize Slide Out Container ViewController

时间:2017-08-14 01:44:09

标签: ios swift storyboard uikit

在关注精彩的Ray Wenderlich tutorial之后,我在我的应用程序中成功创建了一个Slide Out Container视图。

我想将此ContainerViewController合并到我的应用中的其他位置,因此我希望将其模块化为不将代码复制并粘贴到另一个ViewController中,但我感到茫然如何去做这件事。

以下是教程中的代码:

import UIKit
enum SlideOutState {
    case LeftPanelClosed
    case LeftPanelExpanded
}

class StudentContainerViewController: UIViewController {

    var centerNavigationController: UINavigationController!
    var centerViewController: SearchClassesTableViewController! // <--- Need to change this
    var currentState: SlideOutState = .LeftPanelClosed
    var leftViewController: StudentSettingsTableViewController? // <--- And this bad boy
    let centerPanelExpandedOffset: CGFloat = 60

    override func viewDidLoad() {
        super.viewDidLoad()
        centerViewController = UIStoryboard.centerViewController()
        centerViewController.delegate = self

        centerNavigationController = UINavigationController(rootViewController: centerViewController)
        view.addSubview(centerNavigationController.view)
        addChildViewController(centerNavigationController)

        centerNavigationController.didMove(toParentViewController: self)

        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)
    }
}

private extension UIStoryboard {
    class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: Bundle.main ) }

    class func leftViewController() -> StudentSettingsTableViewController? {
        return mainStoryboard().instantiateViewController(withIdentifier: "StudentSettingsTableViewController") as? StudentSettingsTableViewController
    }

    class func centerViewController() -> SearchClassesTableViewController? {
        return mainStoryboard().instantiateViewController(withIdentifier: "SearchClassesTableViewController") as? SearchClassesTableViewController
    }
}

extension StudentContainerViewController: CenterViewControllerDelegate {
    func toggleLeftPanel(){
        let notAlreadyExpanded = (currentState != .LeftPanelExpanded)
        if notAlreadyExpanded {
            addLeftPanelViewController()
        }
        animateLeftPanel(shouldExpand: notAlreadyExpanded)
    }

    func addLeftPanelViewController(){
        if leftViewController == nil {
            leftViewController = UIStoryboard.leftViewController()
            addChildSidePanelController(sidePanelController: leftViewController!)
        }
    }

    func addChildSidePanelController(sidePanelController: StudentSettingsTableViewController) {
        view.insertSubview(sidePanelController.view, at: 0)
        addChildViewController(sidePanelController)
        sidePanelController.didMove(toParentViewController: self)
    }

    func animateLeftPanel(shouldExpand: Bool){
        if shouldExpand {
            currentState = .LeftPanelExpanded
            animateCenterPanelXPosition(targetPosition: centerNavigationController.view.frame.width - centerPanelExpandedOffset)
        } else {
            animateCenterPanelXPosition(targetPosition: 0) {finished in
                self.currentState = .LeftPanelClosed
                self.leftViewController?.view.removeFromSuperview()
                self.leftViewController = nil
            }
        }
    }

    func animateCenterPanelXPosition(targetPosition: CGFloat, completion: ((Bool) -> Void)! = nil) {
        UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
            self.centerNavigationController.view.frame.origin.x = targetPosition
        }, completion: completion)
    }

    func showShadowForCenterViewController(shouldShowShadow: Bool) {
        if shouldShowShadow {
            centerNavigationController.view.layer.shadowOpacity = 0.8
        } else {
            centerNavigationController.view.layer.shadowOpacity = 0.0
        }
    }
}

// MARK: Gesture Recognizer

extension StudentContainerViewController {
    func handlePanGesture(_ recognizer: UIPanGestureRecognizer) -> Void {
        let gestureIsDraggingFromLeftToRight = recognizer.velocity(in: view).x > 0

        switch recognizer.state {
        case .began:
            if currentState == .LeftPanelClosed {
                if gestureIsDraggingFromLeftToRight {
                    addLeftPanelViewController()
                }
                showShadowForCenterViewController(shouldShowShadow: true)
            }
        case .changed:
            if gestureIsDraggingFromLeftToRight || currentState == .LeftPanelExpanded {
                recognizer.view!.center.x = recognizer.view!.center.x + recognizer.translation(in: view).x
                recognizer.setTranslation(CGPoint.zero, in: view)
            }
        case .ended:
            if leftViewController != nil {
                let hasMovedMoreThanHalfway = recognizer.view!.center.x > view.bounds.size.width
                animateLeftPanel(shouldExpand: hasMovedMoreThanHalfway)
            }
        default:
            break
        }
    }
}

我需要能够换出centerleftViewController属性。我创建了一个超类,然后在子类中设置centerleftViewControllers&#39; viewDidLoad s(见下文),但是这不起作用,应用程序在UIStoryboard类func(也显示在下面)mainStoryBoard的扩展名中突破了

超类:

class SlideOutContainerViewController: UIViewController {

    var centerNavigationController: UINavigationController!
    var centerViewController: UIViewController?
    var currentState: SlideOutState = .LeftPanelClosed
    var leftViewController: UIViewController?
    let centerPanelExpandedOffset: CGFloat = 60


...same code as above...
}

和子类:

class StudentSlideOutContainerViewController: SlideOutContainerViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    centerViewController = SearchClassesTableViewController()
    leftViewController = StudentSettingsTableViewController()

        centerViewController = UIStoryboard.centerViewController() as! SearchClassesTableViewController
//        centerViewController.delegate = self

    centerNavigationController = UINavigationController(rootViewController: centerViewController!)
    view.addSubview(centerNavigationController.view)
    addChildViewController(centerNavigationController)

    centerNavigationController.didMove(toParentViewController: self)

    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
    centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)

    }
}

这是堆栈跟踪:

img1

EXC_BAD_ACCESS打破的行:

img2

我知道它很多,而且很有可能解决任何问题,但StackOverflow经常有神奇的方法......

2 个答案:

答案 0 :(得分:1)

您的问题是您的Storyboard协议的实现,您正在递归地调用方法leftViewControllercenterViewController,要求在storyboard中实例化名称但是再次调用centerViewController并且递归循环

我认为您需要将这些属性定义为类属性,或者在协议中返回静态字符串值的方法,以避免递归调用

希望这有助于你

答案 1 :(得分:0)

在mainStoryboard中,您可以尝试覆盖子类中的mainStoryboard方法,而不是返回名为“Main”的UIStoryboard,以便从子类ViewController返回10x2吗?