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

查看:64
本文介绍了切换列表中的选择-SwiftUI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我想通过切换从列表中选择一些课程,但是我尝试过的所有方法都行不通.

我该怎么办?

谢谢您的时间.最好的穆拉特

  struct SubjectCardsView:查看{//MARK:-属性@州var课程:[Course] = Bundle.main.decode(" courses.json")@State私人var切换:Bool = false//标记:-正文var body:some View {NavigationView {列表 {ForEach(courses){当然,区段(标题:文本(course.title).font(.system(大小:15,权重:.medium,设计:.rounded)).foregroundColor(.blue)){ForEach(course.courseName,id:\.name){Toggle(isOn:$ toggle,label:{文字(item.name)})}}}}.listStyle(InsetGroupedListStyle()).navigationBarTitle(选择主题",displayMode:.inline).font(.system(大小:16,权重:.medium,设计:.rounded)).navigationBarItems(前导:按钮(操作:{}, 标签: {文字(取消")}),结尾:Button(action:{}, 标签: {文字(保存")}))}//NavigationView}} 

课程部分!

  import Foundation导入SwiftUI结构课程:可编码,可识别{var id:Intvar标题:字符串var subjectCount:字符串var courseName:[内容]var isToggled = false私人var imageName:字符串var image:图片{图片(imageName)}枚举LessonSegment:字符串,CaseIterable,可识别{案例概述案例资源var id:字符串{self.rawValue}}枚举CodingKeys:字符串,CodingKey {案例ID案例标题案例subjectCount案例imageName案例课程名称}}结构内容:可编码{var id:Int变量名称:字符串var内容:字符串var评估:字串var notify:字符串} 

解决方案

您的 @State私有var切换:Bool = false 没有任何意义.您有很多课程,而不是一门课程.每门课程都应该有自己的开/关,这就是您开始使用的方法:

  struct课程:可编码,可识别{var isToggled = false///在这里!...} 

要使用此功能,您可以在ForEach中引用每个课程 isToggled ,如下所示:

  ForEach(courses){当然是区段(标题:文本(course.title).font(.system(大小:15,权重:.medium,设计:.rounded)).foregroundColor(.blue)){ForEach(course.courseName,id:\.name){///         这里!切换(isOn:course.isToggled,标签:{文字(item.name)})}}} 

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

在哪里可以获得 Binding< Bool> ?来自 @State var课程:当然,[课程] 对不起,双关语


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

标有 @State 的属性,例如您的 @State var课程:[课程] ,包括一个

但是,您的切换开关期望使用 Binding< Bool> ,而不是 Binding< [Course]> .

这是 Bool 部分出现的地方.

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

  struct课程:可编码,可识别{var isToggled = false///这是一个布尔! 

每门课程都有一个 isToggled ,它是一个 Bool .从此答案的较早版本开始,我们就在 ForEach :

  ForEach(courses){当然是...///正在获取Bool,但不幸的是,该Bool无法正常工作(尚未)切换(isOn:course.isToggled,标签:{ 

...我们需要以某种方式将 Binding<> Bool 组合在一起.这意味着我们必须

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

还有...多田!

  $ courses [index] .isToggled///具有Binding< Bool>类型; 

要获取 index ,我们需要遍历 courses.indices ,而不是直接循环遍历 courses .

  ForEach(courses.indices){...///这行得通!切换(isOn:$ courses [index] .isToggled,标签:{ 

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

  ForEach(courses.indices){节(标题:文本(courses [index] .title).font(.system(大小:15,权重:.medium,设计:.rounded)).foregroundColor(.blue)){ForEach(courses [index] .courseName,id:\.name){///$ courses [index] .isToggled是Binding< Bool>切换(isOn:$ courses [index] .isToggled,标签:{文字(item.name)})}}} 

为方便起见,您不必每次都需要当前的 course 时就要做 courses [index] ,您可以使用 Array(zip ,如此答案所示,以遍历(Int,Course).还为循环内的每个 Section 分配了唯一的 id ,因此您添加的任何过渡都可以顺利实现.

  ForEach(Array(zip(zip(courses.indices,courses)),id:\ .1.id){(索引,课程)在区段(标题:文本(course.title).font(.system(大小:15,权重:.medium,设计:.rounded)).foregroundColor(.blue)){ForEach(course.courseName,id:\.name){切换(isOn:$ courses [index] .isToggled,标签:{文字(item.name)})}}} 

(内部课程)实际上是(Range< Array< Course> .Index> .Element,课程),但这几乎是同一回事

最终结果:


Content 中而不是 Course 内编辑 isToggled :

  ForEach(Array(zip(zip(courses.indices,courses)),id:\ .1.id){(索引,课程)在区段(标题:文本(course.title).font(.system(大小:15,权重:.medium,设计:.rounded)).foregroundColor(.blue)){ForEach(Array(zip(zip(course.courseName.indices,course.courseName)),id:\ .1.id){(itemIndex,item)在///         这里!切换(isOn:$ courses [index] .courseName [itemIndex] .isToggled,标签:{文字(item.name)})}}} 

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天全站免登陆