工作表关闭后如何重新加载 ForEach 循环项目 [英] How to reload ForEach loop items after sheet is dismissed
问题描述
我目前正在工作表视图中添加新播放器,在我关闭工作表视图后,主视图 ForEach 循环应该更新所有项目,但它没有.
I'm currently adding new player inside sheet view and after I dismiss sheet view main view ForEach loop should update all items, but it doesn't.
在 ViewModel 中,我将播放器保存到 json 文件,保存后我立即获取它.
In ViewModel I'm saving player to json file and after I have saved it I instantly fetch it.
在工作表视图中保存新用户名后,它使用新用户名打印数组,但 ForEach 循环未在视图中更新.
After I save new username in sheet view it prints array with new username, but ForEach loop is not updating in view.
如何在关闭工作表视图后重新加载视图?
How to reload view after closing sheet view?
我的主视图:
struct PlayersView: View {
@ObservedObject var viewModel: PlayersViewModel
@State var course: Course
@State var isSelected: Bool = false
@State private var selectedItem: [Bool]
@State var selectedPlayers = [String]()
init(viewModel: PlayersViewModel, course: Course) {
self.viewModel = viewModel
self._selectedItem = State(initialValue: Array(repeating: false, count: viewModel.players.count))
self.course = course
}
var body: some View {
VStack {
ForEach(0..<viewModel.players.count) { item in
PlayerCell(isSelected: selectedItem[item], player: viewModel.players[item].playerName)
.onTapGesture {
selectedItem[item].toggle()
if selectedPlayers.contains(viewModel.players[item].playerName) {
self.selectedPlayers = selectedPlayers.filter({ $0 != viewModel.players[item].playerName})
} else {
selectedPlayers.append(viewModel.players[item].playerName)
}
}
}
}
我的 viewModel 看起来像这样:
And my viewModel looks like that:
class PlayersViewModel: ObservableObject {
@Published var players = [Player]()
init() {
readJSON()
}
func saveJSON(username: String) {
var array = players
array.append(Player(playerName: username))
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("example2.json")
try JSONEncoder().encode(array)
.write(to: fileURL)
readJSON()
} catch {
print(error.localizedDescription)
}
}
func readJSON() {
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("example2.json")
let data = try Data(contentsOf: fileURL)
let players = try JSONDecoder().decode([Player].self, from: data)
self.players = players
print(players)
} catch {
print(error.localizedDescription)
}
}
}
推荐答案
查看您的控制台,您将看到以下警告:
Check out your console, you'll see following warning:
ForEachForEach(_:content:)
应该只用于常量 数据.而是使数据符合 Identifiable
或使用 ForEach(_:id:content:)
并提供明确的 id
!
ForEach<Range, Int, ModifiedContent<Text, AddGestureModifier<_EndedGesture>>> count (2) != its initial count (1).
ForEach(_:content:)
should only be used for constant data. Instead conform data toIdentifiable
or useForEach(_:id:content:)
and provide an explicitid
!
您可以将 0..<viewModel.players.count
替换为 viewModel.players.indices
来修复它
You could replace 0..<viewModel.players.count
with viewModel.players.indices
to fix it
但是如果你这样做了,你将面临崩溃,因为你在 init 中初始化了 selectedItem
,当添加新项目时它没有足够的项目
But if you do, you'll face a crash, because you're initializing selectedItem
in the init, and when new item gets added it doesn't have enough items
您可以以某种方式向该数组添加新的 false
,但是如果您想在乞讨中添加新项目,您将在更新 时遇到更多问题selectedItem
标志数组
You can add new false
to this array somehow, but if you'd like to add new item in the begging, you'll have more problems with up-to-dating your selectedItem
flags array
相反,我建议您为 Player
项目添加一个唯一标识符,并为所选玩家存储此 ID 的 Set
Instead of that I suggest you adding a unique identifier to your Player
item and storing Set
of this ids for selected player
struct PlayersView: View {
@ObservedObject var viewModel: PlayersViewModel
@State var isSelected: Bool = false
@State private var selectedIds = Set<String>()
@State var selectedPlayers = [String]()
init(viewModel: PlayersViewModel) {
self.viewModel = viewModel
}
var body: some View {
VStack {
ForEach(viewModel.players) { player in
let selected = selectedIds.contains(player.id)
Text("\(String(describing: selected)) \(player.playerName)")
.onTapGesture {
if selected {
selectedIds.remove(player.id)
} else {
selectedIds.insert(player.id)
}
selectedPlayers = viewModel.players
.filter { selectedIds.contains($0.id) }
.map { $0.playerName }
}
}
}
}
}
struct Player: Codable, Identifiable, Hashable {
let id: String
let playerName: String
}
class PlayersViewModel: ObservableObject {
@Published var players = [Player]()
init() {
readJSON()
}
func saveJSON(username: String) {
var array = players
array.append(Player(id: UUID().uuidString, playerName: username))
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("example2.json")
try JSONEncoder().encode(array)
.write(to: fileURL)
readJSON()
} catch {
print(error.localizedDescription)
}
}
func readJSON() {
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("example2.json")
let data = try Data(contentsOf: fileURL)
let players = try JSONDecoder().decode([Player].self, from: data)
self.players = players
print(players)
} catch {
print(error.localizedDescription)
}
}
}
这篇关于工作表关闭后如何重新加载 ForEach 循环项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!