工作表关闭后如何重新加载 ForEach 循环项目 [英] How to reload ForEach loop items after sheet is dismissed

查看:40
本文介绍了工作表关闭后如何重新加载 ForEach 循环项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在工作表视图中添加新播放器,在我关闭工作表视图后,主视图 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:

ForEach>计数 (2) != 其初始计数 (1).ForEach(_: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 to Identifiable or use ForEach(_:id:content:) and provide an explicit id!

您可以将 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屋!

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