EasyNodeEditor

0.2.1

Developer-Friendly SwiftUI Node Editor
yukiny0811/easy-node-editor

What's New

v0.2.1

2022-11-08T10:35:23Z

Updated Readme for new binding system.

EasyNodeEditor

CI Release Swift Compatibility Platform Compatibility License

Still in alpha stage of development!

EasyNodeEditor is a library for creating node editors with SwiftUI. I am developing this library with the goal of showing as little internal complex code as possible, so that developers can concentrate as much as possible on node creation.

Demo

(v0.1.0)
GIF 2022-09-17 at 8 27 39 PM

(v0.1.4)
GIF 2022-09-28 at 3 41 15 PM

Usage1 - Standard output node

Step 1. Create your node

Create a class, and inherit it from NodeModelBase class.

class YourOutputNode: NodeModelBase {
}

Step 2. Create Outputs

Create output variables.
Put @Output for output variables.
Make sure you put @objc wrapper to all your output variables.
There are no restrictions on the naming of the variables. Name the variables whatever you want, and the library will automatically use that name to display in the node.

class YourOutputNode: NodeModelBase {
    @objc @Output var output: Int = 3
}

Step 3. Register your node

Register your node when you instanciate EasyNodeEditor View.

struct ContentView: View {
    var body: some View {
        EasyNodeEditor(nodeTypes: [YourOutputNode.self])
    }
}

This is it!
The EasyNodeEditor Library will create a node like this.

image

Usage2 - Standard input and output node

Step 1. Create your node

Create a class, and inherit it from NodeModelBase class.

class YourIONode: NodeModelBase {
}

Step 2. Create Inputs and Outputs

Create inputs and/or outputs.
Put @Input for input variables, and @Output for output variables.
Make sure you put @objc wrapper to all your input and output variables.
There are no restrictions on the naming of the variables. Name the variables whatever you want, and the library will automatically use that name to display in the node.

class YourIONode: NodeModelBase {
    @objc @Input var input: Int = 0
    @objc @Output var output: Int = 0
}

Step 3. Define what happens when input value changes

Override processOnChange() function, and define your process.
Don't change input value inside processOnChange(). It will start infinite loop.

class YourIONode: NodeModelBase {
    @objc @Input var input: Int = 0
    @objc @Output var output: Int = 0
    override func processOnChange() {
        output = input * 5
    }
}

Step 4. Register your node

Register your node when you instanciate EasyNodeEditor View.

struct ContentView: View {
    var body: some View {
        EasyNodeEditor(nodeTypes: [YourOutputNode.self, YoutIONode.self])
    }
}

Very easy!!
The EasyNodeEditor Library will create a node like this.

image

Usage 3 - Standard display node

Step 1. Create your node

Create a class, and inherit it from NodeModelBase class.

class YourDisplayNode: NodeModelBase {
}

Step 2. Create Inputs

Create inputs.
Put @Input for input variables.
Make sure you put @objc wrapper to all your input variables.
There are no restrictions on the naming of the variables. Name the variables whatever you want, and the library will automatically use that name to display in the node.

class YourDisplayNode: NodeModelBase {
    @objc @Input var input: Int = 0
}

Step3. Create View

Override middleContent() function, and define your View.

class YourDisplayNode: NodeModelBase {
    @objc @Input var input: Int = 0
    override func middleContent() -> AnyView {
        return AnyView(
            Text("number is now -> \(input)")
        )
    }
}

Step 4. Register your node

Register your node when you instanciate EasyNodeEditor View.

struct ContentView: View {
    var body: some View {
        EasyNodeEditor(nodeTypes: [YourOutputNode.self, YoutIONode.self, YourDisplayNode.self])
    }
}

Amazing!!
The EasyNodeEditor Library will create a node like this.

image

Usage 4 - Standard Interactive Node

I assume you have read Usage 1 ~ 3 here.
For interactive nodes, EasyNodeEditor provides @Middle property wrapper.
Whenever the value of the variables with @Input or @Middle changes, processOnChange() function will fire.
If you need a binding object for interaction, use binding method and pass the KeyPath to get binding object.
After finished making, register your node as usual.

class YourInteractiveNode: NodeModelBase {
    @objc @Input var input: Int = 0
    @objc @Middle var count: Int = 0
    @objc @Output var output: Int = 0
    override func processOnChange() {
        output = input * count
    }
    override func middleContent() -> AnyView {
        return AnyView(
            Group {
                Slider(value: binding(\YourInteractiveNode.count), in: 0...100)
            }
            .frame(minWidth: 200, maxWidth: 200)
            .fixedSize()
        )
    }
}

Simple!!
The EasyNodeEditor Library will create a node like this.

GIF 2022-09-17 at 8 21 30 PM

Full Sample Code

import SwiftUI
import EasyNodeEditor

struct ContentView: View {
    var body: some View {
        EasyNodeEditor(nodeTypes: [YourOutputNode.self, YourIONode.self, YourDisplayNode.self, YourInteractiveNode.self])
    }
}

class YourOutputNode: NodeModelBase {
    @objc @Output var output: Int = 3
}

class YourIONode: NodeModelBase {
    @objc @Input var input: Int = 0
    @objc @Output var output: Int = 0
    override func processOnChange() {
        output = input * 5
    }
}

class YourDisplayNode: NodeModelBase {
    @objc @Input var input: Int = 0
    override func middleContent() -> AnyView {
        return AnyView(
            Text("number is now -> \(input)")
        )
    }
}

class YourInteractiveNode: NodeModelBase {
    @objc @Input var input: Int = 0
    @objc @Middle var count: Int = 0
    @objc @Output var output: Int = 0
    override func processOnChange() {
        output = input * count
    }
    override func middleContent() -> AnyView {
        return AnyView(
            Group {
                Slider(value: binding(\YourInteractiveNode.count), in: 0...100)
            }
            .frame(minWidth: 200, maxWidth: 200)
            .fixedSize()
        )
    }
}

Description

  • Swift Tools 5.5.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Nov 27 2022 22:23:15 GMT-0500 (GMT-05:00)