drootang
drootang

Reputation: 2503

How to create Toggle binding from array of objects?

There are several useful questions on this topic, but all I've found either use deprecated syntax from earlier betas (e.g., BindableObject) or do not require a binding be passed (e.g., to a Toggle).

I want to create a list of Toggles that are bound to elements in an array. I've tried many methods, but the syntax is never corrrect. The version below most closely matches the answers to an existing question.

struct Item: Identifiable {
    var id: String { self.name }
    var name: String
    var enabled: Bool
}

final class ItemSet: ObservableObject {
    @Published var items: [Item]

    init() {
        items = [
            Item(name: "aaa", enabled: true),
            Item(name: "bbb", enabled: false),
            Item(name: "ccc", enabled: true)
        ]
    }
}

var myItems = ItemSet()

struct ContentView: View {
    @ObservedObject var items: ItemSet

    var body: some View {
        List {
            ForEach(items.items) { item in
                Toggle(item.name, isOn: $item.enabled)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(items: myItems)
    }
}

The compiler error I get in Xcode 11.1 is:

Use of unresolved identifier '$item'

on the line where Toggle is defined.

I was under the impression that each Item would itself need to be an ObservableObject with a @Published var enabled: Bool parameter, but I have not been able to get that working either, and all stackoverflow answers seems to say avoid making Item itself ObservableObject.

Any help would be much appreciated.

Upvotes: 1

Views: 1261

Answers (1)

Josh Homann
Josh Homann

Reputation: 16327

You are confusing the property wrapper for items (which is a current value subject) with the binding parameter that a Toggle expects. See corrected implementation with binding below:

import SwiftUI
import Combine

struct Item: Identifiable {
  var isEnabled: Binding<Bool>
  var id: String { self.name }
  var name: String
  init(name: String, enabled enabledValue: Bool) {
    self.name = name
    let enabled = CurrentValueSubject<Bool, Never>(enabledValue)
    isEnabled = Binding<Bool>(
      get: { enabled.value },
      set: { enabled.value = $0}
    )
  }
}

final class ItemSet: ObservableObject {
  @Published var items: [Item]

  init() {
    items = [
      Item(name: "aaa", enabled: true),
      Item(name: "bbb", enabled: false),
      Item(name: "ccc", enabled: true)
    ]
  }
}

var myItems = ItemSet()

struct ContentView: View {
  @ObservedObject var items: ItemSet

  var body: some View {
    List {
      ForEach(items.items) { item in
        Toggle(isOn: item.isEnabled, label: { Text (item.name) })
      }
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView(items: myItems)
  }
}

Upvotes: 5

Related Questions