切换列表中的选择 - SwiftUI

mur*_*adi 1 foreach toggle swift swiftui

我希望我能清楚地解释我的问题。

我想通过切换从列表中选择一些课程,但无论我尝试过什么,它都不起作用。

我应该做些什么?

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

课程部分!

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
}
Run Code Online (Sandbox Code Playgroud)

ahe*_*eze 6

你的@State private var toggle: Bool = false没有道理。你有很多课程,而不是一门课程。每门课程都应该有自己的开关,这就是您开始做的:

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您可以在 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)
            })
            
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这行不通。course.isToggled是 a Bool,而不是Binding<Bool>Toggle 期望的 a 。

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


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

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

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

$courses 的 Xcode 自动补全

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

Toggle(isOn: Binding, label: { Text("一个很酷的切换") })

这就是该Bool部分的用武之地。

您需要将 Binding 的值 替换[Course]Bool。好吧,我们Bool之前有过一次,对吧?

struct Course: Codable, Identifiable {
    var isToggled = false /// this is a Bool!
Run Code Online (Sandbox Code Playgroud)

每门课程都有一个isToggled,即 a Bool。从这个答案的前面开始,我们在以下内容中得到了这个ForEach

ForEach(courses) { course in

    ...

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

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

  • 参考$courses(获取Binding<>
  • 获取每门课程'isToggled

还有……多达!

$courses[index].isToggled /// has type Binding<Bool>
Run Code Online (Sandbox Code Playgroud)

为了得到index,我们需要循环courses.indices而不是直接循环courses

ForEach(courses.indices) { index in

    ...

    ///          this works! 
    Toggle(isOn: $courses[index].isToggled, label: {
Run Code Online (Sandbox Code Playgroud)

然后,只需将course旧代码中出现的所有 替换ForEachcourses[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)
            })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为了方便起见,您不必courses[index]每次需要 current 时都执行此操作course,您可以使用此答案Array(zip中所示的方法来循环 a 。这也为循环内的每个元素分配了一个唯一的值,因此您添加的任何转换都将顺利进行。(Int, Course)idSection

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)
            })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

嗯,(Int, Course)实际上是这样(Range<Array<Course>.Index>.Element, Course),但这几乎是同一件事。

最后结果:

在 ForEach 工作的每一行内切换

编辑isToggled内部Content,而不是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)
            })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)