ios – Swift – adding binding to view causes unwanted behavior


Hello I have a 3d interactive globe that contains a SCNParticleSystem. When I run the code without a binding variable passed down the view hierarchy only 1 SCNParticleSystem is added to the scene. But when I add the binding and rotate the globe, a lot of SCNParticleSystem are added to the scene. Im assuming rotating the globe with a binding makes the init fire a lot which adds the SCNParticleSystem. I tried removing all the SCNParticleSystem before adding one, this didn’t work though. Id appreciate the help.

import SwiftUI
import SceneKit

typealias GenericControllerRepresentable = UIViewControllerRepresentable

@available(iOS 13.0, *)
private struct GlobeViewControllerRepresentable: GenericControllerRepresentable {
    var particles: SCNParticleSystem? = nil
    @Binding public var showProf: Bool

    func makeUIViewController(context: Context) -> GlobeViewController {
        let globeController = GlobeViewController(earthRadius: 1.0, showProf: $showProf)
        updateGlobeController(globeController)
        return globeController
    }
    
    func updateUIViewController(_ uiViewController: GlobeViewController, context: Context) {
        updateGlobeController(uiViewController)
    }
    
    private func updateGlobeController(_ globeController: GlobeViewController) {
        globeController.dotSize = CGFloat(0.005)
              
        globeController.enablesParticles = true
        
        if let particles = particles {
            globeController.particles = particles
        }
    }
}

@available(iOS 13.0, *)
public struct GlobeView: View {
    @Binding public var showProf: Bool
    
    public var body: some View {
        GlobeViewControllerRepresentable(showProf: $showProf)
    }
}

import Foundation
import SceneKit
import CoreImage
import SwiftUI
import MapKit

public typealias GenericController = UIViewController
public typealias GenericColor = UIColor
public typealias GenericImage = UIImage

public class GlobeViewController: GenericController {
    //@Binding var showProf: Bool
    public var earthNode: SCNNode!
    private var sceneView : SCNView!
    private var cameraNode: SCNNode!
    private var worldMapImage: CGImage {
        guard let image = UIImage(named: "earth-dark")?.cgImage else {
            fatalError("Not found")
        }
        return image
    }
    
    private lazy var imgData: CFData = {
        guard let imgData = worldMapImage.dataProvider?.data else { fatalError("Could not fetch data from world map image.") }
        return imgData
    }()
    
    private lazy var worldMapWidth: Int = {
        return worldMapImage.width
    }()
    
    
    public var enablesParticles: Bool = true {
        didSet {
            if enablesParticles {
                setupParticles()
            } else {
                sceneView.scene?.rootNode.removeAllParticleSystems()
            }
        }
    }
    
    public var particles: SCNParticleSystem? {
        didSet {
            if let particles = particles {
                sceneView.scene?.rootNode.removeAllParticleSystems()
                sceneView.scene?.rootNode.addParticleSystem(particles)
            }
        }
    }
    
    private var dotRadius: CGFloat {
        if dotSize > 0 {
            return dotSize
        }
        else {
            return 0.01 * CGFloat(earthRadius) / 1.0
        }
    }
    
    private var dotCount = 50000
    
    public init(earthRadius: Double, showProf: Binding<Bool>) {
        self.earthRadius = earthRadius
        self._showProf = showProf
        super.init(nibName: nil, bundle: nil)
    }
    
    public init(earthRadius: Double, dotCount: Int, showProf: Binding<Bool>) {
        self.earthRadius = earthRadius
        self.dotCount = dotCount
        self._showProf = showProf
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func viewDidLoad() {
        super.viewDidLoad()
        setupScene()
        
        setupParticles()
        
        setupCamera()
        setupGlobe()
        
        setupDotGeometry()
    }
    
    private func setupScene() {
        let scene = SCNScene()
        sceneView = SCNView(frame: view.frame)
        
        sceneView.scene = scene
        
        //let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        //sceneView.addGestureRecognizer(tapGesture)
        
        sceneView.showsStatistics = true
        sceneView.backgroundColor = .black
        sceneView.allowsCameraControl = true
        
        self.view.addSubview(sceneView)
    }
    
    private func setupParticles() {
        guard let stars = SCNParticleSystem(named: "StarsParticles.scnp", inDirectory: nil) else { return }
        stars.isLightingEnabled = false
        
        if sceneView != nil {
            sceneView.scene?.rootNode.addParticleSystem(stars)
        }
    }
    
    private func setupCamera() {
        self.cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
        
        sceneView.scene?.rootNode.addChildNode(cameraNode)
        
    }
    
    private func setupGlobe() {
        self.earthNode = EarthNode(radius: earthRadius, earthColor: earthColor, earthGlow: glowColor, earthReflection: reflectionColor)
        sceneView.scene?.rootNode.addChildNode(earthNode)
    }
}

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img