AdaptiveElements/ExampleContainerViewController.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
ExampleContainerViewController is the core of this sample code project. |
It demonstrates: |
- How to decide which design to use depending on the view's size |
- How to apply the design to the UI |
- How to correctly add and remove child view controllers |
*/ |
import UIKit |
class ExampleContainerViewController: UIViewController { |
/** |
stackView is a UIStackView in our storyboard. |
It will contain the views for our child view controllers. |
**/ |
@IBOutlet var stackView: UIStackView! |
/** |
elementViewController holds our 3 child view controllers. |
They will either be instances of SmallElementViewController or LargeElementViewController. |
**/ |
var elementViewControllers: [UIViewController?] = [nil, nil, nil] |
/** |
displayedDesign is the design that is currently displayed in the view controller. |
It is initially nil because no design has been displayed yet. |
**/ |
var displayedDesign: Design? = nil |
override func viewWillLayoutSubviews() { |
/* |
In viewWillLayoutSubviews, we are guaranteed that our view's size, traits, etc. are up to date. |
It's a good place to update anything that affects the layout of our subviews. |
However, be careful, because this method is called frequently! |
Do as little work as possible, and don't invalidate the layout of any superviews. |
*/ |
// Step 1: Find our size. |
let size = view.bounds.size |
// Step 2: Decide what design to use, based on our rules. |
let newDesign = decideDesign(size) |
// Step 3: If the design is different than what is displayed, change the UI. |
if displayedDesign != newDesign { |
applyDesign(newDesign) |
displayedDesign = newDesign |
} |
} |
func decideDesign(_ size: CGSize) -> Design { |
/* |
Decide which Design is appropriate, given the size of our view, by applying rules. |
Note that these rules are _examples_, which produce good results in this particular sample app, |
but they are not general rules that would automatically work in other apps. |
*/ |
/* |
Decision #1: Should our elements be laid out horizontally or vertically? |
Rule: If the width is greater that the height, be horizontal, otherwise be vertical. |
*/ |
let axis: UILayoutConstraintAxis |
if size.width > size.height { |
axis = .horizontal |
} |
else { |
axis = .vertical |
} |
/* |
Decision #2: Should our elements be small or large? |
Rule: If the width is less than a threshold value, be small, otherwise be large. |
(We chose 750 as a threshold value since it produces reasonable results for this example, |
but there is nothing special about that number.) |
*/ |
let widthThreshold = CGFloat(750) |
let elementKind: Design.ElementKind |
if size.width < widthThreshold { |
elementKind = .small |
} |
else { |
elementKind = .large |
} |
// Return a Design encapsulating the results of those decisions. |
return Design(axis: axis, elementKind: elementKind) |
} |
func applyDesign(_ newDesign: Design) { |
/* |
Change the view controllers and views to display the new design. |
Be careful to only change properties that need to be changed. |
*/ |
// Set the stack view's layout axis to horizontal or vertical. |
if displayedDesign?.axis != newDesign.axis { |
stackView.axis = newDesign.axis |
} |
// Change the view controllers to the small or large kind. |
if displayedDesign?.elementKind != newDesign.elementKind { |
// Repeat these steps for each of the element view controllers: |
for (index, elementViewController) in elementViewControllers.enumerated() { |
// If an old view controller exists, then remove it from this container's child view controllers. |
if let oldElementViewController = elementViewController { |
removeOldElementViewController(oldElementViewController) |
} |
// Create the new view controller. |
let storyboard = UIStoryboard(name: "Main", bundle: nil) |
let newElementViewController = storyboard.instantiateViewController(withIdentifier: newDesign.elementIdentifier) |
// Add it as a child view controller of this container. |
addNewElementViewController(newElementViewController) |
// And remember it, so we can remove it later. |
elementViewControllers[index] = newElementViewController |
} |
} |
} |
/* |
Helper functions to be a well-behaved container view controller: |
*/ |
func addNewElementViewController(_ elementViewController: UIViewController) |
{ |
// Step 1: Add this view controller to our list of child view controllers. |
// This will call elementViewController.willMove(toParentViewController: self). |
addChildViewController(elementViewController) |
// Step 2: Add the view controller's view to our view hierarchy. |
stackView.addArrangedSubview(elementViewController.view) |
// Step 3: Tell the view controller that it has moved, and `self` is the new parent. |
elementViewController.didMove(toParentViewController: self) |
} |
func removeOldElementViewController(_ elementViewController: UIViewController) |
{ |
// Step 1: Tell the view controller that it will move to having no parent view controller. |
elementViewController.willMove(toParentViewController: nil) |
// Step 2: Remove the view controller's view from our view hierarchy. |
elementViewController.view.removeFromSuperview() |
// Step 3: Remove the view controller from our list of child view controllers. |
// This will call elementViewController.didMove(toParentViewController: nil). |
elementViewController.removeFromParentViewController() |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-13