0

我正在尝试使用 ios 构建球形视图应用程序(与全景视图相同)。UITableView 中有列出的图像(paranonma)。您可以使用设备运动和手指手势以 360 度查看每个图像。但是,是否有可能在任何图像上的一个手势导致所有图像采取相同的手势效果?例如,如果我用手指循环顶部图像,则所有图像“也”都与顶部图像循环相同。“Bubbli”应用程序具有该功能。

我试图将 pangesturerecognizer 作为全局变量来共享手势,但它没有用。

我怎样才能..?

这是表格视图代码。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "pic_cell", for: indexPath) as! pic_TableViewCell
    let tmp = tmp_list[indexPath.row]
    cell.pic_View.loadPanoramaView(image: tmp)
    return cell
}

它是 tableviewcell_uiview 代码。

class TableViewCell_UIView: UIView {

var image_name:String = ""

lazy var device: MTLDevice = {
    guard let device = MTLCreateSystemDefaultDevice() else {
        fatalError("Failed to create MTLDevice")
    }
    return device
}()

weak var panoramaView: PanoramaView?

func loadPanoramaView(image: String) {
    #if arch(arm) || arch(arm64)
        let panoramaView = PanoramaView(frame: view.bounds, device: device)
    #else
        let panoramaView = PanoramaView(frame: self.bounds) // iOS Simulator
    #endif
    panoramaView.setNeedsResetRotation()
    panoramaView.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(panoramaView)

    // fill parent view
    let constraints: [NSLayoutConstraint] = [
        panoramaView.topAnchor.constraint(equalTo: self.topAnchor),
        panoramaView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
        panoramaView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
        panoramaView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
    ]
    NSLayoutConstraint.activate(constraints)

    // double tap to reset rotation
    let doubleTapGestureRecognizer = UITapGestureRecognizer(target: panoramaView, action: #selector(PanoramaView.setNeedsResetRotation(_:)))

    doubleTapGestureRecognizer.numberOfTapsRequired = 2
    panoramaView.addGestureRecognizer(doubleTapGestureRecognizer)

    self.panoramaView = panoramaView

    panoramaView.load(UIImage(named: image)!, format: .mono)
}


}

它是定义 PanoramaView 类的 PanoramaView.swift。

final class PanoramaView: UIView, SceneLoadable {
#if (arch(arm) || arch(arm64)) && os(iOS)
public let device: MTLDevice
#endif

public var scene: SCNScene? {
    get {
        return scnView.scene
    }
    set(value) {
        orientationNode.removeFromParentNode()
        value?.rootNode.addChildNode(orientationNode)
        scnView.scene = value
    }
}

public weak var sceneRendererDelegate: SCNSceneRendererDelegate?



public lazy var orientationNode: OrientationNode = {
    let node = OrientationNode()
    let mask = CategoryBitMask.all.subtracting(.rightEye)
    node.pointOfView.camera?.categoryBitMask = mask.rawValue
    return node
}()



lazy var scnView: SCNView = {
    #if (arch(arm) || arch(arm64)) && os(iOS)
        let view = SCNView(frame: self.bounds, options: [
            SCNView.Option.preferredRenderingAPI.rawValue: SCNRenderingAPI.metal.rawValue,
            SCNView.Option.preferredDevice.rawValue: self.device
            ])
    #else
        let view = SCNView(frame: self.bounds)
    #endif
    view.backgroundColor = .black
    view.isUserInteractionEnabled = false
    view.delegate = self
    view.pointOfView = self.orientationNode.pointOfView
    view.isPlaying = true
    self.addSubview(view)
    return view
}()


// to integrated panGesture


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)
}()



#if (arch(arm) || arch(arm64)) && os(iOS)
public init(frame: CGRect, device: MTLDevice) {
self.device = device
super.init(frame: frame)
addGestureRecognizer(panGestureManager.gestureRecognizer) // modify
//addGestureRecognizer(setGestureRecognizer())
}
#else
public override init(frame: CGRect) {
    super.init(frame: frame)
    addGestureRecognizer(panGestureManager.gestureRecognizer) // modify
    //addGestureRecognizer(setGestureRecognizer())
}
#endif


public required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

deinit {
    orientationNode.removeFromParentNode()
}

public override func layoutSubviews() {
    super.layoutSubviews()

    scnView.frame = bounds
}

public override func willMove(toWindow newWindow: UIWindow?) {
    if newWindow == nil {
        interfaceOrientationUpdater.stopAutomaticInterfaceOrientationUpdates()
    } else {
        interfaceOrientationUpdater.startAutomaticInterfaceOrientationUpdates()
        interfaceOrientationUpdater.updateInterfaceOrientation()
    }
}
}

extension PanoramaView: ImageLoadable {}

这是 PanoramaPanGestureManager.swift

final class PanoramaPanGestureManager {
let rotationNode: SCNNode

var allowsVerticalRotation = true
var minimumVerticalRotationAngle: Float?
var maximumVerticalRotationAngle: Float?

var allowsHorizontalRotation = true
var minimumHorizontalRotationAngle: Float?
var maximumHorizontalRotationAngle: Float?

lazy var gestureRecognizer: UIPanGestureRecognizer = {
    let recognizer = AdvancedPanGestureRecognizer()
    recognizer.addTarget(self, action: #selector(handlePanGesture(_:)))
    recognizer.earlyTouchEventHandler = { [weak self] in
        self?.stopAnimations()
        self?.resetReferenceAngles()
    }
    return recognizer
}()

private var referenceAngles: SCNVector3?

init(rotationNode: SCNNode) {
    self.rotationNode = rotationNode
}

@objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
    guard let view = sender.view else {
        return
    }

    switch sender.state {
    case .changed:
        guard let referenceAngles = referenceAngles else {
            break
        }

        var angles = SCNVector3Zero
        let viewSize = max(view.bounds.width, view.bounds.height)
        let translation = sender.translation(in: view)

        if allowsVerticalRotation {
            var angle = referenceAngles.x + Float(translation.y / viewSize) * (.pi / 2)
            if let minimum = minimumVerticalRotationAngle {
                angle = max(angle, minimum)
            }
            if let maximum = maximumVerticalRotationAngle {
                angle = min(angle, maximum)
            }
            angles.x = angle
        }

        if allowsHorizontalRotation {
            var angle = referenceAngles.y + Float(translation.x / viewSize) * (.pi / 2)
            if let minimum = minimumHorizontalRotationAngle {
                angle = max(angle, minimum)
            }
            if let maximum = maximumHorizontalRotationAngle {
                angle = min(angle, maximum)
            }
            angles.y = angle
        }

        SCNTransaction.lock()
        SCNTransaction.begin()
        SCNTransaction.disableActions = true

        rotationNode.eulerAngles = angles.normalized

        SCNTransaction.commit()
        SCNTransaction.unlock()

    case .ended:
        var angles = rotationNode.eulerAngles
        let velocity = sender.velocity(in: view)
        let viewSize = max(view.bounds.width, view.bounds.height)

        if allowsVerticalRotation {
            var angle = angles.x
            angle += Float(velocity.y / viewSize) / .pi
            if let minimum = minimumVerticalRotationAngle {
                angle = max(angle, minimum)
            }
            if let maximum = maximumVerticalRotationAngle {
                angle = min(angle, maximum)
            }
            angles.x = angle
        }

        if allowsHorizontalRotation {
            var angle = angles.y
            angle += Float(velocity.x / viewSize) / .pi
            if let minimum = minimumHorizontalRotationAngle {
                angle = max(angle, minimum)
            }
            if let maximum = maximumHorizontalRotationAngle {
                angle = min(angle, maximum)
            }
            angles.y = angle
        }

        SCNTransaction.lock()
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 1
        SCNTransaction.animationTimingFunction = CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1)

        rotationNode.eulerAngles = angles

        SCNTransaction.commit()
        SCNTransaction.unlock()

    default:
        break
    }
}

func stopAnimations() {
    SCNTransaction.lock()
    SCNTransaction.begin()
    SCNTransaction.disableActions = true

    rotationNode.eulerAngles = rotationNode.presentation.eulerAngles.normalized
    rotationNode.removeAllAnimations()

    SCNTransaction.commit()
    SCNTransaction.unlock()
}

private func resetReferenceAngles() {
    referenceAngles = rotationNode.presentation.eulerAngles
}
}

private final class AdvancedPanGestureRecognizer: UIPanGestureRecognizer {
var earlyTouchEventHandler: (() -> Void)?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesBegan(touches, with: event)

    if state == .possible {
        earlyTouchEventHandler?()
    }
}
}

private extension Float {
var normalized: Float {
    let angle: Float = self

    let π: Float = .pi
    let π2: Float = π * 2

    if angle > π {
        return angle - π2 * ceil(abs(angle) / π2)
    } else if angle < -π {
        return angle + π2 * ceil(abs(angle) / π2)
    } else {
        return angle
    }
}
}

private extension SCNVector3 {
var normalized: SCNVector3 {
    let angles: SCNVector3 = self

    return SCNVector3(
        x: angles.x.normalized,
        y: angles.y.normalized,
        z: angles.z.normalized
    )
}
}

如何集成所有单元格的手势管理器..?

4

0 回答 0