Skip to content

Instantly share code, notes, and snippets.

@cellularmitosis
Last active November 13, 2015 18:18
Show Gist options
  • Save cellularmitosis/56d734ab087a3f283455 to your computer and use it in GitHub Desktop.
Save cellularmitosis/56d734ab087a3f283455 to your computer and use it in GitHub Desktop.
Workaround for UIViewController containment bug
//
// ViewController.swift
// ContainerTimingBugDemo
//
// Created by Jason Pepas on 11/12/15.
// Copyright © 2015 Jason Pepas. All rights reserved.
//
/*
Instructions:
1. Create a new "Single View Application" Xcode project.
2. Replace the contents of ViewController.swift with this gist.
3. Uncomment the below test cases one-by-one.
*/
import UIKit
class ViewController: UIViewController
{
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
// self.presentViewController(VCTestCase1(), animated: true, completion: nil)
// self.presentViewController(VCTestCase2(), animated: true, completion: nil)
// self.presentViewController(VCTestCase3(), animated: true, completion: nil)
self.presentViewController(VCTestCase4(), animated: true, completion: nil)
// self.presentViewController(VCTestCase5(), animated: true, completion: nil)
// self.presentViewController(VCTestCase6(), animated: true, completion: nil)
}
}
extension UIViewController
{
func ensureViewHasLoaded()
{
let _ = view
}
}
extension UIViewController
{
func containChildViewController(childVC:UIViewController, inView containerView:UIView)
{
addChildViewController(childVC)
if shouldAutomaticallyForwardAppearanceMethods()
{
containerView.addSubview(childVC.view)
}
else
{
childVC.ensureViewHasLoaded()
childVC.beginAppearanceTransition(true, animated: false)
containerView.addSubview(childVC.view)
childVC.endAppearanceTransition()
}
childVC.didMoveToParentViewController(self)
containerView.bringSubviewToFront(childVC.view)
childVC.view.translatesAutoresizingMaskIntoConstraints = false
}
func containChildViewController(child:UIViewController)
{
self.containChildViewController(child, inView:view)
}
}
class VerboseVC: UIViewController
{
override func viewDidLoad()
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewDidAppear(animated)
}
override func viewWillDisappear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewWillDisappear(animated)
}
override func viewDidDisappear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewDidDisappear(animated)
}
}
class ChildVC: VerboseVC {}
class ViewLifeCycleTracker
{
enum State
{
case OffScreen
case TransitioningOnScreen
case OnScreen
case TransitioningOffScreen
}
var state: State = .OffScreen
func viewWillAppear()
{
state = .TransitioningOnScreen
}
func viewDidAppear()
{
state = .OnScreen
}
func viewWillDisappear()
{
state = .TransitioningOffScreen
}
func viewDidDisappear()
{
state = .OffScreen
}
}
class ViewControllerWithContainerLifeCycleBugWorkaround: UIViewController
{
let tracker = ViewLifeCycleTracker()
override func shouldAutomaticallyForwardAppearanceMethods() -> Bool
{
return !(tracker.state == .TransitioningOnScreen)
}
override func viewWillAppear(animated: Bool)
{
tracker.viewWillAppear()
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool)
{
tracker.viewDidAppear()
super.viewDidAppear(animated)
}
override func viewWillDisappear(animated: Bool)
{
tracker.viewWillDisappear()
super.viewWillDisappear(animated)
}
override func viewDidDisappear(animated: Bool)
{
tracker.viewDidDisappear()
super.viewDidDisappear(animated)
}
}
class VCTestCase: ViewControllerWithContainerLifeCycleBugWorkaround {}
class VerboseVCTestCase: VCTestCase
{
override func viewDidLoad()
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewDidAppear(animated)
}
override func viewWillDisappear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewWillDisappear(animated)
}
override func viewDidDisappear(animated: Bool)
{
debugPrint("\(self.dynamicType): \(__FUNCTION__)")
super.viewDidDisappear(animated)
}
}
class VCTestCase1: VerboseVCTestCase
{
override func viewDidLoad()
{
super.viewDidLoad()
// CORRECT: SubViewController will correctly receive viewWillAppear and viewDidAppear
containChildViewController(ChildVC())
}
}
class VCTestCase2: VerboseVCTestCase
{
override func viewWillAppear(animated: Bool)
{
// CORRECT: SubViewController will correctly receive viewWillAppear and viewDidAppear
self.containChildViewController(ChildVC())
super.viewWillAppear(animated)
}
}
class VCTestCase3: VerboseVCTestCase
{
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
// CORRECT: SubViewController will correctly receive viewWillAppear and viewDidAppear
self.containChildViewController(ChildVC())
}
}
class VCTestCase4: VerboseVCTestCase
{
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
dispatch_async(dispatch_get_main_queue()) { () -> Void in
// CORRECT: SubViewController will correctly receive viewWillAppear and viewDidAppear
// This is the case which is fixed by ViewControllerWithContainerLifeCycleBugWorkaround
self.containChildViewController(ChildVC())
}
}
}
class VCTestCase5: VerboseVCTestCase
{
override func viewDidAppear(animated: Bool)
{
// CORRECT: SubViewController will correctly receive viewWillAppear and viewDidAppear
self.containChildViewController(ChildVC())
super.viewDidAppear(animated)
}
}
class VCTestCase6: VerboseVCTestCase
{
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
// CORRECT: SubViewController will correctly receive viewWillAppear and viewDidAppear
self.containChildViewController(ChildVC())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment