0

The general structure of my code is that I have a UIKit app in which I am trying to embed a swiftui view. So I have a file called SettingsViewController which is as follows:

class SettingsViewController: UIViewController {
    ...
    var items: [(SettingsView.Setting)] = ...
    var actionsForItems: [() -> Void = []

    @State var isOn: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()
        actionsForItems = ...
        ...
        addSettingCell(isOn: isOn)

        let childView = UIHostingController(rootView: SettingsView(settings: items, actionsForSettings: actionsForItems))
        addChild(childView)
        childView.view.frame = container.bounds
        container.addSubview(childView.view)
        childView.didMove(toParent: self)
    }

...

    func addCell(isOn: Bool) {
        items.insert((settingName, $isOn as AnyObject) as SettingsView.Setting)
        actionsForItems.insert({
            self.onSwitchValueChanged(isOn: isOn) //defined later
        })
    }
}

which creates a view called Settings View which is structured as follows:

struct SettingsView: View {
    typealias Setting = (key: String, value: AnyObject?)
    var settings: [Setting]
    var actions: [() -> Void]
    
    var body: some View {
        List(settings.indices) { index in
            ...
            SettingsWithSwitchView(setting: settings[index], action: actions[index], isOn: setting.value as Binding<Bool>)
        }
        Spacer()
    }
}

and SettingsWithSwitchView is as follows:

struct SettingsWithSwitchView: View {
    var setting: SettingsView.Setting
    var action: () -> Void
    @Binding var isOn: Bool {
        willSet {
            print("willSet: newValue =\(newValue)  oldValue =\(isOn)")
        }
        didSet {
            print("didSet: oldValue=\(oldValue) newValue=\(isOn)")
            action()
        }
    }
    
    var body: some View {
        HStack {
            Text(setting.key)
                .foregroundColor(Color("GrayText"))
                .font(Font.custom("OpenSans", size: 15))
            Spacer()
            Toggle(isOn: $isOn) {}
        }
    }
}

I read in another post here on Stack Overflow that calling didSet on the isOn property should be the way to accomplish this, but I need to call onSwitchValueChanged when the Toggle value is updated, but my current setup does not work. I would really appreciate some help to figure out how to do this. I can update with some other information if necessary.

B. Klein
  • 45
  • 2
  • 8
  • 1
    State is for SwiftUI Views not UIViewControllers – lorem ipsum Jul 30 '21 at 21:09
  • So what alternative do I need to do instead? I've tried having the state variable in the SettingsWithSwitchView and that also didn't work, as it would not update the value in the ViewController class. – B. Klein Jul 30 '21 at 21:12
  • 1
    Seems like you want `ObservableObject` (I had a similar problem [here](https://stackoverflow.com/a/66979438/14351818)) – aheze Jul 30 '21 at 21:14
  • Delegates, ObservableObjects, passing an action, etc. there are plenty of questions in SO about SwiftUI and UIViewController communication – lorem ipsum Jul 30 '21 at 21:16

1 Answers1

0

The thing that ended up working for me was creating a ViewModel which was also an ObservableObject and then setting the action for the toggle inside of .onTapGesture

B. Klein
  • 45
  • 2
  • 8