How to Pass One Swiftui View as a Variable to Another View Struct

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

Pass a SwiftUI view that has its own arguments as a variable to another view struct

You need to correct the init in the MainMenuButtonView:

struct MainMenuButtonView<Content: View>: View {
var imageName: String
var title: String
var description: String
var content: () -> Content // change to closure

// add all parameters in the init
init(imageName: String, title: String, description: String, @ViewBuilder content: @escaping () -> Content) {
self.imageName = imageName // assign all the parameters, not only `content`
self.title = title
self.description = description
self.content = content
}

var body: some View {
VStack {
NavigationLink(destination: content()) { // use `content()`
Image(imageName)
.resizable()
.frame(width: 100, height: 100)
Text(title)
.font(.title)
Text(description)
.foregroundColor(Color(UIColor.systemGray2))
.multilineTextAlignment(.center)
.font(.footnote)
.frame(width: 175)
}
.buttonStyle(PlainButtonStyle())
}
}
}

Also, you need to pass a closure to the content parameter (as you indicated in the init):

struct MainMenuView: View {
var body: some View {
NavigationView {
MainMenuButtonView(
imageName: "List Icon",
title: "Lists",
description: "Auto generated shopping lists by store",
content: { TestMainView(testText: "Lists") } // pass as a closure
)
.navigationBarTitle(Text("Main Menu"))
}
}
}

How to pass and access property from one view to another in SwiftUI?

You should store expand as @State in your parent View and pass it via Binding. Simplified example:

public struct TopSheet<Content>: View where Content : View {
@Binding var expand : Bool
var content: () -> Content

//declare other properties private so they don't get used in the generated init

public var body: some View {
Text("Here")
}
}

struct NextView: View {
@State private var expand = false

var body: some View {
TopSheet(expand: $expand) {
Text("Content")
}
}
}

If you really want/need your explicit init, you can do this:

public struct TopSheet<Content>: View where Content : View {
@Binding private var expand : Bool
private var content: () -> Content

public init(expand: Binding<Bool>, @ViewBuilder content: @escaping () -> Content) {
self._expand = expand
self.content = content
}

public var body: some View {
Text("Here")
}
}

How to pass optional view to another view in SwiftUI?

Way1:

Here is a way, but you would get issue and error if your headerView type be deferent than the body Content! E.g. Circle and Text. I solved this issue in my second Way:

struct TestView<Content: View>: View {

@ViewBuilder let headerContent: (() -> Content)?
@ViewBuilder let bodyContent: () -> Content

init(headerContent: (() -> Content)? = nil, bodyContent: @escaping () -> Content) {
self.headerContent = headerContent
self.bodyContent = bodyContent
}

var body: some View {

return VStack {

headerContent?()
bodyContent()
}

}

}


Way2:

Here is the right way for you:

struct TestView<BodyContent: View, HeaderContent: View>: View {

@ViewBuilder let headerContent: () -> HeaderContent
@ViewBuilder let bodyContent: () -> BodyContent

init(headerContent: @escaping () -> HeaderContent, bodyContent: @escaping () -> BodyContent) {
self.headerContent = headerContent
self.bodyContent = bodyContent
}

init(bodyContent: @escaping () -> BodyContent) where HeaderContent == EmptyView {
self.headerContent = { EmptyView() }
self.bodyContent = bodyContent
}

var body: some View {

return VStack {

headerContent()

bodyContent()
}

}
}

Use case:

struct ContentView: View {

var body: some View {

TestView(headerContent: {
Circle().fill(Color.red).frame(width: 25, height: 25)
}, bodyContent: {
Text("Hello, World!")
})


TestView(bodyContent: {
Text("Hello, World!")
})

}

}

Swiftui How to pass a view that takes a parameter to another view that will set the parameter when calling the view

You can pass parameter as view builder argument.

Here is possible approach (tested with Xcode 12.1 / iOS 14.1)

struct ContentView2<LinkView: View>: View {
let linkView: (String)->LinkView
let testList = ["Test1", "Test2", "Test3"]
var body: some View {
HStack {
NavigationView {
List {
ForEach(testList, id: \.self) { test in
NavigationLink(destination: self.linkView(test)) {Text(test)}
}
}
}
}
}

init(@ViewBuilder linkView: @escaping (String)->LinkView) {
self.linkView = linkView
}
}

struct ContentView1: View {
var body: some View {
ContentView2() { value in
DestinationLinkView(title: value)
}
}
}

backup

Pass view to struct in SwiftUI

The error says here is that the View protocol has associatedtype Body inside it and thus you can't use the View as a type. This means we have to provide more information about the expected type of the tabView. One of the solutions would be to use AnyView as a type, but that would require additional content wrapping into AnyView.

What I would suggest doing here instead of using AnyView is to let the compiler figure out the actual tabView type for you.

  1. Let's tell the compiler that we expect some type Content that confirms to the View protocol
  2. Additionally I don't really see the necessity of using TabView as part of the TabItem declaration. Try going with just View unless you have a strong reason for not doing so.
struct TabItem<Content: View>: View {   // Content is a type that we expect. `View` is used instead of `TabView` as it is in original code
var tabView: Content // Using Content instead of a View as it is an actual type now
...
}

The rest of the code can stay unmodified.

How to pass SwiftUI View as function parameter?

You can do what the error says, and use ContentViewProtocol as a generic constraint. Make showContent generic:

func showContent<V: ContentViewProtocol>(view: V)
{
let child = UIHostingController(rootView: view)
...
}

SwiftUI Question passing a variable to another view

This is easily done with Bindings. Because mhzValue is marked with the @State property wrapper, it has an associated Binding. You can therefore declare a @Binding variable in your second view, and initialize it with the Binding to the original variable.

struct NextView : View {
@Binding var mhzValue: Float
...
}

When you specify NextView as the destination for your navigation button, pass it a Binding to mhzValue. (The dollar-sign syntax is a shorthand way to refer to the binding.)

struct ContentView : View {
@State private var mhzValue : Float = 0
...
NavigationButton(destination: NextView(mhzValue: self.$mhzValue)){...}
...
}

You can then use mhzValue inside NextView:

struct NextView : View {

@Binding var mhzValue: Float

var body: some View {
VStack{
Text("Display Slider Value Here:")
Text("\(Int(mhzValue)) Value")
.font(.title)
.fontWeight(.semibold)
.color(.blue)
}
}
}

Any changes you make to mhzValue within NextView will effectively be changes to ContentView.mhzValue.



Related Topics



Leave a reply



Submit