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




