View as a Parameter / Variable in SwiftUI

Hi, all!


Still trying to wrap my brain around SwiftUI and how different it is from UIKit stuff.


I've created a View called DraggableView which runs a closure when it's being dragged. I made it so that this view could accept as a variable in its constructor any other generic view, and it would essentially put that view into a draggable container. I had to construct it like so:


DraggableView: View {
     var containedView: Content
...
}

And it seems to work. The DraggableView gets a container view and displays it properly.


In the drag closure, I want the DraggableView to be able to pass back the view it was constructed with (A duplicate view of its insides).

I don't know if it's because of Opaque return types or something, but I'm really confused at passing views around in general. I can't just have it expect a UIView like I used to be able to and that kind of *****.


My drag closure looks like so right now and I'm getting all kinds of errors. I don't even really know what AnyView is but can't find any good documentation on it. There also appears to be a difference depending on where it's used? As in SwiftUI.AnyView vs AnyView?


var dragClosure: (DragGesture.Value, CGFloat, CGFloat, AnyView) -> ()


What should I do? I'm super confused.


Thanks so much.

Answered by krebera in 381259022

For anyone who finds this in the future:


struct Whatever {
     var view: AnyView
}

let myWhatever = Whatever(view: AnyView(CustomView))

You just have to wrap it in AnyView. Took me three days to figure it out but it works a treat.


Cheers!

Doc on AnyView (not very good):

https://developer.apple.com/documentation/swiftui/anyview


Where did you find that you have to pass AnyView for the closure ?


I found this SO interesting:

https://stackoverflow.com/questions/56833659/what-is-content-in-swiftui


Could you show the errors you get ?

That's really interesting. Although I still do not understand what a good course of action is in this circumstance.


Say I want to write a function that takes a view and say an int as an argument and returns a vuew.

Would it be:

func fooBar(Content, Int) -> Content {
}

?


Or am I essentially attempting to write a view modifier?

My goal here is to have any encapsulated UI view give the user a duplicate to drag around after a long press. I have most of the functionality down, but giving the content view back the view the DraggableView was provided in the first place is just tricky.


The errors vary depending on what I'm typing, but it sounds like AnyView just isn't the solution here. Some reading says that AnyView is like a type-erased wrapper for a view, which is kind of what I want?

Did you try to implement in plain UIKit ? probably more flexible.


Unless you hve an express need to do it in SwiftUI ?

I have not yet, although I'm highly confident I could do so.

I guess I hadn't considered that. I was trying to make the whole thing in SwiftUI from the ground up but I guess that's not technically necessary.

Accepted Answer

For anyone who finds this in the future:


struct Whatever {
     var view: AnyView
}

let myWhatever = Whatever(view: AnyView(CustomView))

You just have to wrap it in AnyView. Took me three days to figure it out but it works a treat.


Cheers!

27

Thanks for the feedback.


So, how do you write your closure ?

Take a look at my solution: https://stackoverflow.com/a/63937440/6898849

Code Block
struct ContainerView<Content: View>: View {
let content: Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content()
}
var body: some View {
// Do something with `content`
}
}

You don't need AnyView (which you anyways should use as little as possible as it prevents all kinds of optimizations).
My solution also allows you to use if-else and switch-case blocks inside (thanks to @ViewBuilder):

Code Block
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")
}
}
}
}

I used krebera's example to solve the question I had. Wrap the view in Anyview by replacing CustomView with the view you want to use. So...
Code Block
struct Whatever {
var view: AnyView
}
let myWhatever = Whatever(view: AnyView(Text("Bruh, krebera is rad, thank you sooooo much!")))



View as a Parameter / Variable in SwiftUI
 
 
Q