All problems in computer science can be solved by another level of indirection - David Wheller

override func traitCollectionDidChange(_ previousTraits: UITraintCollection?) {
	super.traitCollectionDidChange(previousTraits)

	if previousTraits?.horizontalSizeClass != traitCollection.horinontalSizeClass {
		switch traitCollection.horizontalSizeClass {
		case .compact:
			setupConstraintsForCompactEnvironment()
		case .unspecified:
			fallthrough
		case .regular:
			setupConstraintsForRegularEnvironment()
		}
	}
}


class SimpleExampleViewController: UIViewController {
	@IBOutlet var stackView: UIStackView!

	override func viewWillLayoutSubviews() {
		let size = view.bounds.size
		let useWideDesign = size.width >= size.height
		if useWideDesign {
			stackView.axis = .horizontal
		} else {
			stacView.axis = .vertical
		}
	}
}

override func viewWillTransition(to size: CGSize,
						with coordinator: UIViewControllerTransitionCoordinator) {
	super.viewWlllTransition(to: size, with: coordinator)
	
	coordinator.animate(alongsideTransition: { _ in
		stackView.transform = CGAffineTransform(scaleX: 1.4, y: 1.4)
	}, completion: { _ in
		UIView.animate(withDuration: 0.5, animations: {
			stackView.transform = CGAffineTransform.identity
		})
	})
}

class ExampleContainerViewController: UIViewController {
	var elementViewControllers: [UIViewController?] = [nil, nil , nil]

	var displayedDesign: Design? = nil
	
	override func viewWillLayoutSubviews() {
		let size = view.bounds.size
	
		let newDesign = decideDesign(size)

		if displayedDesign != newDesign {
			applyDesign(newDesign)
			displayedDesign = newDesign	
		}
	}

	private func decideDesign(_ size: CGSize) -> Design {
		let axis: UILayoutConstraintAxis
	
		if size.width > size.height {
			axis = .horizontal
		} else {
			axis = .vertical
		}

		let elementKind: Design.ElementKind
		let widthThreshold = CGFloat(750)
		if size.width < widthThreshold {
			elementKind = .small
		} else {
			elementKind = .large
		}

		return Design(axis: axis, elementKind: elementKind)
	}

	private func applyDesign(_ newDesign: Design) {
		stackView.axis = newDesign.axis

		if displayedDesign?.elementKind != newDesign.elementKind {
			for (index, elementViewController) in elementViewControllers.enumerated() {
				if let oldElementViewController = elementViewController {
					removeOldElementViewController(oldElementViewController)
				}

				let storyboard = UIStoryboard(name: "Main", bundle: nil)
				let newElementViewController = storyboard.instantiateViewController(withIdentifiner: newDesign.elementIdentifier)
				addNewElementViewController(newElementViewController)
				elementViewControllers[index] = newElementViewController
			}
		}
	}

// 각 메소드의 순서가 중요하다.
	private func addNewElementViewController(_ elementViewController: UIViewController) {
		addChildViewController(elementViewController)
		stackView.addArrangedSubview(elementviewController.view)
		elementViewController.didMove(toParentViewController: self)
	}

	private func removeOldElementViewController(_ elementViewController: UIViewController) {
		elementViewController.willMove(toParentViewController: nil)
		elementViewController.view.removeFromSuperView()
		elementViewController.removeFromParentViewController()
	}
}