[英]SwiftUI: Toggle for individual items of ForEach
使用ForEach
,我想为每一行创建单独的切换。 现在, @State
绑定同时切换所有项目,我不知道如何将它们分开。
在下面的代码中,我放了一个硬编码的数组,但它确实来自一个不断变化的.json文件。 因此,我需要ForEach
和绑定是动态的。
这篇关于隐藏List
项的文章和这篇关于List
行问题的文章很有帮助,但我无法使绑定适用于我的项目。 我在第 2 天试图弄清楚这一点,我在网上找到的内容都没有解决这个具体问题。
下面是我的代码的一个小例子,它重现了我的挑战。 数组中的动态数据来自.json 文件。
import SwiftUI
struct GreekWords: Codable, Hashable {
var greekWordArray = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta"]
// The array data comes from a dynamic .json file
}
struct ContentView: View {
var greekWords: GreekWords
@State private var wordToggle = false
var body: some View {
VStack(spacing: 0) {
ForEach(greekWords.greekWordArray, id: \.self) { word in
Toggle(word, isOn: $wordToggle)
}
}
.padding(.horizontal)
}
}
我希望这是一个简单的解决方案,所以我提前感谢您的帮助。 另外,如果您能指出我更好地学习 SwiftUI 的任何方向,我将不胜感激。 我已经在 HackingWithSwift 上尝试了所有 Apple 教程和书籍以及 SwiftUI 的 100 天。
干杯!
在您的示例代码中,所有切换都引用同一个变量。 因此,当然所有切换都将始终显示相同的 state。
在您提供的链接中的示例实现中,它不仅仅是一个字符串数组,它是一个对象数组,还包含一个 bool 变量来通过切换控制该特定项目。
更新(2):
也许以下方法更符合您的预期。 抱歉,昨晚没想到。 但请记住,切换 state 的 var 仅在该视图中可用,您可以在该视图中显示状态,但不能真正使用它。 如果您想(重新)使用该信息,我宁愿选择昨晚的替代方案(见下文)。
//
// GreekWordTest.swift
// GreekWordTest
//
// Created by Sebastian on 15.08.22.
//
import SwiftUI
struct GreekWords: Codable, Hashable {
var greekWordArray = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta"]
// The array data comes from a dynamic .json file
}
struct ContentView: View {
var greekWords: GreekWords
var body: some View {
VStack(spacing: 0) {
ForEach(greekWords.greekWordArray, id: \.self) { word in
GreekWordToggleView(greekWord: word)
.padding()
}
}
.padding(.horizontal)
}
}
struct GreekWordToggleView: View {
var greekWord: String
@State private var wordToggle = false
var body: some View {
VStack(spacing: 0) {
Toggle(greekWord, isOn: $wordToggle)
}
.padding(.horizontal)
}
}
这里是截图:
选择:
昨晚的接近
//
// GreekWordTest.swift
// GreekWordTest
//
// Created by Sebastian on 14.08.22.
//
import SwiftUI
struct ContentView: View {
@StateObject var greekWordsViewModel = GreekWordsViewModel()
var body: some View {
VStack() {
GreekWordView(greekWordsViewModel: greekWordsViewModel)
}
// For this test I am fetching the data once in the beginning when ContentView apears the first time, later I also added a button to fetch it again, it'll overwrite the existing data. You can also add a logic just to update it, that is up to you and your needs.
.onAppear(){
greekWordsViewModel.fetchData()
}
}
}
struct GreekWordView: View {
@ObservedObject var greekWordsViewModel: GreekWordsViewModel
var body: some View {
VStack(){
ForEach(greekWordsViewModel.greekWordArray.indices, id: \.self){ id in
Toggle(greekWordsViewModel.greekWordArray[id].name, isOn: $greekWordsViewModel.greekWordArray[id].isOn)
.padding()
}
// Here is the extra button to (re-)fetch the data from the json.
Button(action: {
greekWordsViewModel.fetchData()
}) {
Text("Fetch Data")
}
.padding()
}
}
}
struct GreekWord: Identifiable, Hashable {
var id: String = UUID().uuidString
var name: String
var isOn: Bool
}
class GreekWordsViewModel: ObservableObject {
@Published var greekWordArray: [GreekWord] = []
func fetchData(){
// As mentioned above, in his example I empty the array on each new loading event. You can also implement a logic to just update the data.
greekWordArray = []
let greekWords: [String] = load("greekWordsData.json")
for greekWord in greekWords {
greekWordArray.append(GreekWord(name: greekWord, isOn: false))
}
}
}
为了解码 json,我使用了以下内容:
//
// ModelData.swift
// SwiftTest
//
// Created by Sebastian Fox on 14.08.22.
//
import Foundation
// This function is used to decode a file with a json. I guess you already created something that is decoding a json according to your need, of course you can still use it.
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
最后为了测试,我使用了一个非常简单的 greekWordsData.json 文件,它只包含:
["Alpha", "Beta", "Delta", "Gamma", "Epsilon", "Zeta"]
这是一个屏幕截图:
最好的,塞巴斯蒂安
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.