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 View
s 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.
- Let's tell the compiler that we expect some type
Content
that confirms to theView
protocol - Additionally I don't really see the necessity of using
TabView
as part of theTabItem
declaration. Try going with justView
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
Accessing Swift Set Elements by Their Hash Value
In Swift, Can You Find All Types in a Module That Adhere to a Specific Protocol
Gcd Pattern for Chaining Async Operations While Piping the Results
Is a Static Boolean a Reference Type in Swift
Replacement for _Stdlib_Getdemangledtypename() in Swift 2.2
How to Call Non-Escaping Closure Inside a Local Closure
Apple Vision - Barcode Detection Doesn't Work for Barcodes with Different Colours
Nested Tabview - Remove Inner Tab Bar iOS 13, Swift Ui
Drawing Pixels on the Screen Using Coregraphics in Swift
How to Trim a String in Swift Based on a Character
Unknown Error When Adding an CSSearchableitem to Core Spotlight (Macos)
Updating Fetchedresultscontroller for Predicate Set by Uisearchbar
Best Way to Handle Errors from Async Closures in Swift 2