2

I have found almost the perfect code for my needs. But there is one problem:

I would like to use toggles in each row instead of buttons.

Original code posted by @kontiki here: SwiftUI hide list item from list item view

import SwiftUI

struct Item: Identifiable {
    let id = UUID()
    var isComplete: Bool = false
}

class Model: ObservableObject {
    @Published var isOn: Bool = false
    @Published var arr = [Item(isComplete: true), Item(isComplete: false), Item(isComplete: true), Item(isComplete: false), Item(isComplete: true), Item(isComplete: true)]
}

struct ContentView: View {
    @ObservedObject var model = Model()

    var body: some View {
        List {

            Toggle(isOn: $model.isOn) { Text("Toggle") }

            ForEach(self.model.arr.filter { model.isOn ? true : $0.isComplete }) { item in
                Row(item: item, model: self.model)
            }
        }
    }
}

struct Row: View {
    let item: Item
    @ObservedObject var model: Model

    var body: some View {
        HStack {

            Button(action: {

                if let idx = self.model.arr.firstIndex(where: { $0.id == self.item.id }) {
                    self.model.arr[idx].isComplete.toggle()
                    self.model.isOn = false
                }

            }) {

                Text("Button")

            }

            Text(item.isComplete ? "Complete" : "Not complete")

        }
    }
}

Instead of button I have tried to use toggle like this:

Toggle(isOn: $item.isComplete) {
                Text("Done")
            }

There is this if statement inside button that I don't understand. I have tried to take this let idx declaration outside of button too to make it easier for using toggle (as I don't know how to make such complicated toggle) but this also failed.

mallow
  • 2,368
  • 2
  • 22
  • 63

1 Answers1

1

Note: There is known issue with Toggle control update in List. Please be aware. The details are in Problems with layout of some rows in SwiftUI list topic.

Here is possible approach. Modified lines are marked in comments.

import SwiftUI

struct Item: Identifiable, Equatable { // <<
    let id = UUID()
    var isComplete: Bool = false
}

class Model: ObservableObject {
    @Published var isOn: Bool = false
    @Published var arr = [Item(isComplete: true), Item(isComplete: false), Item(isComplete: true), Item(isComplete: false), Item(isComplete: true), Item(isComplete: true)]
}

struct ContentView: View {
    @ObservedObject var model = Model()

    var body: some View {
        List {

            Toggle(isOn: $model.isOn) { Text("Toggle") }

            ForEach(self.model.arr.filter { model.isOn ? true : $0.isComplete }) { item in
                Row(item: self.$model.arr[self.model.arr.firstIndex(of: item)!]) // <<
            }
        }
    }
}

struct Row: View {
    @Binding var item: Item // <<

    var body: some View {
        HStack {
            Toggle(isOn: $item.isComplete) {
                Text("Done")
            }
            Text(item.isComplete ? "Complete" : "Not complete")

        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thank you, Asperi! You are e genius. It works! :) I am aware of this problem with toggles in lists. I am the person who asked this questions you linked to. I hope this will be fixed soon. – mallow Nov 24 '19 at 07:13
  • I have one small problem though. `struct LocksItemView_Previews: PreviewProvider { static var previews: some View { LocksItemView(item: Item(isComplete: false)) } }` produces an error: Cannot convert value of type 'Item' to expected argument type 'Binding' How should I change this line? `item: Item(isComplete: false)` – mallow Nov 24 '19 at 07:14
  • 1
    :) I missed that it was you. Try `LocksItemView(item: .constant(Item(isComplete: false)))` – Asperi Nov 24 '19 at 07:24