Passing Data from Simple Nsview to Swiftui View

Passing data from simple NSView to SwiftUI View

Here is a solution (with some replicated parts). Tested with Xcode 11.4 / macOS 10.15.4

class KeyboardInput: ObservableObject {
@Published var keyCode: UInt16 = 0
}

struct KeyboardEvent: NSViewRepresentable {
@Binding var keyStorage: UInt16 // << here !!
init(into storage: Binding<UInt16>) {
_keyStorage = storage
}

class KeyView: NSView {
var owner: KeyboardEvent? // << view holder

override var acceptsFirstResponder: Bool { true }
override func keyDown(with event: NSEvent) {
print("\(event.keyCode)")
owner?.keyStorage = event.keyCode
}
}

func makeNSView(context: Context) -> NSView {
let view = KeyView()
view.owner = self // << inject
DispatchQueue.main.async {
view.window?.makeFirstResponder(view)
}
return view
}

func updateNSView(_ nsView: NSView, context: Context) {
}

}

struct ContentView: View {
@EnvironmentObject var input: KeyboardInput // save the keyCode here
var body: some View {
VStack {
Text("Code: \(input.keyCode)")
KeyboardEvent(into: $input.keyCode) // << binding !!!
}
}
}

Passing an EnvronmentObject to NSHostingControllers

Yes, in such case EnvironmentObject is not injected automatically. The solution would be to separate content into designated views (for better design) and inject environment object manually.

Here it is

Text(self.env.value)
GeometryReader { geometry in
SplitView(master: {
MasterView().environmentObject(self.env)
}, detail: {
HStack {
DetailView().environmentObject(self.env)
}).frame(width: geometry.size.width, height: geometry.size.height)
}

and views

struct MasterView: View {
var body: some View {
Text("master")
.background(Color.yellow)
}
}

struct DetailView: View {
var body: some View {
HStack {
Text(self.env.value) }
.background(Color.orange)
}
}

How to pass one SwiftUI View as a variable to another View struct

To sum up everything I read here and the solution which worked for me:

struct ContainerView<Content: View>: View {
@ViewBuilder var content: Content

var body: some View {
content
}
}

This not only allows you to put simple Views inside, but also, thanks to @ViewBuilder, use if-else and switch-case blocks:

struct SimpleView: View {
var body: some View {
ContainerView {
Text("SimpleView Text")
}
}
}

struct IfElseView: View {
var flag = true

var body: some View {
ContainerView {
if flag {
Text("True text")
} else {
Text("False text")
}
}
}
}

struct SwitchCaseView: View {
var condition = 1

var body: some View {
ContainerView {
switch condition {
case 1:
Text("One")
case 2:
Text("Two")
default:
Text("Default")
}
}
}
}

Bonus:
If you want a greedy container, which will claim all the possible space (in contrary to the container above which claims only the space needed for its subviews) here it is:

struct GreedyContainerView<Content: View>: View {
@ViewBuilder let content: Content

var body: some View {
content
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}

If you need an initializer in your view then you can use @ViewBuilder for the parameter too. Even for multiple parameters if you will:

init(@ViewBuilder content: () -> Content) {…}

Swifui: MacOS App: Fetching Core Data in ContentView OK, but how to use the result from AppDelegate?

You need to inject Order to the environment if it will be used as an @EnvironmentObject:

let order = Order() // declare it once
let contentView = ContentView().environmentObject(order) // inject to the environment

Problem With Bindings with an NSViewRepresentable hosted NSScrollView

It is State - it is updated when is used in body, so instead use like below:

demo

struct ContentView: View{
@State var offset: CGFloat = 10.0

var body: some View {
VStack {
Text("Offset: \(offset)") // << here !!
MyScrollView(offset: $offset){
ZStack{
Rectangle().foregroundColor(.clear).frame(width: 1200, height: 1000)
Rectangle().foregroundColor(.blue).frame(width: 100, height: 100)
}
}
}
}
}


Related Topics



Leave a reply



Submit