Recepe (How To): Metal-backed control (SceneKit/SpriteKit) for iOS/MacOS

I’ve struggled with this for about a week: make a component that should display a SceneKit view, and would work in both iOS and MacOS builds, and could be used with other components. I have now achieved that and wanted to share the “wisdom”.

Add the following to “Extra System Frameworks” in iOS/MacOS exporters (choose the ones you want): Metal, SceneKit, MetalKit

If you want to display the assets from the default SceneKit “Game” project, copy “art.sncassets” folder into your “Source” folder, and then add “Source/art.scnassets” to “Custom Xcode Resource Folders” in iOS/MacOS exporters (for some reason, it doesn’t work if you don’t place the assets folder under “Source”).

Create new .h/.cpp file pair in Juicer, and rename .cpp file into .mm file. Let’s say you named these SceneKitControl.h and SceneKitControl.mm.

SceneKitControl.h:

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

#if JUCE_IOS
#define SceneKitBase UIViewComponent
#endif

#if JUCE_MAC
#define SceneKitBase NSViewComponent
#endif

class SceneKitControl : public SceneKitBase
{
public:
    
    SceneKitControl ();
    
    void
    resized () override;
    
private:
    
    void*
    m_view = nullptr;
};

SceneKitControl.mm:

#include <SceneKit/SCNView.h>
#include <SceneKit/SCNScene.h>

#include "SceneKitControl.h"

SceneKitControl::SceneKitControl ()
{
    // nothing for now
}

void
SceneKitControl::resized ()
{
    if (!m_view)
    {
        const auto b = getLocalBounds ();
        if (!b.isEmpty())
        {
            CGRect rect;
            rect.origin.x = b.getX();
            rect.origin.y = b.getY();
            rect.size.height = b.getHeight();
            rect.size.width = b.getWidth();

            SCNView* view = [[SCNView alloc] initWithFrame:rect];
            view.allowsCameraControl = true;
            view.showsStatistics = true;

            NSString *name = @"art.scnassets/ship.scn";
            SCNScene* scene = [SCNScene sceneNamed:name];
            view.scene = scene;
            
            m_view = view;
            setView (view);
        }
    }
}

How you can use this component anywhere in your iOS/MacOS app.

You can see the result here (Instagram video).

3 Likes

Any solutions to adding an Objective-C / Swift bridging header in Juicer?

1 Like

Yes! Create your bridging header named e.g. YourApp-Bridging-Header.h.

Then add these flags to “Custom XCode flags” for the Debug scheme in the XCode exporter of Projucer project (I keep my source code files in a folder called Source in the project root of the JUCE project).

SWIFT_OBJC_BRIDGING_HEADER=../../Source/YourApp-Bridging-Header.h, SWIFT_VERSION=5.0, SWIFT_OPTIMIZATION_LEVEL="-Onone"

and in Release you omit the “SWIFT_OPTIMIZATION_LEVEL” flag.

EDIT: LOL, this answer was apparently your very own fix @akuz8. Source: Request - Xcode and Swift files - #10 by akuz8

1 Like

Thanks for this @akuz8

In case someone is specifically interested in the SpriteKit version of this, I made a starter project with the default SpriteKit game scene:

1 Like