简体   繁体   中英

SwiftUI TupleView and generics, how to parse recursively tuple of some View and Group

I have Tuple with views of different types that are some View , there can also be some Group that is also generic type. Now I consider how can I parse such structure to achieve array of views.

Here are initializer and debugger screenshot

public init<A: View, B: View>(@ViewBuilder content: () -> TupleView<(A, B)>) {

    let views = content().value

    self.childs = [AnyView(views.0), AnyView(views.1)]
}

在此处输入图片说明

UPDATE 1

I've tried to implement initializer which @ViewBuilder returns TupleView of two Groups, then I can enable different combinations of Groups in View builder by implementing all permutations of initializers.

public init<A: View, B: View>(@ViewBuilder content: () -> TupleView<(Group<A>, Group<B>)>)  {

        let groups = content().value

        groups.0.content...
        groups.1.content... // here content is internal
}

But here problem is that groups.0 which is first Group< TupleView < ... > doesn't provide public access to content property (it is marked internal ) So I cannot access content of this property. I have even tried Mirror(reflecting: groups.0) but it also doesn't help as I cannot cast its children values to appropriate TupleView as I doesn't know ? generics type.

UPDATE 2

So now the only workaround to enable addition of more then 10 views to my custom container and then layouting this views as I would is to implement my own custom Grouping view instead of Group!

struct Grouping: View {

    let childs : [AnyView]

    var body: some View {
        EmptyView()
    }

    public init<A: View>(@ViewBuilder content: () -> A) {
           // this init will be used for any non-supported number of TupleView

           let view = content()
           self.childs = [AnyView(view)]
       }

    // MARK: TupleView support
    public init<A: View, B: View>(@ViewBuilder content: () -> TupleView<(A, B)>) {

           let views = content().value

           self.childs = [AnyView(views.0), AnyView(views.1)]

    }

    // ... other 10 init permutations returning TupleView of views 
}

And in my custom container view then I can implement inits that take this Grouping TupleView.

public init(@ViewBuilder content: () -> TupleView<(Grouping, Grouping)>) {

        let groups = content().value
        self.childs = groups.0.childs + groups.1.childs

    }

And it seams to do the job. I do not understand why Apple doesn't provide public access to Group content and its subviews. This way we have very limited way to make custom layouting components

You can try this:

func recursiveViewToViewList<V0: View>(viewGroup: V0) -> [AnyView] {
    [AnyView(viewGroup)]
}
func recursiveViewToViewList<V0: View, V1: View>(viewGroup: TupleView<(V0,V1)>) -> [AnyView] {
    []
        + recursiveViewToViewList(viewGroup: viewGroup.value.0)
        + recursiveViewToViewList(viewGroup: viewGroup.value.1)
}
func recursiveViewToViewList<V0: View, V1: View, V2: View>(viewGroup: TupleView<(V0,V1,V2)>) -> [AnyView] {
    []
        + recursiveViewToViewList(viewGroup: viewGroup.value.0)
        + recursiveViewToViewList(viewGroup: viewGroup.value.1)
        + recursiveViewToViewList(viewGroup: viewGroup.value.2)
}
func recursiveViewToViewList<V0: View, V1: View, V2: View, V3: View>(viewGroup: TupleView<(V0,V1,V2,V3)>) -> [AnyView] {
    []
        + recursiveViewToViewList(viewGroup: viewGroup.value.0)
        + recursiveViewToViewList(viewGroup: viewGroup.value.1)
        + recursiveViewToViewList(viewGroup: viewGroup.value.2)
        + recursiveViewToViewList(viewGroup: viewGroup.value.3)
}
func recursiveViewToViewList<Data: Collection, ID: Hashable, Content: View>(viewGroup: ForEach<Data, ID, Content>) -> [AnyView] {
    viewGroup.data.flatMap {
        recursiveViewToViewList(viewGroup: viewGroup.content($0))
    }
}
struct Grouping: View {
    let childs: [AnyView]
    public init<A: View>(@ViewBuilder content: () -> A) {
        childs = recursiveViewToViewList(viewGroup: content())
    }
}

EDIT: this doesn't work. Overload resolution is done before generic realization, so only the first function (the base case one) is resolved when you only have the : View constraint on generic parameters. The best chance may be to do dynamic dispatch there (likely using reflection since you can't typecheck for a generic with open arguments), though I don't know whether it is possible, neither how to do it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM