Can onebody help me with the following please: I would like to be able to set a variable in a class conforming to "ObservableObject" to different Views in order to be able to pass that variable as "content" to custom view with @ViewBuilder
Here is an example:
class TopInfoController:ObservableObject {
@Published var isDisplayed:Bool=false
@Published var title=""
//for calculated content it works - but this makes it not as flexible as I would like
//it to be
@ViewBuilder var content:some View {
VStack {
Text("BLATESTBLA")
}
}
//I would like to be able to set this from outside, not to be just calculated
//something like this:
//@ViewBuilder var content:(some View)?
//i can do var test:String?, why can't I seem to be able (some View)?
//and set it later on when needed
}
struct ContentView: View {
@StateObject var cntrl=TopInfoController()
var body: some View {
ZStack {
MainDisplay()
.environmentObject(cntrl)
TopInfo()
.environmentObject(cntrl)
.opacity(cntrl.isDisplayed ? 1:0)
}
}
}
struct TopInfo:View {
@EnvironmentObject var cntrl:TopInfoController
var body: some View {
VStack {
MyNotification(title: cntrl.title) {
//this is where I need to be able to display any content (View)
//that I would like to be able to set from whereever in the app that
//would have access to
//"TopInfoController" observableobject
cntrl.content
}
}
}
}
struct MyNotification<Content:View>:View {
let title:String
let content:Content
init(title: String, @ViewBuilder content:()->Content) {
self.title=title
self.content=content()
}
var body: some View {
VStack {
Text(title)
content
}
}
}
struct MainDisplay:View {
@EnvironmentObject var cntrl:TopInfoController
var body: some View {
VStack {
Text("TITLE")
Spacer()
Button {
cntrl.title="TEST"
//!!!!!
//here, I would like to be able to do something like
//cntrl.content=self.notificationContent
cntrl.isDisplayed.toggle()
} label: {
Text("Display")
}
}
}
@ViewBuilder var notificationContent:some View {
VStack {
Text("this is my notification")
Image(systemName: "heart.fill")
}
}
}
Is there a way to create a variable that would hold optional type (some View)? Based on what I need to display, I would set this variable.
Thanks a lot for any explanation that would help me understand how to do it or why it is not possible.
Libor
Working example using AnyView
class TopInfoController:ObservableObject {
@Published var isDisplayed:Bool=false
@Published var title:String=""
@Published var generalAlertView:AnyView?
}
struct TopInfo:View {
@EnvironmentObject var cntrl:TopInfoController
var body: some View {
VStack {
MyNotification(title: cntrl.title) {
cntrl.generalAlertView
}
}
}
}
struct MyNotification<Content:View>:View {
let title:String
let content:Content
init(title: String, @ViewBuilder content:()->Content) {
self.title=title
self.content=content()
}
var body: some View {
VStack {
Text(title)
content
}
}
}
struct ContentView: View {
@StateObject var cntrl=TopInfoController()
var body: some View {
ZStack {
// Example()
MainDisplay()
.environmentObject(cntrl)
TopInfo()
.environmentObject(cntrl)
.opacity(cntrl.isDisplayed ? 1:0)
}
}
}
struct MainDisplay:View {
@EnvironmentObject var cntrl:TopInfoController
var body: some View {
VStack {
Text("TITLE")
Spacer()
HStack {
Button {
cntrl.title="Test 1"
cntrl.generalAlertView=AnyView(notification1)
cntrl.isDisplayed=true
} label: {
Text("Display 1")
}
.tint(.green)
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
Button {
cntrl.title="Test 2"
cntrl.generalAlertView=AnyView(notification2)
cntrl.isDisplayed=true
} label: {
Text("Display 2")
}
.tint(.red)
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
}
}
}
@ViewBuilder private var notification1:some View {
VStack {
Text("this is my notification")
Image(systemName: "heart.fill")
}
}
@ViewBuilder private var notification2:some View {
HStack {
Text("Something else to display")
Image(systemName: "circle.fill")
}
}
}
'''
View
is a specific protocol, not a type; not all instances are the same: aVStack
is different than aText
. SwiftUI doesn't know how to treat the variable until you use it. This means that you can't have a variable that contains a "modifiable" view.You can instead define a view that has a different content for each instance. The variable inside that view will not be of type
View
, but will be a function that returns a type ofsome View
.The code below is an example:
Usage: