From 6a8eec5a07d855bd4d99e8f3835e591d4cbffd57 Mon Sep 17 00:00:00 2001 From: Charles Magahern Date: Sun, 13 Oct 2019 15:40:13 -0700 Subject: [PATCH] Improvements to resizing/reorientation --- .../Controllers/MainViewController.swift | 39 +++++++--- .../Controllers/SwitchesViewController.swift | 20 +++-- .../VisualizationViewController.swift | 74 +++++++++++++++---- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/XIONControlPanel/Controllers/MainViewController.swift b/XIONControlPanel/Controllers/MainViewController.swift index 02b37e3..54a81e7 100644 --- a/XIONControlPanel/Controllers/MainViewController.swift +++ b/XIONControlPanel/Controllers/MainViewController.swift @@ -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) diff --git a/XIONControlPanel/Controllers/SwitchesViewController.swift b/XIONControlPanel/Controllers/SwitchesViewController.swift index 954b7df..716a03b 100644 --- a/XIONControlPanel/Controllers/SwitchesViewController.swift +++ b/XIONControlPanel/Controllers/SwitchesViewController.swift @@ -25,8 +25,9 @@ class SwitchesViewController: UIViewController, UICollectionViewDelegateFlowLayout { weak var delegate: SwitchesViewControllerDelegate? + fileprivate var _collectionView: UICollectionView = UICollectionView(frame: CGRect.zero, - collectionViewLayout: UICollectionViewFlowLayout()) + collectionViewLayout: UICollectionViewFlowLayout()) fileprivate var _currentDevicesHash: Int = 0 static fileprivate let collectionViewDeviceSwitchCellReuseIdentifier = "DeviceSwitchReuseID" @@ -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) } diff --git a/XIONControlPanel/Controllers/VisualizationViewController.swift b/XIONControlPanel/Controllers/VisualizationViewController.swift index d478c56..fb1e021 100644 --- a/XIONControlPanel/Controllers/VisualizationViewController.swift +++ b/XIONControlPanel/Controllers/VisualizationViewController.swift @@ -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 var _cubletsNode: SCNNode = SCNNode() - fileprivate var _cublets: [SCNNode] = [] - fileprivate var _percentActivated: Float = 0.0 + 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