Last active
November 13, 2015 18:18
-
-
Save cellularmitosis/56d734ab087a3f283455 to your computer and use it in GitHub Desktop.
Workaround for UIViewController containment bug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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