Improvements to resizing/reorientation

This commit is contained in:
Charles Magahern
2019-10-13 15:40:13 -07:00
parent 1dca4b98e5
commit 6a8eec5a07
3 changed files with 102 additions and 31 deletions

View File

@@ -18,8 +18,8 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
{ {
let url = URL(string: "http://midna.xionsf.com:5000") let url = URL(string: "http://midna.xionsf.com:5000")!
_server = WemoServer(url!) _server = WemoServer(url)
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
} }
@@ -54,8 +54,6 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
let bounds = self.view.bounds let bounds = self.view.bounds
let horizontalSizeClass = self.traitCollection.horizontalSizeClass
let headerBounds = CGRect( let headerBounds = CGRect(
x: 0.0, x: 0.0,
y: 0.0, y: 0.0,
@@ -74,14 +72,14 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
let visualizationFrame = CGRect( let visualizationFrame = CGRect(
x: bodyBounds.origin.x, x: bodyBounds.origin.x,
y: bodyBounds.origin.y, y: bodyBounds.origin.y,
width: rint(0.55 * bodyBounds.size.width), width: rint(0.5 * bodyBounds.size.width),
height: bodyBounds.size.height height: bodyBounds.size.height
) )
_visualizationController.view.frame = visualizationFrame _visualizationController.view.frame = visualizationFrame
var switchesOriginX: CGFloat = 0.0 var switchesOriginX: CGFloat = 0.0
var switchesWidth: CGFloat = 0.0 var switchesWidth: CGFloat = 0.0
if (horizontalSizeClass == .regular) { if (_visualizationShouldBeVisible()) {
switchesOriginX = visualizationFrame.maxX switchesOriginX = visualizationFrame.maxX
switchesWidth = bodyBounds.size.width - visualizationFrame.size.width switchesWidth = bodyBounds.size.width - visualizationFrame.size.width
} else { } else {
@@ -102,6 +100,8 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
{ {
super.viewDidAppear(animated) super.viewDidAppear(animated)
UIApplication.shared.isIdleTimerDisabled = true
_headerView.xionLogoView.beginAnimating() _headerView.xionLogoView.beginAnimating()
if (!_server.connected) { 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) override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{ {
super.viewWillTransition(to: size, with: coordinator) super.viewWillTransition(to: size, with: coordinator)
@@ -152,14 +158,23 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
// MARK: Internal // MARK: Internal
internal func _visualizationShouldBeVisible() -> Bool
{
switch self.traitCollection.horizontalSizeClass {
case .regular:
return true
case .compact:
return false
default:
return false
}
}
internal func _updateSizeClassPresentation() internal func _updateSizeClassPresentation()
{ {
let horizontalSizeClass = self.traitCollection.horizontalSizeClass _visualizationController.view.isHidden = !_visualizationShouldBeVisible()
if (horizontalSizeClass == .regular) {
_visualizationController.view.isHidden = false
} else {
_visualizationController.view.isHidden = true
}
} }
internal func _updateVisualization(_ animated: Bool) internal func _updateVisualization(_ animated: Bool)

View File

@@ -25,6 +25,7 @@ class SwitchesViewController: UIViewController,
UICollectionViewDelegateFlowLayout UICollectionViewDelegateFlowLayout
{ {
weak var delegate: SwitchesViewControllerDelegate? weak var delegate: SwitchesViewControllerDelegate?
fileprivate var _collectionView: UICollectionView = UICollectionView(frame: CGRect.zero, fileprivate var _collectionView: UICollectionView = UICollectionView(frame: CGRect.zero,
collectionViewLayout: UICollectionViewFlowLayout()) collectionViewLayout: UICollectionViewFlowLayout())
fileprivate var _currentDevicesHash: Int = 0 fileprivate var _currentDevicesHash: Int = 0
@@ -82,6 +83,7 @@ class SwitchesViewController: UIViewController,
let bounds = self.view.bounds let bounds = self.view.bounds
_collectionView.frame = bounds _collectionView.frame = bounds
_collectionView.collectionViewLayout.invalidateLayout()
} }
// MARK: API // MARK: API
@@ -184,22 +186,28 @@ class SwitchesViewController: UIViewController,
{ {
let spacing = SwitchesViewController.collectionViewCellsSpacing let spacing = SwitchesViewController.collectionViewCellsSpacing
let bounds = collectionView.bounds let bounds = collectionView.bounds
let boundsWidth = bounds.size.width
var cellsPerRow: CGFloat = 0.0 var cellsPerRow: CGFloat = 0.0
switch (self.traitCollection.horizontalSizeClass) { switch self.traitCollection.horizontalSizeClass {
case .regular where (bounds.size.width >= 400.0), case .regular where boundsWidth >= 500.0,
.compact where (bounds.size.width >= 400.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 cellsPerRow = 3.0
break
case .compact: case .compact:
cellsPerRow = 2.0 cellsPerRow = 2.0
default: default:
cellsPerRow = 2.0 cellsPerRow = 2.0
} }
let dimensions = floor((collectionView.bounds.size.width / cellsPerRow) - ((spacing * (cellsPerRow - 1.0)) / cellsPerRow)) let dimensions = floor((collectionView.bounds.size.width / cellsPerRow) - ((spacing * (cellsPerRow - 1.0)) / cellsPerRow))
if (indexPath.item < ActionCell.count) { 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 { } else {
return CGSize(width: dimensions, height: dimensions) return CGSize(width: dimensions, height: dimensions)
} }

View File

@@ -15,20 +15,24 @@ import SceneKit
let π = CGFloat(Double.pi) let π = CGFloat(Double.pi)
class VisualizationViewController: UIViewController class VisualizationViewController: UIViewController, SCNSceneRendererDelegate
{ {
fileprivate var _scene: SCNScene = SCNScene() fileprivate let _scene: SCNScene = SCNScene()
fileprivate var _sceneView: SCNView? fileprivate let _cameraNode: SCNNode = SCNNode()
fileprivate var _cameraNode: SCNNode = SCNNode() fileprivate let _lightNode: SCNNode = SCNNode()
fileprivate var _lightNode: SCNNode = SCNNode()
fileprivate var _cubletsNode: SCNNode = SCNNode() fileprivate var _cubletsNode: SCNNode = SCNNode()
fileprivate var _sceneView: SCNView?
fileprivate var _cublets: [SCNNode] = [] fileprivate var _cublets: [SCNNode] = []
fileprivate var _percentActivated: Float = 0.0 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 cubletsDimensions = 5
static fileprivate let cubletsSize = 1.0 static fileprivate let cubletsSize = 1.0
static fileprivate let cubletsSpacing = 2.0 static fileprivate let cubletsSpacing = 2.0
static fileprivate let rotationAnimationKey = "RotationAnimation" static fileprivate let rotationAnimationKey = "RotationAnimation"
static fileprivate let cameraAdditionalDistance = Float(8.0)
// MARK: Overrides // MARK: Overrides
@@ -39,6 +43,7 @@ class VisualizationViewController: UIViewController
view.backgroundColor = UIColor.black view.backgroundColor = UIColor.black
view.scene = _scene view.scene = _scene
view.allowsCameraControl = false view.allowsCameraControl = false
view.delegate = self
_sceneView = view _sceneView = view
self.view = view self.view = view
@@ -68,6 +73,16 @@ class VisualizationViewController: UIViewController
_sceneView?.stop(nil) _sceneView?.stop(nil)
} }
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
let bounds = self.view.bounds
_rendererDataQueue.async {
self._rendererViewBounds = bounds
}
}
// MARK: API // MARK: API
var connectionStatus: ConnectionStatus = .disconnected var connectionStatus: ConnectionStatus = .disconnected
@@ -146,6 +161,13 @@ class VisualizationViewController: UIViewController
_percentActivated = percentage _percentActivated = percentage
} }
// MARK: SCNSceneRendererDelegate
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
{
_recomputeCameraDistance()
}
// MARK: Internal // MARK: Internal
internal func _setupCamera() internal func _setupCamera()
@@ -153,7 +175,6 @@ class VisualizationViewController: UIViewController
let camera = SCNCamera() let camera = SCNCamera()
_cameraNode.camera = camera _cameraNode.camera = camera
_cameraNode.position = SCNVector3Make(0.0, 0.0, 30.0)
_scene.rootNode.addChildNode(_cameraNode) _scene.rootNode.addChildNode(_cameraNode)
let centerNode = SCNNode() let centerNode = SCNNode()
@@ -169,12 +190,9 @@ class VisualizationViewController: UIViewController
let light = SCNLight() let light = SCNLight()
light.type = SCNLight.LightType.omni light.type = SCNLight.LightType.omni
light.color = UIColor.white light.color = UIColor.white
_lightNode = SCNNode()
_lightNode.light = light _lightNode.light = light
_lightNode.position = _cameraNode.position
_scene.rootNode.addChildNode(_lightNode) _cameraNode.addChildNode(_lightNode)
} }
internal func _setupModel() internal func _setupModel()
@@ -240,6 +258,36 @@ class VisualizationViewController: UIViewController
_sceneView?.technique = technique _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() internal func _beginModelResetTimer()
{ {
/* since this visualization is running all the time, trigonometric functions begin /* since this visualization is running all the time, trigonometric functions begin