diff --git a/Sources/PanoramaPanGestureManager.swift b/Sources/PanoramaPanGestureManager.swift index bac2792..44d4119 100644 --- a/Sources/PanoramaPanGestureManager.swift +++ b/Sources/PanoramaPanGestureManager.swift @@ -10,18 +10,25 @@ import UIKit import UIKit.UIGestureRecognizerSubclass import SceneKit -final class PanoramaPanGestureManager { - let rotationNode: SCNNode +public final class PanoramaPanGestureManager { + public var isEnabled: Bool { + get { + return self.gestureRecognizer.isEnabled + } + set { + self.gestureRecognizer.isEnabled = newValue + } + } - var allowsVerticalRotation = true - var minimumVerticalRotationAngle: Float? - var maximumVerticalRotationAngle: Float? + public var allowsVerticalRotation = true + public var minimumVerticalRotationAngle: Float? + public var maximumVerticalRotationAngle: Float? - var allowsHorizontalRotation = true - var minimumHorizontalRotationAngle: Float? - var maximumHorizontalRotationAngle: Float? + public var allowsHorizontalRotation = true + public var minimumHorizontalRotationAngle: Float? + public var maximumHorizontalRotationAngle: Float? - lazy var gestureRecognizer: UIPanGestureRecognizer = { + internal lazy var gestureRecognizer: UIPanGestureRecognizer = { let recognizer = AdvancedPanGestureRecognizer() recognizer.addTarget(self, action: #selector(handlePanGesture(_:))) recognizer.earlyTouchEventHandler = { [weak self] in @@ -33,11 +40,13 @@ final class PanoramaPanGestureManager { private var referenceAngles: SCNVector3? - init(rotationNode: SCNNode) { + private let rotationNode: SCNNode + + internal init(rotationNode: SCNNode) { self.rotationNode = rotationNode } - @objc func handlePanGesture(_ sender: UIPanGestureRecognizer) { + @objc fileprivate func handlePanGesture(_ sender: UIPanGestureRecognizer) { guard let view = sender.view else { return } diff --git a/Sources/PanoramaView.swift b/Sources/PanoramaView.swift index b2e1ab2..a064c99 100644 --- a/Sources/PanoramaView.swift +++ b/Sources/PanoramaView.swift @@ -9,6 +9,11 @@ import UIKit import SceneKit +fileprivate struct RenderProperties { + var isDeviceMotionEnabled: Bool = true + weak var sceneRendererDelegate: SCNSceneRendererDelegate? +} + public final class PanoramaView: UIView, SceneLoadable { #if (arch(arm) || arch(arm64)) && os(iOS) public let device: MTLDevice @@ -25,7 +30,39 @@ public final class PanoramaView: UIView, SceneLoadable { } } - public weak var sceneRendererDelegate: SCNSceneRendererDelegate? + public weak var sceneRendererDelegate: SCNSceneRendererDelegate? { + get { + return self.renderProperties.sceneRendererDelegate + } + set { + self.updateRenderProperties { $0.sceneRendererDelegate = newValue } + } + } + + public var isDeviceMotionEnabled: Bool { + get { + return self.renderProperties.isDeviceMotionEnabled + } + set { + self.updateRenderProperties { $0.isDeviceMotionEnabled = newValue } + } + } + + public var isPanGestureEnabled: Bool { + get { + return self.panGestureManager.isEnabled + } + set { + self.panGestureManager.isEnabled = newValue + } + } + + public lazy var panGestureManager: PanoramaPanGestureManager = { + let manager = PanoramaPanGestureManager(rotationNode: self.orientationNode.userRotationNode) + manager.minimumVerticalRotationAngle = -60 / 180 * .pi + manager.maximumVerticalRotationAngle = 60 / 180 * .pi + return manager + }() public lazy var orientationNode: OrientationNode = { let node = OrientationNode() @@ -52,17 +89,18 @@ public final class PanoramaView: UIView, SceneLoadable { return view }() - fileprivate lazy var panGestureManager: PanoramaPanGestureManager = { - let manager = PanoramaPanGestureManager(rotationNode: self.orientationNode.userRotationNode) - manager.minimumVerticalRotationAngle = -60 / 180 * .pi - manager.maximumVerticalRotationAngle = 60 / 180 * .pi - return manager - }() - fileprivate lazy var interfaceOrientationUpdater: InterfaceOrientationUpdater = { return InterfaceOrientationUpdater(orientationNode: self.orientationNode) }() + fileprivate var renderProperties: RenderProperties { + return renderPropertiesQueue.sync { _renderProperties } + } + + private var _renderProperties = RenderProperties() + + private let renderPropertiesQueue = DispatchQueue(label: "com.eje-c.MetalScope.PanoramaView.renderPropertiesQueue") + #if (arch(arm) || arch(arm64)) && os(iOS) public init(frame: CGRect, device: MTLDevice) { self.device = device @@ -98,6 +136,12 @@ public final class PanoramaView: UIView, SceneLoadable { interfaceOrientationUpdater.updateInterfaceOrientation() } } + + private func updateRenderProperties(_ action: @escaping (_ properties: inout RenderProperties) -> Void) { + renderPropertiesQueue.async(flags: .barrier) { [unowned self] in + action(&self._renderProperties) + } + } } extension PanoramaView: ImageLoadable {} @@ -167,22 +211,24 @@ extension PanoramaView: OrientationIndicatorDataSource { extension PanoramaView: SCNSceneRendererDelegate { public func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { - var disableActions = false + if isDeviceMotionEnabled { + var disableActions = false - if let provider = orientationNode.deviceOrientationProvider, provider.shouldWaitDeviceOrientation(atTime: time) { - provider.waitDeviceOrientation(atTime: time) - disableActions = true - } + if let provider = orientationNode.deviceOrientationProvider, provider.shouldWaitDeviceOrientation(atTime: time) { + provider.waitDeviceOrientation(atTime: time) + disableActions = true + } - SCNTransaction.lock() - SCNTransaction.begin() - SCNTransaction.animationDuration = 1 / 15 - SCNTransaction.disableActions = disableActions + SCNTransaction.lock() + SCNTransaction.begin() + SCNTransaction.animationDuration = 1 / 15 + SCNTransaction.disableActions = disableActions - orientationNode.updateDeviceOrientation(atTime: time) + orientationNode.updateDeviceOrientation(atTime: time) - SCNTransaction.commit() - SCNTransaction.unlock() + SCNTransaction.commit() + SCNTransaction.unlock() + } sceneRendererDelegate?.renderer?(renderer, updateAtTime: time) }