在列表中切换选择 - SwiftUI [英] Toggle selection in a list - SwiftUI
问题描述
我希望我能把我的问题解释清楚.
我想通过切换从列表中选择一些课程,但我尝试过的任何课程都不起作用.
我该怎么办?
感谢您的时间.最好的,穆拉特
struct SubjectCardsView:查看{//标记:- 属性@State var 课程:[课程] = Bundle.main.decode("courses.json")@State 私有变量切换:Bool = false//标记:- 身体var主体:一些视图{导航视图{列表 {ForEach(courses) { 课程在Section(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {ForEach(course.courseName, id: .name) { item in切换(isOn:$toggle,标签:{文本(项目名称)})}}}}.listStyle(InsetGroupedListStyle()).navigationBarTitle(选择你的主题", displayMode: .inline).font(.system(size: 16, weight: .medium, design: .rounded)).navigationBarItems(领先:按钮(动作:{}, 标签: {文本(取消")}),尾随:按钮(动作:{}, 标签: {文本(保存")}))}//导航视图}}
课程部分!
导入基础导入 SwiftUIstruct 课程:可编码、可识别 {变量 ID:整数变量名称:字符串var subjectCount: 字符串var courseName: [内容]var isToggled = false私有变量图像名称:字符串无功图像:图像{图像(图像名称)}枚举 LessonSegment:字符串、CaseIterable、可识别 {案例概述案例资源var id: String { self.rawValue }}枚举编码键:字符串,编码键{案例ID案例标题案例主题计数案例图片名称案例课程名称}}结构内容:可编码{变量 ID:整数变量名:字符串变量内容:字符串var 评估:字符串var通知:字符串}
你的 @State private var toggle: Bool = false
没有意义.你有很多课程,而不是一门课程.每门课程都应该有自己的开/关开关,这是您开始使用的:
struct Course: Codable, Identifiable {var isToggled = false///这里!...}
要使用它,您可以在 ForEach 中引用每个 course
的 isToggled
,如下所示:
ForEach(courses) { course inSection(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {ForEach(course.courseName, id: .name) { item in/// 这里!切换(isOn:course.isToggled,标签:{文本(项目名称)})}}}
然而,这行不通.course.isToggled
是 Bool
,而不是 Toggle 所期望的 Binding
.
从哪里可以获得 Binding
?来自 @State var 课程:[课程]
,当然!抱歉双关语
Binding<>
部分来自 @State
声明.
用 @State
标记的属性,例如您的 @State var 课程:[课程]
,包括
但是,您的切换需要 Binding
,而不是 Binding<[Course]>
.
这是 Bool
部分的用武之地.
您需要将绑定的值 [Course]
替换为 Bool
.好吧,我们之前有一个 Bool
,对吧?
struct Course: Codable, Identifiable {var isToggled = false///这是一个布尔值!
每门课程都有一个isToggled
,它是一个Bool
.在这个答案的前面,我们在 ForEach
中得到了这个:
ForEach(courses) { course in...///获取布尔值,不幸的是它不起作用(还)切换(isOn:course.isToggled,标签:{
... 我们需要以某种方式将 Binding<>
与 Bool
结合起来.这意味着我们必须
- reference
$courses
(获取Binding<>
) - 获取每门课程的
isToggled
还有……多田!
$courses[index].isToggled///类型为 Binding
要获得index
,我们需要遍历courses.indices
而不是直接循环courses
.
ForEach(courses.indices) { index in...///这有效!切换(isOn:$courses[index].isToggled,标签:{
然后,只需将旧代码的 ForEach
中出现的 course
替换为 courses[index]
.这是完整的工作示例:
ForEach(courses.indices) { index inSection(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 是一个 Binding切换(isOn:$courses[index].isToggled,标签:{文本(项目名称)})}}}
为方便起见,您不必每次想要当前的course
时都执行courses[index]
,您可以使用Array(zip
如 this answer 所示,循环遍历 (Int, Course)
.这还为循环内的每个 Section
分配唯一的 id
,因此您添加的任何转换都会顺利进行.
ForEach(Array(zip(courses.indices, course)), id: .1.id) { (index, course) inSection(header: Text(course.title).font(.system(size: 15, weight: .medium, design: .rounded)).foregroundColor(.blue)) {ForEach(course.courseName, id: .name) { item in切换(isOn:$courses[index].isToggled,标签:{文本(项目名称)})}}}
好吧 (Int, Course)
实际上是 (Range
但这几乎是一样的.
最终结果:
在 Content
中编辑 isToggled
,而不是 Course
:
ForEach(Array(zip(courses.indices, course)), id: .1.id) { (index, course) inSection(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/// 这里!切换(isOn:$courses[index].courseName[itemIndex].isToggled,标签:{文本(项目名称)})}}}
I hope I can explain my question clearly.
I want to select some courses from list via toggle but whatever I've tried it didn't work.
What am I supposed to do?
Thank you for your time. Bests, Murat
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
}
}
Course part!
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
}
Your @State private var toggle: Bool = false
doesn't make sense. You have many courses, not a single course. Each course should have it's own toggle on/off, which is what you started to do with:
struct Course: Codable, Identifiable {
var isToggled = false /// here!
...
}
To use this, you can reference each course
's isToggled
inside the ForEach, like this:
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)
})
}
}
}
However, this won't work. course.isToggled
is a Bool
, not a Binding<Bool>
, which the Toggle expects.
Where can you get Binding<Bool>
? From the @State var courses: [Course]
, of course! sorry for pun
The Binding<>
part comes from the @State
declaration.
Properties that are marked with @State
, like your @State var courses: [Course]
, include a projectedValue
that has the Binding<>
type.
You can access the projectedValue
by adding a $
to the property. So, if you write $courses
, that will have type Binding<[Course]>
.
But, your toggle expects Binding<Bool>
, not Binding<[Course]>
.
This is where the Bool
part comes in.
You will need to replace the Binding's value, [Course]
, with a Bool
. Well, we had a Bool
before, right?
struct Course: Codable, Identifiable {
var isToggled = false /// this is a Bool!
Each course has a isToggled
, which is a Bool
. From earlier on in this answer, we got this inside the ForEach
:
ForEach(courses) { course in
...
/// getting the Bool, which unfortunately doesn't work (yet)
Toggle(isOn: course.isToggled, label: {
... We need to somehow combine the Binding<>
with the Bool
. This means that we must
- reference
$courses
(to get theBinding<>
) - get each courses'
isToggled
And... tada!
$courses[index].isToggled /// has type Binding<Bool>
To get index
, we'll need to loop over courses.indices
instead of directly looping over courses
.
ForEach(courses.indices) { index in
...
/// this works!
Toggle(isOn: $courses[index].isToggled, label: {
Then, just replace every occurrence of course
in your old code's ForEach
with courses[index]
. Here's the full working example:
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)
})
}
}
}
As a convenience so you don't have to do courses[index]
every time you want the current course
, you can use Array(zip
as shown in this answer to loop over a (Int, Course)
. This also assigns a unique id
for every Section
inside the loop, so any transitions you add will work out smoothly.
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)
})
}
}
}
Well (Int, Course)
is actually (Range<Array<Course>.Index>.Element, Course)
but that's pretty much the same thing.
Final result:
Edit for isToggled
inside Content
, not 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)
})
}
}
}
这篇关于在列表中切换选择 - SwiftUI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!