My GameScene Freezes when i dismiss modally presented viewController - ios

I am working on SpriteKit Game. From within GameScene i want to represent another viewController. I have done it correctly, but when i dismiss Modally presented viewController, GameScene on my Root viewController freezes. Here is my code on GameScene....
NotificationCenter.default.post(name: NSNotification.Name(rawValue:"present"), object:nil)
Here is my code for root viewController(GameViewController)
override func viewDidLoad() {
super.viewDidLoad()
let gameScene = GameScene()
let skView = self.view as! SKView
skView.showsFPS = true
skView.ignoresSiblingOrder = true
let size = CGSize(width:590, height:390)
/* Set the scale mode to scale to fit the window */
//menuScene.scaleMode = .aspectFit
// menuScene.anchorPoint = CGPoint(x:0, y:0)
//skView.allowsTransparency = true
//size our scene to fit the view exactly:
// let rect1 = CGRect(origin:point, size:size)
//skView.setNeedsUpdateConstraints()
gameScene.size = CGSize(width:size.width, height:size.height)
gameScene.scaleMode = .aspectFill
skView.presentScene(gameScene)
skView.translatesAutoresizingMaskIntoConstraints = false
skView.ignoresSiblingOrder = true
skView.showsFPS = true
skView.showsNodeCount = true
NotificationCenter.default.addObserver(self, selector: #selector(self.presentVC), name: NSNotification.Name(rawValue:"present"), object: nil)
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
func presentVC(){
let toViewController = viewController() as UIViewController
toViewController.modalPresentationStyle = .overFullScreen
self.present(toViewController, animated: true, completion: nil)
toViewController.transitioningDelegate = self
print("presenting next")
}
And Here is the code for another viewController that is to be represented modally over rootViewController:
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(image:UIImage(named:"Background1"))
imageView.frame = CGRect(x:0, y:0, width:1000, height:800)
let aButton = UIButton()
aButton.frame = CGRect(x:view.frame.width/2 - 50, y:view.frame.height/2 - 50, width:100, height:100)
aButton.setTitle("aButton", for: .normal)
aButton.backgroundColor = .green
aButton.addTarget(self, action: #selector(pressed), for: .touchUpInside)
aButton.isEnabled = true
view.addSubview(imageView)
view.addSubview(aButton)
}
func pressed(){
dismissVC()}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
func dismissVC(){
self.dismiss(animated: true, completion: nil)
print("returning previous")
}

Here is the answer with explanation. First of all it was not GameScene in frozen state. But it was view of another viewController that was present even after completion of activity on modally presented viewController. Presence of view of another viewController was due to nonExecution of viewController dismiss code inside that viewController. So presence of another viewController view was not allowing me to interact with my app. Now Solution is, In above Posted question, In GameViewController code i have added presentVC() function. Inside that instead of line:
self.present(toViewController, animated: true, completion: nil)
Use following line:
self.show(toViewController, sender:nil)

Related

Is there a way to have interactive modal dismissal for a .fullscreen modally presented view controller?

I want to enable interactive modal dismissal that pans along with a users finger on a fullscreen modally presented view controller .fullscreen.
I've seen that it's fairly trivial to do so on the .pageSheet and the .formSheet which have it built in but have not seen a clear example for the full screen.
I'm guessing I'd need to have a pan gesture added to my vc within the body of it's code and then adjust for the states myself but wondering if anyone knows what exactly needs to be done / if there's a simpler way to do it as it seems much more complicated for the .fullscreen case
It can be done with creating your custom UIPresentationController and UIViewControllerTransitioningDelegate. Lets say we have TestViewController and we want to present SecondViewController with total presentedHeight of 1.0 (fullScreen). Presentation will be triggered with #IBAction func buttonPressed and can be dismissed by dragging controller down (as we are used to it). It would be also nice to add some backgroundEffect to be gradually changed while user is sliding down the SecondViewController (especially when used only presentedHeight of 0.6).
Firstly we define OverlayViewController which will be later superclass of presented SecondViewControllerand will contain UIPanGestureRecognizer.
class OverlayViewController: UIViewController {
var hasSetPointOrigin = false
var pointOrigin: CGPoint?
var delegate: OverlayViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction))
view.addGestureRecognizer(panGesture)
}
override func viewDidLayoutSubviews() {
if !hasSetPointOrigin {
hasSetPointOrigin = true
pointOrigin = self.view.frame.origin
}
}
#objc func panGestureRecognizerAction(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
// Not allowing the user to drag the view upward
guard translation.y >= 0 else { return }
let currentPosition = translation.y
let originPos = self.pointOrigin
delegate?.userDragged(draggedPercentage: translation.y/originPos!.y)
// setting x as 0 because we don't want users to move the frame side ways!! Only want straight up or down
view.frame.origin = CGPoint(x: 0, y: self.pointOrigin!.y + translation.y)
if sender.state == .ended {
let dragVelocity = sender.velocity(in: view)
if dragVelocity.y >= 1100 {
self.dismiss(animated: true, completion: nil)
} else {
// Set back to original position of the view controller
UIView.animate(withDuration: 0.3) {
self.view.frame.origin = self.pointOrigin ?? CGPoint(x: 0, y: 400)
self.delegate?.animateBlurBack(seconds: 0.3)
}
}
}
}
}
protocol OverlayViewDelegate: AnyObject {
func userDragged(draggedPercentage: CGFloat)
func animateBlurBack(seconds: TimeInterval)
}
Next we define custom PresentationController
class PresentationController: UIPresentationController {
private var backgroundEffectView: UIView?
private var backgroundEffect: BackgroundEffect?
private var viewHeight: CGFloat?
private let maxDim:CGFloat = 0.6
private var tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer()
convenience init(presentedViewController: UIViewController,
presenting presentingViewController: UIViewController?,
backgroundEffect: BackgroundEffect = .blur,
viewHeight: CGFloat = 0.6)
{
self.init(presentedViewController: presentedViewController, presenting: presentingViewController)
self.backgroundEffect = backgroundEffect
self.backgroundEffectView = returnCorrectEffectView(backgroundEffect)
self.tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissController))
self.backgroundEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.backgroundEffectView?.isUserInteractionEnabled = true
self.backgroundEffectView?.addGestureRecognizer(tapGestureRecognizer)
self.viewHeight = viewHeight
}
private override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
}
override var frameOfPresentedViewInContainerView: CGRect {
CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height * (1-viewHeight!)),
size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height *
viewHeight!))
}
override func presentationTransitionWillBegin() {
self.backgroundEffectView?.alpha = 0
self.containerView?.addSubview(backgroundEffectView!)
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
switch self.backgroundEffect! {
case .blur:
self.backgroundEffectView?.alpha = 1
case .dim:
self.backgroundEffectView?.alpha = self.maxDim
case .none:
self.backgroundEffectView?.alpha = 0
}
}, completion: { (UIViewControllerTransitionCoordinatorContext) in })
}
override func dismissalTransitionWillBegin() {
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
self.backgroundEffectView?.alpha = 0
}, completion: { (UIViewControllerTransitionCoordinatorContext) in
self.backgroundEffectView?.removeFromSuperview()
})
}
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
}
override func containerViewDidLayoutSubviews() {
super.containerViewDidLayoutSubviews()
presentedView?.frame = frameOfPresentedViewInContainerView
backgroundEffectView?.frame = containerView!.bounds
}
#objc func dismissController(){
self.presentedViewController.dismiss(animated: true, completion: nil)
}
func graduallyChangeOpacity(withPercentage: CGFloat) {
self.backgroundEffectView?.alpha = withPercentage
}
func returnCorrectEffectView(_ effect: BackgroundEffect) -> UIView {
switch effect {
case .blur:
var blurEffect = UIBlurEffect(style: .dark)
if self.traitCollection.userInterfaceStyle == .dark {
blurEffect = UIBlurEffect(style: .light)
}
return UIVisualEffectView(effect: blurEffect)
case .dim:
var dimView = UIView()
dimView.backgroundColor = .black
if self.traitCollection.userInterfaceStyle == .dark {
dimView.backgroundColor = .gray
}
dimView.alpha = maxDim
return dimView
case .none:
let clearView = UIView()
clearView.backgroundColor = .clear
return clearView
}
}
}
extension PresentationController: OverlayViewDelegate {
func userDragged(draggedPercentage: CGFloat) {
graduallyChangeOpacity(withPercentage: 1-draggedPercentage)
switch self.backgroundEffect! {
case .blur:
graduallyChangeOpacity(withPercentage: 1-draggedPercentage)
case .dim:
graduallyChangeOpacity(withPercentage: maxDim-draggedPercentage)
case .none:
self.backgroundEffectView?.alpha = 0
}
}
func animateBlurBack(seconds: TimeInterval) {
UIView.animate(withDuration: seconds) {
switch self.backgroundEffect! {
case .blur:
self.backgroundEffectView?.alpha = 1
case .dim:
self.backgroundEffectView?.alpha = self.maxDim
case .none:
self.backgroundEffectView?.alpha = 0
}
}
}
}
enum BackgroundEffect {
case blur
case dim
case none
}
Create SecondViewController subclassing OverlayViewController:
class SecondViewController: OverlayViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
addSlider()
}
func addSlider() {
let sliderWidth:CGFloat = 100
let centerOfScreen = self.view.frame.size.width / 2
let rect = CGRect(x: centerOfScreen - sliderWidth/2, y: 80, width: sliderWidth, height: 10)
let slider = UIView(frame: rect)
slider.backgroundColor = .black
self.view.addSubview(slider)
}
Add showOverlay() function that will be triggered after buttonPressed and conform your presenting UIViewController (TestViewController) to UIViewControllerTransitioningDelegate :
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func buttonPressed(_ sender: Any) {
showOverlay()
}
func showOverlay() {
let secondVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondVC") as! SecondViewController
secondVC.modalPresentationStyle = .custom
secondVC.transitioningDelegate = self
self.present(secondVC, animated: true, completion: nil)
}
}
extension TestViewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController) -> UIPresentationController?
{
let presentedHeight: CGFloat = 1.0
let controller = PresentationController(presentedViewController: presented,
presenting: presenting,
backgroundEffect: .dim,
viewHeight: presentedHeight)
if let vc = presented as? OverlayViewController {
vc.delegate = controller
}
return controller
}
}
Now we should be able to present SecondViewController with showOverlay() function setting its presentedHeight to 1.0 and .dim background effect. We can dismiss SecondViewController similar to another modal presentations.

Presenting a UIViewController from an embedded ViewController keeping its bounds

I try to present a ViewController modally from a parent ViewController that's already embedded in a UIView. But whenever I try presenting it, it just pops over the entire window. I want it to be equally sized as its parent. I do not need an animation for the transition. The only thing that worked so far is to again add it's view manually and manipulate the constraints via auto layout. Therefore I would present the controller by hiding its corresponding view. But compared to present that seems more like a workaround than a solution. So far I tried some options on modalPresentationStyle, but none of them seem to work for me. Glad for any help.
Cheers!
class ViewController: UIViewController {
lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
lazy var viewController: ParentViewController = {
let viewController = ParentViewController()
return viewController
}()
override func viewDidLoad() {
super.viewDidLoad()
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75).isActive = true
containerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.75).isActive = true
viewController.view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(viewController.view)
viewController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
viewController.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
viewController.view.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
viewController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
viewController.didMove(toParent: self)
}
}
class ParentViewController: UIViewController {
#IBAction func buttonPressed(_ button: UIButton) {
let childController = UIViewController() // This View Controller should fit it´s parents size. Instead it´s just presented normally and fits the window
present(childController, animated: false)
}
}
Edit
Derived from the community answers I implemented following solution:
extension UIViewController {
func open(_ viewControllerToPresent: UIViewController, in view: UIView? = nil, animated: Bool, duration: TimeInterval = 0.25, completion: (() -> Void)? = nil) {
viewControllerToPresent.view.isHidden = true
open(viewControllerToPresent, in: view)
if animated {
viewControllerToPresent.view.alpha = 0.0
viewControllerToPresent.view.isHidden = false
UIView.animate(withDuration: duration) {
viewControllerToPresent.view.alpha = 1.0
} completion: { (_) in
completion?()
}
}
else {
viewControllerToPresent.view.isHidden = false
completion?()
}
}
func open(_ viewControllerToPresent: UIViewController, in view: UIView? = nil) {
let parentView = view ?? self.view!
addChild(viewControllerToPresent)
viewControllerToPresent.view.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(viewControllerToPresent.view)
viewControllerToPresent.view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor).isActive = true
viewControllerToPresent.view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor).isActive = true
viewControllerToPresent.view.topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true
viewControllerToPresent.view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor).isActive = true
viewControllerToPresent.didMove(toParent: self)
}
func close(animated: Bool, duration: TimeInterval = 0.25, completion: (() -> Void)? = nil) {
if animated {
UIView.animate(withDuration: duration) {
self.view.alpha = 0.0
} completion: { (_) in
self.close()
completion?()
}
}
else {
close()
completion?()
}
}
func close() {
view.removeFromSuperview()
willMove(toParent: nil)
removeFromParent()
}
}

Push QLPreviewController and set Translucent to false?

I am unable to disable the translucent property of my QLPreviewController. What i have already tried:
let preview = SideQLPreviewController()
preview.navigationController?.navigationBar.isTranslucent = false //before
self.navigationController?.pushViewController(preview, animated: false)
preview.navigationController?.navigationBar.isTranslucent = false //after
self.navigationController?.navigationBar.isTranslucent = false
And already tried to subclass and set:
class SideQLPreviewController: QLPreviewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isTranslucent = false
// Do any additional setup after loading the view.
}
But still no success - any ideas?
if you present the QLPreviewController there is no navigationcontroller at all. something like this could work though:
class PreviewController: QLPreviewController {
var navigationBar: UINavigationBar? {
return view.recursiveSubviews.filter({ $0 is UINavigationBar }).first as? UINavigationBar
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationBar?.isTranslucent = false
}
}
extension UIView {
var recursiveSubviews: [UIView] {
var recursiveSubviews: [UIView] = []
for subview in subviews {
recursiveSubviews.append(subview)
recursiveSubviews.append(contentsOf: subview.recursiveSubviews)
}
return recursiveSubviews
}
}
You can do it in the viewDidLayoutSubviews of your subclass, this worked for me.
class PreviewController: QLPreviewController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
navigationController?.navigationBar.isTranslucent = false
}
}
Then just instantiate the new class
let previewController = PreviewController()//QLPreviewController()
previewController.dataSource = self
navigationController?.pushViewController(previewController, animated: true)

Scene Transition fails after presenting from a different ViewController

I have a GameViewController, which transitions between different scenes, for example: CircleScene to SquareScene, back & forth with Transitions.
All of the above logic works fine, as long as GameViewController is "initial view controller".
Once I have one other view controller, above the GameViewController, the screen transitions for GameViewController doesn't work anymore.
GameViewController.swift
import UIKit
import SpriteKit
import GameplayKit
protocol ScreenManager {
func gotoSquare()
func gotoCircle()
}
class GameViewController: UIViewController, ScreenManager {
let sceneHeight = 380
let sceneWidth = 570
func gotoCircle() {
let transition = SKTransition.push(with: .right, duration: 1.0)
let newScene = CircleScene(size: CGSize(width: sceneWidth, height: sceneHeight))
newScene.screenManager = self
newScene.scaleMode = .aspectFill
let view = self.view as! SKView?
view?.presentScene(newScene, transition: transition)
}
func gotoSquare() {
let transition = SKTransition.push(with: .left, duration: 1.0)
let newScene = SquareScene(size: CGSize(width: sceneWidth, height: sceneHeight))
newScene.screenManager = self
newScene.scaleMode = .aspectFill
let view = self.view as! SKView?
view?.presentScene(newScene, transition: transition)
}
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let scene = CircleScene(size: CGSize(width: sceneWidth, height: sceneHeight))
scene.screenManager = self
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
MainViewContoller.swift
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPressed(_ sender: AnyObject) {
// Go to GameViewController
let gameViewController:GameViewController = storyboard?.instantiateViewController(withIdentifier: "GameViewControllerID") as! GameViewController
self.present(gameViewController, animated:false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I have tried ViewController methods (present/show) unsuccessfully. Is there anything wrong with ViewController presentation that causes scene transitions to fail? Appreciate your help.

Container view, Failed use delegate

i wanna designed slide function.So i use container view in storyboard without any segue.there is a button in centerView to open or close leftView.but this function is written in container.swift and i don't know how to set delegate..please help me,thanks.(All codes as below)
Container.swift
import UIKit
class ContainerViewController: UIViewController,test {
var leftViewController: UIViewController? {
willSet{
if self.leftViewController != nil {
if self.leftViewController!.view != nil {
self.leftViewController!.view!.removeFromSuperview()
}
self.leftViewController!.removeFromParentViewController()
}
}
didSet{
self.view!.addSubview(self.leftViewController!.view)
self.addChildViewController(self.leftViewController!)
}
}
var rightViewController: UIViewController? {
willSet {
if self.rightViewController != nil {
if self.rightViewController!.view != nil {
self.rightViewController!.view!.removeFromSuperview()
}
self.rightViewController!.removeFromParentViewController()
}
}
didSet{
self.view!.addSubview(self.rightViewController!.view)
self.addChildViewController(self.rightViewController!)
}
}
var menuShown: Bool = false
func showMenu() {
print(__FUNCTION__,__LINE__)
UIView.animateWithDuration(0.3, animations: {
self.rightViewController!.view.frame = CGRect(x: self.view.frame.origin.x + 235, y: self.view.frame.origin.y, width: self.view.frame.width, height: self.view.frame.height)
}, completion: { (Bool) -> Void in
self.menuShown = true
})
}
func hideMenu() {
UIView.animateWithDuration(0.3, animations: {
self.rightViewController!.view.frame = CGRect(x: 0, y: self.view.frame.origin.y, width: self.view.frame.width, height: self.view.frame.height)
}, completion: { (Bool) -> Void in
self.menuShown = false
})
}
var centerView:CenterViewController!
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let CenterNC:UINavigationController = storyboard.instantiateViewControllerWithIdentifier("CenterNC") as! UINavigationController
let leftVC:LeftViewController = storyboard.instantiateViewControllerWithIdentifier("LeftVC") as! LeftViewController
centerView = storyboard.instantiateViewControllerWithIdentifier("CenterVC") as! CenterViewController
self.leftViewController = leftVC
self.rightViewController = CenterNC
self.centerView.delegate = self
// Do any additional setup after loading the view.
}
}
CenterViewController.swift
import UIKit
protocol test{
func showMenu()
func hideMenu()
}
class CenterViewController: UIViewController {
var delegate:test! = nil
#IBAction func pressed(sender: AnyObject) {
if (delegate != nil) {
delegate.showMenu()
}
else{
print("Error")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Debug ContainerView.swift
Debug CenterViewController.swift
Usually you use whatever.delegate = self but I'm not quite sure what you want to do. (Also whatever.datasource = self). I dont know if this works for VCs.
As far as I know it is bad practice to have one UIViewController in the storyboard that contains two other VCs.
Rather you should either transition programmatically or with a storyboard segue.
self.presentViewController(LeftViewController(), animated: true, completion: nil)
self.dismissViewController(true, completion: nil)
View Controllers are automatically removed from memory (look up ARC if your curious) (as long as you don't have variables in the VC to be removed that are (or can be) accessed from another class that wasn't deinitialized - if that's confusing ignore it)
If you want a sliding animation or something like this, it's probably best to find something on GitHub to clone and import into your project. I've made an app with a tab bar that slides between views with some code I found online.
I've been droning but having two VCs in one VC is terrible for memory and in general bad practice. Utilize Custom Segues instead.

Resources