sheet 关闭后如何重新加载 ForEach 循环项

How to reload ForEach loop items after sheet is dismissed

我目前正在 sheet 视图中添加新播放器,在我关闭 sheet 视图后,主视图 ForEach 循环应该更新所有项目,但它没有。

在 ViewModel 中,我将播放器保存到 json 文件,保存后我会立即获取它。

在 sheet 视图中保存新用户名后,它会打印包含新用户名的数组,但 ForEach 循环不会在视图中更新。

如何在关闭 sheet 视图后重新加载视图?

我的主要观点:

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({ [=11=] != viewModel.players[item].playerName})
          } else {
            selectedPlayers.append(viewModel.players[item].playerName)
          }
        }
    }
  }

我的 viewModel 看起来像这样:

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)
  }
 }
 }

检查您的控制台,您会看到以下警告:

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 to Identifiable or use ForEach(_:id:content:) and provide an explicit id!

您可以将 0..<viewModel.players.count 替换为 viewModel.players.indices 来修复它

但如果你这样做,你将面临崩溃,因为你正在初始化 selectedItem,当添加新项目时它没有足够的项目

您可以通过某种方式向此数组添加新的 false,但是如果您想在开始时添加新项目,那么更新您的 [=17= 会遇到更多问题]标志数组

相反,我建议您为 Player 物品添加一个唯一标识符,并为所选玩家存储 Set 个此 ID

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([=10=].id) }
                            .map { [=10=].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)
        }
    }
}