C. Skjerdal
C. Skjerdal

Reputation: 3099

SwiftUI Pull Data from Nested View

I have textfields that are a couple views deep. I am trying to pull the inputed data to send to a server. Has anyone found an example of pulling data that is deep nested in views?

Here is the structure of this feature..

symbol: > NavigationLink

symbol: ~ Subview

HostingViewController ~ CustomListView > FormTypeView > BuildFormView (Has ObservedObject constructed here) ~ ViewPagerView (Horizontal List Of PageViews) ~ FourItemCardView ~ TextFieldInputView.

Alternate Structure Outline to help explain

- HostingViewController ~ CustomListView

     - FormTypeView

         - BuildFormView ~ ViewPagerView ~ FourItemCardView ~ TextFieldInputView

When you click "Add" in the ListView, you choose from a form type, and then the BuildFormView creates a multi-card swipe-able view pager. This is built inside ObservableObject.

In a perfect world all my data would bind to..

@Published var pageCells: [PageViewCell] = [PageViewCell]()

Where I could call it in ObservableObject's buildFormData1()..

 let headline: String = pageCells[0].valueOne
 let location: String = pageCells[0].valueTwo
 let isPrivate: String = pageCells[0].valueFour

I have managed view binding in swiftui, and callbacks upon a button press, but to bind a view or callback 2-3 levels doesn't seem to work.

I have tried EnvironmentalObject which sort of breaks. I can't use it in SceneDelegate since I have my HostingViewController in storyboard. I can define it where I connect the CustomListView to the HostingViewController, but that crashes ( can't remember the reason off my head ). Even if this did work to obtain the data specific would be difficult. As my view pager is dynamically changing I can't individually define every variable in my ObservedObject.

Has anyone found an example of pulling data that is deep nested? I can't avoid the nesting either because SwiftUI breaks (a misleading error) whenever you have too many views. Also let me know what code to post since there is a ton I could post which might over complicate this question.

Upvotes: 1

Views: 357

Answers (1)

Asperi
Asperi

Reputation: 257711

Here is possible approach based on view preferences. The demo replicates simple hierarchy for your last four views and shows how to transfer text entered in the last view to the very top view in hierarchy.

demo

// Models
struct TextValueKey: PreferenceKey {       // view pref for transfer
    static var defaultValue: String = ""

    static func reduce(value: inout String, nextValue: () -> String) {
        value = nextValue()
    }
}

class BuildFormViewModel: ObservableObject { // view model on top
    @Published var value: String = ""
}

// Views
struct BuildFormView: View {
    @ObservedObject var vm = BuildFormViewModel()
    var body: some View {
        VStack {
            Text("Current: \(vm.value)")
            Divider()
            FourItemCardView()
                .onPreferenceChange(TextValueKey.self) { 
                   self.vm.value = $0       // consume value
                }
        }
    }
}

struct ViewPagerView: View {
    var body: some View {
        FourItemCardView()
    }
}

struct FourItemCardView: View {
    var body: some View {
        TextFieldInputView()
    }
}

struct TextFieldInputView: View {
    @State private var text: String = ""
    var body: some View {
        TextField("Placeholder", text: $text)
            .textFieldStyle(RoundedBorderTextFieldStyle()).padding()
            .preference(key: TextValueKey.self, value: text)  // inject value
    }
}

Upvotes: 2

Related Questions