在列表中切换选择 - SwiftUI [英] Toggle selection in a list - SwiftUI

查看:29
本文介绍了在列表中切换选择 - 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 中引用每个 courseisToggled,如下所示:

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.isToggledBool,而不是 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(zipthis 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.Index>.Element, Course) 但这几乎是一样的.

最终结果:


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 the Binding<>)
  • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆