在列表中切换选择 - SwiftUI

Toggle selection in a list - SwiftUI

希望能把我的问题解释清楚

我想 select 通过切换列表中的一些课程,但无论我尝试过什么都没有用。

我该怎么办?

感谢您的宝贵时间。 最好的, 穆拉特

struct SubjectCardsView: View {
    // MARK: - Properties
    @State var courses: [Course] = Bundle.main.decode("courses.json")
    
    @State private var toggle: Bool = false
    
    // MARK: - Body
    var body: some View {
        
        NavigationView {
            
            List {
                
                ForEach(courses) { course in
                    Section(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {
                        ForEach(course.courseName, id: \.name) { item  in
                            Toggle(isOn: $toggle, label: {
                                Text(item.name)
                            })
                            
                        }
                    }
                }
            }
            
            .listStyle(InsetGroupedListStyle())
            .navigationBarTitle("Choose your subject", displayMode: .inline).font(.system(size: 16, weight: .medium, design: .rounded))
            .navigationBarItems(leading: Button(action: {
                
            }, label: {
                Text("Cancel")
            }), trailing: Button(action: {
                
            }, label: {
                Text("Save")
            }))
            
            
        } // NavigationView
    }
}

课程部分!

import Foundation
import SwiftUI

struct Course: Codable, Identifiable {
    
    var id: Int
    var title: String
    var subjectCount: String
    var courseName: [Content]
    var isToggled = false
    
    private var imageName: String
    var image: Image {
        Image(imageName)
    }

    enum LessonSegment: String, CaseIterable, Identifiable {
        case overview
        case resources

        var id: String { self.rawValue }
    }
    
    enum CodingKeys: String, CodingKey {
        case id
        case title
        case subjectCount
        case imageName
        case courseName
   
    }
}

struct Content: Codable {
    
    var id: Int
    var name: String
    var content: String
    var assessment: String
    var notify: String
}

你的 @State private var toggle: Bool = false 没有意义。你有很多课程,而不是一门课程。每门课程都应该有自己的开关 on/off,这是你开始做的:

struct Course: Codable, Identifiable {
    var isToggled = false /// here!

    ...
}

要使用它,您可以在 ForEach 中引用每个 courseisToggled,如下所示:

ForEach(courses) { course in

    Section(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {
        ForEach(course.courseName, id: \.name) { item  in

            ///          here!
            Toggle(isOn: course.isToggled, label: {
                Text(item.name)
            })
            
        }
    }
}

但是,这是行不通的。 course.isToggledBool,而不是 Toggle 期望的 Binding<Bool>

从哪里可以得到Binding<Bool>?当然是来自@State var courses: [Course]抱歉双关语


Binding<>部分来自@State声明。

标有 @State 的属性,例如您的 @State var courses: [Course],包括具有 Binding<> 类型的 projectedValue

您可以通过向 属性 添加 $ 来访问 projectedValue。所以,如果你写 $courses,那将有类型 Binding<[Course]>.

但是,您的切换需要 Binding<Bool>,而不是 Binding<[Course]>

这是 Bool 部分的用武之地。

您需要将绑定的值 [Course] 替换为 Bool。好吧,我们之前有一个Bool,对吧?

struct Course: Codable, Identifiable {
    var isToggled = false /// this is a Bool!

每个课程都有一个isToggled,也就是一个Bool。从这个答案的前面开始,我们在 ForEach:

中得到了这个
ForEach(courses) { course in

    ...

    ///          getting the Bool, which unfortunately doesn't work (yet)
    Toggle(isOn: course.isToggled, label: {

...我们需要以某种方式将 Binding<>Bool 结合起来。这意味着我们必须

  • 引用$courses(得到Binding<>
  • 获取每个课程的isToggled

还有... tada!

$courses[index].isToggled /// has type Binding<Bool>

要得到 index,我们需要遍历 courses.indices 而不是直接遍历 courses.

ForEach(courses.indices) { index in

    ...

    ///          this works! 
    Toggle(isOn: $courses[index].isToggled, label: {

然后,只需将旧代码 ForEach 中出现的每个 course 替换为 courses[index]。这是完整的工作示例:

ForEach(courses.indices) { index in
    Section(header: Text(courses[index].title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {
        ForEach(courses[index].courseName, id: \.name) { item  in

            /// $courses[index].isToggled is a Binding<Bool>
            Toggle(isOn: $courses[index].isToggled, label: {
                Text(item.name)
            })
        }
    }
}

为方便起见,您不必在每次需要当前 course 时都执行 courses[index],您可以使用 Array(zip,如 所示循环 (Int, Course)。这还会为循环内的每个 Section 分配一个唯一的 id,因此您添加的任何过渡都会顺利进行。

ForEach(Array(zip(courses.indices, courses)), id: \.1.id) { (index, course) in

    Section(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {
        ForEach(course.courseName, id: \.name) { item  in

            Toggle(isOn: $courses[index].isToggled, label: {
                Text(item.name)
            })
        }
    }
}

好吧 (Int, Course) 实际上是 (Range<Array<Course>.Index>.Element, Course) 但这几乎是一回事。

最终结果:


编辑 Content 中的 isToggled,而不是 Course

ForEach(Array(zip(courses.indices, courses)), id: \.1.id) { (index, course) in
    Section(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {
        ForEach(Array(zip(course.courseName.indices, course.courseName)), id: \.1.id) { (itemIndex, item) in

            ///          here!
            Toggle(isOn: $courses[index].courseName[itemIndex].isToggled, label: {
                Text(item.name)
            })
        }
    }
}