Russ
Russ

Reputation: 566

How to execute a function in a SwiftUI sub-view?

This is more of a general understanding question regarding SwiftUI.

What I would like to be able to do is to execute a function associated with another view. There are a number of cases/reasons for doing this. My most recent endeavor is to have a view that collects a number of fields, like an address, then to use that view within other views.

My question is, how do I get the data from the address view? In the example shown, how might I call the getXML() function? Is there a better way to accomplish this?

import SwiftUI

struct AddressView: View
{
    @State var street1 = ""
    @State var street2 = ""
    @State var city = ""
    @State var state = ""
    @State var zip = ""

    var body: some View
    {
        VStack
        {
            TextField("Street1", text: $street1)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Street2", text: $street2)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("City", text: $city)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("State", text: $state)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Zip", text: $zip)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            
        }.padding()
    }
    
    func getXML() -> String
    {
        var xml = XMLStuff("Address")
        xml.push(tag: "street1", value: street1)
        xml.push(tag: "street2", value: street2)
        xml.push(tag: "city", value: city)
        xml.push(tag: "state", value: state)
        xml.push(tag: "zip", value: zip)
        return xml.string()
    }
}

The following is some code that would be used to include several such views but I can't figure out how to extract the data in order to save/use it.

import SwiftUI

struct ContactView: View
{
    @State var name: String = ""
    
    var body: some View
    {
        VStack
        {
            TextField("Name", text: $name)
            AddressView()
            PhoneView()
        }
    }
}

I know I could create a bunch of state variables in the caller and then pass them to the sub-view, but I'm trying to avoid that if possible.

Upvotes: 2

Views: 2153

Answers (1)

pawello2222
pawello2222

Reputation: 54426

You can create an ObservableObject class to store your properties:

class ContactViewModel: ObservableObject {
    @Published var street1 = ""
    @Published var street2 = ""
    @Published var city = ""
    @Published var state = ""
    @Published var zip = ""

    func getXML() -> String {
        var xml = XMLStuff("Address")
        xml.push(tag: "street1", value: street1)
        xml.push(tag: "street2", value: street2)
        xml.push(tag: "city", value: city)
        xml.push(tag: "state", value: state)
        xml.push(tag: "zip", value: zip)
        return xml.string()
    }
}

Then, use this class in the ContentView:

struct ContactView: View {
    @StateObject private var viewModel = ContactViewModel()
    @State private var name: String = ""

    var body: some View {
        VStack {
            TextField("Name", text: $name)
            AddressView(viewModel: viewModel)
            ...
        }
    }
}

and pass it to the AddressView:

struct AddressView: View {
    @ObservedObject var viewModel: ContactViewModel

    var body: some View {
        VStack {
            TextField("Street1", text: $viewModel.street1)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Street2", text: $viewModel.street2)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("City", text: $viewModel.city)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("State", text: $viewModel.state)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Zip", text: $viewModel.zip)
                .textFieldStyle(RoundedBorderTextFieldStyle())

        }.padding()
    }
}

You can also call the getXML() function from either ContentView or AddressView:

viewModel.getXML()

Upvotes: 2

Related Questions