Improvements to resizing/reorientation
This commit is contained in:
@@ -18,8 +18,8 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
|
||||
|
||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
|
||||
{
|
||||
let url = URL(string: "http://midna.xionsf.com:5000")
|
||||
_server = WemoServer(url!)
|
||||
let url = URL(string: "http://midna.xionsf.com:5000")!
|
||||
_server = WemoServer(url)
|
||||
|
||||
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
|
||||
}
|
||||
@@ -54,8 +54,6 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
let bounds = self.view.bounds
|
||||
let horizontalSizeClass = self.traitCollection.horizontalSizeClass
|
||||
|
||||
let headerBounds = CGRect(
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
@@ -74,14 +72,14 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
|
||||
let visualizationFrame = CGRect(
|
||||
x: bodyBounds.origin.x,
|
||||
y: bodyBounds.origin.y,
|
||||
width: rint(0.55 * bodyBounds.size.width),
|
||||
width: rint(0.5 * bodyBounds.size.width),
|
||||
height: bodyBounds.size.height
|
||||
)
|
||||
_visualizationController.view.frame = visualizationFrame
|
||||
|
||||
var switchesOriginX: CGFloat = 0.0
|
||||
var switchesWidth: CGFloat = 0.0
|
||||
if (horizontalSizeClass == .regular) {
|
||||
if (_visualizationShouldBeVisible()) {
|
||||
switchesOriginX = visualizationFrame.maxX
|
||||
switchesWidth = bodyBounds.size.width - visualizationFrame.size.width
|
||||
} else {
|
||||
@@ -102,6 +100,8 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
|
||||
{
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
|
||||
_headerView.xionLogoView.beginAnimating()
|
||||
|
||||
if (!_server.connected) {
|
||||
@@ -117,6 +117,12 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool)
|
||||
{
|
||||
super.viewDidDisappear(animated)
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
|
||||
{
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
@@ -152,14 +158,23 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func _visualizationShouldBeVisible() -> Bool
|
||||
{
|
||||
switch self.traitCollection.horizontalSizeClass {
|
||||
case .regular:
|
||||
return true
|
||||
|
||||
case .compact:
|
||||
return false
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
internal func _updateSizeClassPresentation()
|
||||
{
|
||||
let horizontalSizeClass = self.traitCollection.horizontalSizeClass
|
||||
if (horizontalSizeClass == .regular) {
|
||||
_visualizationController.view.isHidden = false
|
||||
} else {
|
||||
_visualizationController.view.isHidden = true
|
||||
}
|
||||
_visualizationController.view.isHidden = !_visualizationShouldBeVisible()
|
||||
}
|
||||
|
||||
internal func _updateVisualization(_ animated: Bool)
|
||||
|
||||
@@ -25,6 +25,7 @@ class SwitchesViewController: UIViewController,
|
||||
UICollectionViewDelegateFlowLayout
|
||||
{
|
||||
weak var delegate: SwitchesViewControllerDelegate?
|
||||
|
||||
fileprivate var _collectionView: UICollectionView = UICollectionView(frame: CGRect.zero,
|
||||
collectionViewLayout: UICollectionViewFlowLayout())
|
||||
fileprivate var _currentDevicesHash: Int = 0
|
||||
@@ -82,6 +83,7 @@ class SwitchesViewController: UIViewController,
|
||||
|
||||
let bounds = self.view.bounds
|
||||
_collectionView.frame = bounds
|
||||
_collectionView.collectionViewLayout.invalidateLayout()
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
@@ -184,22 +186,28 @@ class SwitchesViewController: UIViewController,
|
||||
{
|
||||
let spacing = SwitchesViewController.collectionViewCellsSpacing
|
||||
let bounds = collectionView.bounds
|
||||
let boundsWidth = bounds.size.width
|
||||
var cellsPerRow: CGFloat = 0.0
|
||||
|
||||
switch (self.traitCollection.horizontalSizeClass) {
|
||||
case .regular where (bounds.size.width >= 400.0),
|
||||
.compact where (bounds.size.width >= 400.0):
|
||||
switch self.traitCollection.horizontalSizeClass {
|
||||
case .regular where boundsWidth >= 500.0,
|
||||
.compact where boundsWidth >= 500.0:
|
||||
cellsPerRow = 4.0
|
||||
|
||||
case .regular where 400.0 ... 499.99 ~= boundsWidth,
|
||||
.compact where 400.0 ... 499.99 ~= boundsWidth:
|
||||
cellsPerRow = 3.0
|
||||
break
|
||||
|
||||
case .compact:
|
||||
cellsPerRow = 2.0
|
||||
|
||||
default:
|
||||
cellsPerRow = 2.0
|
||||
}
|
||||
|
||||
let dimensions = floor((collectionView.bounds.size.width / cellsPerRow) - ((spacing * (cellsPerRow - 1.0)) / cellsPerRow))
|
||||
if (indexPath.item < ActionCell.count) {
|
||||
return CGSize(width: collectionView.bounds.size.width, height: rint(dimensions / 2.0))
|
||||
return CGSize(width: collectionView.bounds.size.width, height: rint(dimensions / 1.5))
|
||||
} else {
|
||||
return CGSize(width: dimensions, height: dimensions)
|
||||
}
|
||||
|
||||
@@ -15,20 +15,24 @@ import SceneKit
|
||||
|
||||
let π = CGFloat(Double.pi)
|
||||
|
||||
class VisualizationViewController: UIViewController
|
||||
class VisualizationViewController: UIViewController, SCNSceneRendererDelegate
|
||||
{
|
||||
fileprivate var _scene: SCNScene = SCNScene()
|
||||
fileprivate var _sceneView: SCNView?
|
||||
fileprivate var _cameraNode: SCNNode = SCNNode()
|
||||
fileprivate var _lightNode: SCNNode = SCNNode()
|
||||
fileprivate let _scene: SCNScene = SCNScene()
|
||||
fileprivate let _cameraNode: SCNNode = SCNNode()
|
||||
fileprivate let _lightNode: SCNNode = SCNNode()
|
||||
fileprivate var _cubletsNode: SCNNode = SCNNode()
|
||||
fileprivate var _sceneView: SCNView?
|
||||
fileprivate var _cublets: [SCNNode] = []
|
||||
fileprivate var _percentActivated: Float = 0.0
|
||||
|
||||
fileprivate let _rendererDataQueue: DispatchQueue = DispatchQueue(label: "RendererQueue")
|
||||
fileprivate var _rendererViewBounds: CGRect = .zero
|
||||
|
||||
static fileprivate let cubletsDimensions = 5
|
||||
static fileprivate let cubletsSize = 1.0
|
||||
static fileprivate let cubletsSpacing = 2.0
|
||||
static fileprivate let rotationAnimationKey = "RotationAnimation"
|
||||
static fileprivate let cameraAdditionalDistance = Float(8.0)
|
||||
|
||||
// MARK: Overrides
|
||||
|
||||
@@ -39,6 +43,7 @@ class VisualizationViewController: UIViewController
|
||||
view.backgroundColor = UIColor.black
|
||||
view.scene = _scene
|
||||
view.allowsCameraControl = false
|
||||
view.delegate = self
|
||||
|
||||
_sceneView = view
|
||||
self.view = view
|
||||
@@ -68,6 +73,16 @@ class VisualizationViewController: UIViewController
|
||||
_sceneView?.stop(nil)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews()
|
||||
{
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
let bounds = self.view.bounds
|
||||
_rendererDataQueue.async {
|
||||
self._rendererViewBounds = bounds
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
|
||||
var connectionStatus: ConnectionStatus = .disconnected
|
||||
@@ -146,6 +161,13 @@ class VisualizationViewController: UIViewController
|
||||
_percentActivated = percentage
|
||||
}
|
||||
|
||||
// MARK: SCNSceneRendererDelegate
|
||||
|
||||
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
|
||||
{
|
||||
_recomputeCameraDistance()
|
||||
}
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func _setupCamera()
|
||||
@@ -153,7 +175,6 @@ class VisualizationViewController: UIViewController
|
||||
let camera = SCNCamera()
|
||||
|
||||
_cameraNode.camera = camera
|
||||
_cameraNode.position = SCNVector3Make(0.0, 0.0, 30.0)
|
||||
_scene.rootNode.addChildNode(_cameraNode)
|
||||
|
||||
let centerNode = SCNNode()
|
||||
@@ -169,12 +190,9 @@ class VisualizationViewController: UIViewController
|
||||
let light = SCNLight()
|
||||
light.type = SCNLight.LightType.omni
|
||||
light.color = UIColor.white
|
||||
|
||||
_lightNode = SCNNode()
|
||||
_lightNode.light = light
|
||||
_lightNode.position = _cameraNode.position
|
||||
|
||||
_scene.rootNode.addChildNode(_lightNode)
|
||||
_cameraNode.addChildNode(_lightNode)
|
||||
}
|
||||
|
||||
internal func _setupModel()
|
||||
@@ -240,6 +258,36 @@ class VisualizationViewController: UIViewController
|
||||
_sceneView?.technique = technique
|
||||
}
|
||||
|
||||
internal func _recomputeCameraDistance()
|
||||
{
|
||||
// compute how far the camera must be from the origin in order to fit the
|
||||
// entire scene into the current viewport size.
|
||||
|
||||
guard let renderer = _sceneView else { return }
|
||||
guard let camera = _cameraNode.camera else { return }
|
||||
|
||||
let viewBounds = _rendererDataQueue.sync { _rendererViewBounds }
|
||||
let worldMin = renderer.unprojectPoint(SCNVector3(
|
||||
viewBounds.origin.x,
|
||||
viewBounds.origin.y,
|
||||
0.0
|
||||
))
|
||||
let worldMax = renderer.unprojectPoint(SCNVector3(
|
||||
viewBounds.origin.x + viewBounds.size.width,
|
||||
viewBounds.origin.y + viewBounds.size.height,
|
||||
0.0
|
||||
))
|
||||
let farX = worldMax.x - worldMin.x
|
||||
|
||||
let fov = Float(camera.fieldOfView)
|
||||
let aabb = _cubletsNode.boundingBox
|
||||
let minO = (aabb.max.x - aabb.min.x) / farX
|
||||
let halfMinO = minO / 2.0
|
||||
let cameraDistance = (halfMinO / tan(fov)) + Self.cameraAdditionalDistance
|
||||
|
||||
_cameraNode.position = SCNVector3Make(0.0, 0.0, cameraDistance)
|
||||
}
|
||||
|
||||
internal func _beginModelResetTimer()
|
||||
{
|
||||
/* since this visualization is running all the time, trigonometric functions begin
|
||||
|
||||
Reference in New Issue
Block a user