简体   繁体   中英

Passing filtered @Bindable objects to multiple views in SwiftUI

I'm trying to pass a filter array to multiple views, but the filtering is not working. If I remove the filter, you can pass the array to the next view, but that leads to another error during the ForEach loop. I've posted all the code below.

Does anyone know how you can pass a filter version of a @Bindable array? Also why can't I print sport.name and sport.isFavorite.description in the ForEach loop?

I'm using swiftUI on Xcode 11.0 beta 5.

import SwiftUI
import Combine

struct Sport: Identifiable{
    var id = UUID()
    var name : String
    var isFavorite = false
}

final class SportData: ObservableObject  {
    @Published var store =
        [
            Sport(name: "soccer",   isFavorite: false),
            Sport(name: "tennis",   isFavorite: false),
            Sport(name: "swimming", isFavorite: true),
            Sport(name: "running",  isFavorite: true)
    ]
}

struct Testing: View {
    @ObservedObject var sports = SportData()

    var body: some View {
        VStack {
            TestingTwo(sports: $sports.store.filter({$0.isFavorite}))
        }
    }
}

struct TestingTwo: View {
    @Binding var sports : [Sport]

    var body: some View {t
        NavigationView {
            VStack(spacing: 10){
                ForEach($sports) { sport in
                    NavigationLink(destination: TestingThree(sport: sport)){
                        HStack {
                            Text(sport.name)
                            Spacer()
                            Text(sport.isFavorite.description)
                        }
                        .padding(.horizontal)
                        .frame(width: 200, height: 50)
                        .background(Color.blue)
                    }
                }
            }
        }
    }
}

struct TestingThree: View {
    @Binding var sport : Sport

    var body: some View {
        VStack {
            Text(sport.isFavorite.description)
                .onTapGesture {
                    self.sport.isFavorite.toggle()
            }
        }
    }
}


#if DEBUG
struct Testing_Previews: PreviewProvider {
    static var previews: some View {
        Testing()
    }
}
#endif

Filtering in your case might be better placed in the navigation view, due to your binding requirements.

struct Testing: View {
    @ObservedObject var sports = SportData()

    var body: some View {
        VStack {
            TestingTwo(sports: $sports.store)
        }
    }
}

struct TestingTwo: View {
    @Binding var sports : [Sport]
    @State var onlyFavorites = false

    var body: some View {t
        NavigationView {
            VStack(spacing: 10){
                ForEach($sports) { sport in
                    if !self.onlyFavorites || sport.value.isFavorite {
                        NavigationLink(destination: TestingThree(sport: sport)){
                            HStack {
                                Text(sport.value.name)
                                Spacer()
                                Text(sport.value.isFavorite.description)
                            }
                            .padding(.horizontal)
                            .frame(width: 200, height: 50)
                            .background(Color.blue)
                        }
                    }
                }
            }
        }
    }
}

Now you can switch the isFavorite state either within the action implementation of a button, or while specifying the integration of you TestingTwo view.

struct Testing: View {
    @ObservedObject var sports = SportData()

    var body: some View {
        VStack {
            TestingTwo(sports: $sports.store, onlyFavorites: true)
        }
    }
}

Regarding the second part of your question: Note the value addendum in the ForEach loop. You're dealing with as binding here (as ForEach($sports) indicates), hence sport is not an instance of Sport .

You can't get a @Binding from a computed property, since the computed property is computed dynamically. A typical way to avoid this is to pass in ids of the sports objects and the data store itself, whereby you can access the sports items via id from the store.

If you really want to pass a @Binding in you have to remove the filter (pass in an actually backed array) and modfy the ForEach like the following:

  ForEach($sports.store) { (sport: Binding<Sport>) in

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM